From: kwatch Date: 2006-03-12T17:13:47+09:00 Subject: Re: [RCR] abstract method in Ruby Jacob Fugal wrote: > > I'm of a similar opinion with others that predeclaring an abstract > method that raises NotImplementedError is of limited use when compared > with the existing NoMethodError duck-typing approach. As such, I don't > think it belongs in the core. Hmm... IMO, abstract method is more useful than 'singleton.rb' or 'delegate.rb'. It's just my opinion. > *However*, this is a useful concept which could be used to extend the > capabilities of the language for those who do still want it. I > encourage you to build a library from this idea and publicize it. Just > because it doesn't belong in the core doesn't mean it won't be useful > to some. Yes, I'm going to release 'abstract.rb'. > One place I can see this being used is if the > NotImplementedError gave a more descriptive output, such as "#{self} > requires that the host implement the method '#{method_name}'." E.g. > "Enumerable requires that the host implement the method 'each'." ....(snip)... > One thing that you can do to make this approach (which seems cleaner > and simpler than the backtrace manipulation approach you later > proposed) more flexible by removing the arity restriction on the > method using the splat operator: Thaks for your good advice. I changed Module#abstract_method() like the following: ---------- class Module def abstract_method args_str, *method_names method_names.each do |name| mesg = "class %s should implement abstract method `#{self.name}##{name}()'." module_eval <<-END def #{name}(#{args_str}) err = NotImplementedError.new(mesg % self.class.name) err.set_backtrace caller() raise err end END end end end ---------- Manipulating backtrace of exception is need in order to show linenumber in which an abstract method is invoked. example: ---------- require 'abstract' class Foo abstract_method '*args', :m1, :m2 # removing the arity restriction end class Bar < Foo def m1; puts "Foo#m1() called."; end end obj = Bar.new obj.m1 #=> "Foo#m1() called." obj.m2 #=> ex.rb:10: class Bar should implement abstract method `Foo#m2()'. (NotImplementedError) ---------- -- regards, kwatch Jacob Fugal wrote: > On 3/11/06, kwatch wrote: > > I think it is convenient if the Module#abstract_method is defined. > > I'm of a similar opinion with others that predeclaring an abstract > method that raises NotImplementedError is of limited use when compared > with the existing NoMethodError duck-typing approach. As such, I don't > think it belongs in the core. > > *However*, this is a useful concept which could be used to extend the > capabilities of the language for those who do still want it. I > encourage you to build a library from this idea and publicize it. Just > because it doesn't belong in the core doesn't mean it won't be useful > to some. One place I can see this being used is if the > NotImplementedError gave a more descriptive output, such as "#{self} > requires that the host implement the method '#{method_name}'." E.g. > "Enumerable requires that the host implement the method 'each'." > > > ---------- > > class Module > > def abstract_method(*method_names) > > method_names.each do |name| > > s = <<-END > > def #{name} > > mesg = "\#{self.class.name}##{name}() is not implemented." > > raise NotImplementedError.new(mesg) > > end > > END > > module_eval s > > end > > end > > > > class Foo > > abstract_method :m1, :m2, :m3 # define abstract methods > > end > > > > obj = Foo.new > > obj.m1 #=> Foo#m1() is not implemented yet. (NotImplementedError) > > ---------- > > > > But this solution doesn't allow us to define a method with arguments. > > One thing that you can do to make this approach (which seems cleaner > and simpler than the backtrace manipulation approach you later > proposed) more flexible by removing the arity restriction on the > method using the splat operator: > > $ cat abstract.rb > class Module > def abstract_method(*method_names) > mesg_template = "#{self} requires that the host implement the > method '%s'." > method_names.each do |name| > mesg = mesg_template % [name] > module_eval <<-END > def #{name}(*args) > raise NotImplementedError.new("#{mesg}") > end > END > end > end > end > > module MyModule > def foo > bar("test") > end > > abstract_method :bar > end > > class MyClass > include MyModule > end > > a = MyClass.new > b = MyClass.new > > class << a > def bar( s ) > puts s > end > end > > a.foo > b.foo > > $ ruby abstract.rb > test > (eval):2:in `bar': MyModule requires that the host implement the > method 'bar'. (NotImplementedError) > from abstract.rb:17:in `foo' > from abstract.rb:37 > > -- > Jacob Fugal