From: Yehuda Katz Date: 2009-09-01T03:12:04+09:00 Subject: [ruby-core:25226] Re: Module#prepend and Array#prepend --00c09f8c1fb505cd94047273973f Content-Type: text/plain; charset=ISO-8859-1 Indeed, The existing in-ruby solutions only work if the original author takes pains to supports extensions. In Rails 3, we do this. However, this is a large burden on Ruby programmers, and we could provide an in-ruby solution (prepend) that guarantees that any class can be extended. Consider the following (extremely simplified) scenario: class ActionController def render(template) find_template(name).render(self) end def find_template(name) return Template.new(name) end end Again, this is an extremely simplified example. Now what if I want to add support for layouts to ActionController? Because render is defined on the class itself, I cannot do so. As the creator of ActionController, I can do: module Renderer def render(template) find_template(name).render(self) end def find_template(name) return Template.new(name) end end class ActionController include Renderer end And then to extend, I can do: module Layouts def render(template, layout) find_template(layout) { super } end end class ActionController include Layouts end Because the original creator of ActionController put the implementation in a module, another Ruby programmer can extend it. But frequently, Ruby code does not do this. I would like to be able to do: module Layouts def render(template, layout) find_template(layout) { super } end end class ActionController prepend Layouts end which would have the same effect as when the implementation was explicitly inserted into a module, but it'll work on *any* class, not just ones specially designed. -- Yehuda On Mon, Aug 31, 2009 at 9:41 AM, Carl Lerche wrote: > 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. 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. > > I hope I explained my thoughts clearly. > > Regards, > Carl Lerche > > > On Aug 31, 2009, at 5:39 AM, Yusuke ENDOH wrote: > > Hi, >> >> 2009/8/31 Yehuda Katz : >> >>> Matz, >>> As we discussed at LoneStar, I have the following proposal: >>> >> (snip) >> >>> Effectively, prepend would prepend a module to the class' ancestor chain. >>> >> >> >> I cannot guess the problem you want to solve. I know this mail >> is just discussion log or reminder, but I'd like you to elaborate >> your proposal completely when sending to ruby-core. >> >> Why don't you define subclass explicitly? >> >> class Person >> def speak(words) >> puts words >> end >> end >> module Exclaim >> def speak(words) >> super("#{ words }!") >> end >> end >> class Exclaimer < Person >> include Exclaim >> end >> Exclaimer.new.speak("matz") #=> matz! >> >> As far as I read your mail, I think this is the correct way. >> >> Do you want to define Person by including Exclaimer? If so, >> how about this? >> >> class Person >> def speak(words) >> puts adjust_words(words) >> end >> end >> module Exclaimer >> def adjust_words(words) >> "#{ words }!" >> end >> end >> class Person >> include Exclaimer >> end >> Person.new.speak("matz") #=> matz! >> >> -- >> Yusuke ENDOH >> >> > > -- Yehuda Katz Developer | Engine Yard (ph) 718.877.1325 --00c09f8c1fb505cd94047273973f Content-Type: text/html; charset=ISO-8859-1 Content-Transfer-Encoding: quoted-printable Indeed,

The existing in-ruby solutions only work if the = original author takes pains to supports extensions. In Rails 3, we do this.= However, this is a large burden on Ruby programmers, and we could provide = an in-ruby solution (prepend) that guarantees that any class can be extende= d.

Consider the following (extremely simplified) scenario:=

class ActionController
=A0=A0def render= (template)
=A0=A0 =A0find_template(name).render(self)
= =A0=A0end

=A0=A0def find_template(name)
=A0=A0 =A0retur= n Template.new(name)
=A0=A0end
end

=
Again, this is an extremely simplified example. Now what if I want to = add support for layouts to ActionController? Because render is defined on t= he class itself, I cannot do so. As the creator of ActionController, I can = do:

module Renderer
=A0=A0def render(templat= e)
=A0=A0 =A0find_template(name).render(self)
=A0=A0end=

=A0=A0def find_template(name)
=A0=A0 = =A0return Template.new(name)
=A0=A0end
end

class ActionControlle= r
=A0=A0include Renderer
end

A= nd then to extend, I can do:

module Layouts
<= div> =A0=A0def render(template, layout)
=A0=A0 =A0find_template(layout= ) { super }
=A0=A0end
end

class ActionController
=A0=A0include Layouts
end

Because the original creator of ActionController put the implementatio= n in a module, another Ruby programmer can extend it. But frequently, Ruby = code does not do this. I would like to be able to do:

module Layouts
=A0=A0def render(template, layout)<= /div>
=A0=A0 =A0find_template(layout) { super }
=A0=A0end
end

class ActionController
=A0=A0= prepend Layouts
end

which would have the same effect as when = the implementation was explicitly inserted into a module, but it'll wor= k on any=A0class, not just ones specially designed.

-- Yehuda
On Mon, Aug 31, = 2009 at 9:41 AM, Carl Lerche <clerche@engineyard.com> wrote:
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 on= to the prepend feature. Instead, you would need to do this:

=A0module Speak

=A0 def speak(words)
=A0 =A0 puts words
=A0 end
=A0end
=A0module Exclaim
=A0 def speak(words)
=A0 =A0 super("#{ words }!")
=A0 end
=A0end
=A0class Person # Empty class that only includes the module
=A0 include Speak

=A0end
=A0class Exclaimer < Person
=A0 include Exclaim
=A0end


If your classes are NOT empty and only include modules, then it is impossib= le to include a module "under" methods defined directly on the cl= ass. This causes pain when you are using a ruby library that does not assum= e that you will be extending classes. Rails "solved" this problem= using alias_method_chain, but this method can cause confusion.

I hope I explained my thoughts clearly.

Regards,
Carl Lerche


On Aug 31, 2009, at 5:39 AM, Yusuke ENDOH wrote:

Hi,

2009/8/31 Yehuda Katz <wycats@gmail.com>:
Matz,
As we discussed at LoneStar, I have the following proposal:
(snip)
Effectively, prepend would prepend a module to the class' ancestor chai= n.


I cannot guess the problem you want to solve. =A0I know this mail
is just discussion log or reminder, but I'd like you to elaborate
your proposal completely when sending to ruby-core.

Why don't you define subclass explicitly?

=A0class Person
=A0 def speak(words)
=A0 =A0 puts words
=A0 end
=A0end
=A0module Exclaim
=A0 def speak(words)
=A0 =A0 super("#{ words }!")
=A0 end
=A0end
=A0class Exclaimer < Person
=A0 include Exclaim
=A0end
=A0Exclaimer.new.speak("matz") #=3D> matz!

As far as I read your mail, I think this is the correct way.

Do you want to define Person by including Exclaimer? =A0If so,
how about this?

=A0class Person
=A0 def speak(words)
=A0 =A0 puts adjust_words(words)
=A0 end
=A0end
=A0module Exclaimer
=A0 def adjust_words(words)
=A0 =A0 "#{ words }!"
=A0 end
=A0end
=A0class Person
=A0 include Exclaimer
=A0end
=A0Person.new.speak("matz") #=3D> matz!

--
Yusuke ENDOH <mame@t= sg.ne.jp>






--
Yehuda Katz=
Developer | Engine Yard
(ph) 718.877.1325
--00c09f8c1fb505cd94047273973f--