Hi,

2009/9/1 Carl Lerche <clerche / engineyard.com>:
> Yusuke,
>
> The reason that something like #prepend would be needed is so that ruby can
> provide a good way to do AOP. Your example is not exactly a 1-1 mapping onto
> the prepend feature. Instead, you would need to do this:
>
>>  module Speak
>>   def speak(words)
>>     puts words
>>   end
>>  end
>>  module Exclaim
>>   def speak(words)
>>     super("#{ words }!")
>>   end
>>  end
>>  class Person # Empty class that only includes the module
>>   include Speak
>>  end
>>  class Exclaimer < Person
>>   include Exclaim
>>  end
>
>
> If your classes are NOT empty and only include modules, then it is
> impossible to include a module "under" methods defined directly on the
> class.


Do you talk about the following behavior as a problem?

  class Person
    def speak(words)
      puts words
    end
  end
  module Exclaim
    def speak(words)
      super("#{ words }!")
    end
  end
  class Person
    include Exclaim
  end
  Person.new.speak("matz") #=> "matz", not "matz!"


> This causes pain when you are using a ruby library that does not
> assume that you will be extending classes. Rails "solved" this problem using
> alias_method_chain, but this method can cause confusion.


Thank you, now it's starting to make sense.

However, I think that module inclusion is not such a patching tool;
open class is.  In the above case, we can achieve our intent by
redefining `speak' directly:

  class Person
    def speak(words)
      puts "#{ words }!"
    end
  end
  Person.new.speak("matz") #=> "matz!"

When `speak' has a more complex definition, this way is a pain because
we cannot use super.  It is possible to solve the pain by preserving
the original definition of speak as a alias, for example, old_speak.
But I admit the solution is ugly.

Then, I guess that what we really need is a method wrapping feature:

  class Person
    refine_method(:speak) do |old_method, words|
      # old_method is a Method of original definition
      old_method.call("#{ words }!")
    end
  end
  Person.new.speak("matz") #=> "matz!"

-- 
Yusuke ENDOH <mame / tsg.ne.jp>