John Merlino
2012-Sep-18 01:04 UTC
multiple modules defining same method included into a class
In Ruby, classes are never closed: you can always add methods to an existing class. This applies to the classes you write as well as the standard, built-in classes. All you have to do is open up a class definition for an existing class, and the new contents you specify will be added to whatever''s there. In the same regard, if module is used with the name of a pre-existing module that module is re-opened. If a method is defined in a re-opened module with the same name as a pre-existing method in the same module the old method is overwritten with the new. And, of course, modules can be made immutable, effectively preventing them from being reopened by freezing the module object. Frozen modules raise RuntimeErrors when methods are defined, or variables manipulated, in their context. When you include a module in a class, without a prefix (prefix meaning something like this: AbstractController::Layouts), then ruby searches for the module of that name within the scope of the current module wrapping the class that the include was called in. So, for example: module ActionController class Base include Rendering Since we "include Rendering" within the scope of the Base class object, ruby first looksup the constant Rendering, that is, looking for a module named Rendering in the ActionController namespace, since that is the namesapce that Base is defined in??? Well, ActionController module does define a module named Rendering: module ActionController module Rendering def render(*args) raise ::AbstractController::DoubleRenderError if response_body super self.content_type ||= Mime[lookup_context.rendered_format].to_s response_body end So now the method render(*args) is included in the Base class as an instance method. So when we call, for example, (hypothetically) ActionView::Base.new.render :action => "my_action", :layout => false, ActionView::Base is instantiated and we invoke the render instance method, and since no render method is declared in the Base class context, ruby scope chain does lookup into the module Rendering and finds the method so calls it. Now, as you may know, it''s possible to pass multiple kinds of arguments to render, depending on whether you want to render a template or a partial or file, etc. The * syntax does the job here, allowing you vary the number of arguments passed. It checks if there is already a response to the request, and raises an Exception, if so. Otherwise, it calls super. During this super call, I got a little confused. In ActionController::Base, there is another module included called AbstractController::Layouts. Layouts in turn includes a module called Rendering. Since there is a Rendering module within the AbstractController namespace, it is found, and it indeed contains a method called render. def render(*args, &block) options = _normalize_render(*args, &block) self.response_body = render_to_body(options) end And because render is included in Layouts, it''s included as an instance method, and because Layouts was included in ActionController::Base, that render method utlimately makes its way up to the Base class. Now we have two modules (ActionController::Rendering and AbstractController::Rendering that are including this method into Base. So why doesn''t one of them get overwritten? Why does the call to super work? -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org For more options, visit https://groups.google.com/groups/opt_out.
John Merlino
2012-Sep-18 04:39 UTC
Re: multiple modules defining same method included into a class
So it does appear that the ActionController::Rendering module does include AbstractController::Rendering, which means a call to super in the render method of ActionController::Rendering will in turn call the render method of AbstractController::Rendering. What makes this somewhat confusing is that ActionController::Rendering is included in ActionController::Base, but AbstractController::Rendering is included in ActionController::Base as well. Yet AbstractController::Rendering is also included in ActionController::Rendering. Wouldn''t AbstractController::Rendering being included ActionController::Rendering and the fact that ActionController::Rendering is included in ActionController::Base make AbstractController::Rendering being included in ActionController::Base redundant? On Sep 17, 9:04 pm, John Merlino <stoici...-YDxpq3io04c@public.gmane.org> wrote:> In Ruby, classes are never closed: you can always add methods to an > existing class. This applies to the classes you write as well as the > standard, built-in classes. All you have to do is open up a class > definition for an existing class, and the new contents you specify > will be added to whatever''s there. In the same regard, if module is > used with the name of a pre-existing module that module is re-opened. > If a method is defined in a re-opened module with the same name as a > pre-existing method in the same module the old method is overwritten > with the new. And, of course, modules can be made immutable, > effectively preventing them from being reopened by freezing the module > object. Frozen modules raise RuntimeErrors when methods are defined, > or variables manipulated, in their context. > > When you include a module in a class, without a prefix (prefix meaning > something like this: AbstractController::Layouts), then ruby searches > for the module of that name within the scope of the current module > wrapping the class that the include was called in. > > So, for example: > > module ActionController > class Base > include Rendering > > Since we "include Rendering" within the scope of the Base class > object, ruby first looksup the constant Rendering, that is, looking > for a module named Rendering in the ActionController namespace, since > that is the namesapce that Base is defined in??? > > Well, ActionController module does define a module named Rendering: > > module ActionController > module Rendering > def render(*args) > raise ::AbstractController::DoubleRenderError if response_body > super > self.content_type ||= Mime[lookup_context.rendered_format].to_s > response_body > end > > So now the method render(*args) is included in the Base class as an > instance method. So when we call, for example, (hypothetically) > ActionView::Base.new.render :action => "my_action", :layout => false, > ActionView::Base is instantiated and we invoke the render instance > method, and since no render method is declared in the Base class > context, ruby scope chain does lookup into the module Rendering and > finds the method so calls it. Now, as you may know, it''s possible to > pass multiple kinds of arguments to render, depending on whether you > want to render a template or a partial or file, etc. The * syntax does > the job here, allowing you vary the number of arguments passed. It > checks if there is already a response to the request, and raises an > Exception, if so. Otherwise, it calls super. > > During this super call, I got a little confused. In > ActionController::Base, there is another module included called > AbstractController::Layouts. Layouts in turn includes a module called > Rendering. Since there is a Rendering module within the > AbstractController namespace, it is found, and it indeed contains a > method called render. > > def render(*args, &block) > options = _normalize_render(*args, &block) > self.response_body = render_to_body(options) > end > > And because render is included in Layouts, it''s included as an > instance method, and because Layouts was included in > ActionController::Base, that render method utlimately makes its way up > to the Base class. Now we have two modules > (ActionController::Rendering and AbstractController::Rendering that > are including this method into Base. So why doesn''t one of them get > overwritten? Why does the call to super work?-- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
Matt Jones
2012-Sep-18 13:14 UTC
Re: multiple modules defining same method included into a class
On Monday, 17 September 2012 21:04:58 UTC-4, John Merlino wrote:> > In Ruby, classes are never closed: you can always add methods to an > existing class. This applies to the classes you write as well as the > standard, built-in classes. All you have to do is open up a class > definition for an existing class, and the new contents you specify > will be added to whatever''s there. In the same regard, if module is > used with the name of a pre-existing module that module is re-opened. > If a method is defined in a re-opened module with the same name as a > pre-existing method in the same module the old method is overwritten > with the new. And, of course, modules can be made immutable, > effectively preventing them from being reopened by freezing the module > object. Frozen modules raise RuntimeErrors when methods are defined, > or variables manipulated, in their context. > > When you include a module in a class, without a prefix (prefix meaning > something like this: AbstractController::Layouts), then ruby searches > for the module of that name within the scope of the current module > wrapping the class that the include was called in. > > So, for example: > > module ActionController > class Base > include Rendering > > Since we "include Rendering" within the scope of the Base class > object, ruby first looksup the constant Rendering, that is, looking > for a module named Rendering in the ActionController namespace, since > that is the namesapce that Base is defined in??? > > Well, ActionController module does define a module named Rendering: > > module ActionController > module Rendering > def render(*args) > raise ::AbstractController::DoubleRenderError if response_body > super > self.content_type ||= Mime[lookup_context.rendered_format].to_s > response_body > end > > > So now the method render(*args) is included in the Base class as an > instance method. So when we call, for example, (hypothetically) > ActionView::Base.new.render :action => "my_action", :layout => false, > ActionView::Base is instantiated and we invoke the render instance > method, and since no render method is declared in the Base class > context, ruby scope chain does lookup into the module Rendering and > finds the method so calls it. Now, as you may know, it''s possible to > pass multiple kinds of arguments to render, depending on whether you > want to render a template or a partial or file, etc. The * syntax does > the job here, allowing you vary the number of arguments passed. It > checks if there is already a response to the request, and raises an > Exception, if so. Otherwise, it calls super. > > During this super call, I got a little confused. In > ActionController::Base, there is another module included called > AbstractController::Layouts. Layouts in turn includes a module called > Rendering. Since there is a Rendering module within the > AbstractController namespace, it is found, and it indeed contains a > method called render. > > def render(*args, &block) > options = _normalize_render(*args, &block) > self.response_body = render_to_body(options) > end > > And because render is included in Layouts, it''s included as an > instance method, and because Layouts was included in > ActionController::Base, that render method utlimately makes its way up > to the Base class. Now we have two modules > (ActionController::Rendering and AbstractController::Rendering that > are including this method into Base. So why doesn''t one of them get > overwritten? Why does the call to super work? > >The modules aren''t "included" exactly - they''re just added to the list of ancestors (try ActionController::Base.ancestors for an eyeful) where methods are looked up. Modules included later in the source are "higher" on the list (similar to subclass methods vs. base class methods) and calling ''super'' simply specifies that the *next* available method up the chain should be called. This works (as of recent Rails versions) for generated methods as well: class SomeModel < ActiveRecord::Base has_many :wats def wats # calling ''super'' here hits the generated accessor from has_many end end There''s one additional wrinkle for many of the modules in Rails itself - many of them are extended with ActiveSupport::Concern, which tidies a bunch of things up and (most relevant here) changes the semantics of "include SomeOtherModule". See this article for more: http://opensoul.org/blog/archives/2011/02/07/concerning-activesupportconcern/ --Matt Jones -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To view this discussion on the web visit https://groups.google.com/d/msg/rubyonrails-talk/-/B3FfuJtZ4KIJ. For more options, visit https://groups.google.com/groups/opt_out.
John Merlino
2012-Sep-19 17:26 UTC
Re: multiple modules defining same method included into a class
Yep: 1.9.3p0 :005 > module B 1.9.3p0 :006?> def name 1.9.3p0 :007?> puts ''b module'' 1.9.3p0 :008?> end 1.9.3p0 :009?> end => nil 1.9.3p0 :010 > module C 1.9.3p0 :011?> def name 1.9.3p0 :012?> puts ''c module'' 1.9.3p0 :013?> end 1.9.3p0 :014?> end => nil 1.9.3p0 :015 > class A 1.9.3p0 :016?> include B 1.9.3p0 :017?> include C 1.9.3p0 :018?> end => A 1.9.3p0 :019 > A.new.name c module => nil On Sep 18, 9:14 am, Matt Jones <al2o...-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:> On Monday, 17 September 2012 21:04:58 UTC-4, John Merlino wrote: > > > In Ruby, classes are never closed: you can always add methods to an > > existing class. This applies to the classes you write as well as the > > standard, built-in classes. All you have to do is open up a class > > definition for an existing class, and the new contents you specify > > will be added to whatever''s there. In the same regard, if module is > > used with the name of a pre-existing module that module is re-opened. > > If a method is defined in a re-opened module with the same name as a > > pre-existing method in the same module the old method is overwritten > > with the new. And, of course, modules can be made immutable, > > effectively preventing them from being reopened by freezing the module > > object. Frozen modules raise RuntimeErrors when methods are defined, > > or variables manipulated, in their context. > > > When you include a module in a class, without a prefix (prefix meaning > > something like this: AbstractController::Layouts), then ruby searches > > for the module of that name within the scope of the current module > > wrapping the class that the include was called in. > > > So, for example: > > > module ActionController > > class Base > > include Rendering > > > Since we "include Rendering" within the scope of the Base class > > object, ruby first looksup the constant Rendering, that is, looking > > for a module named Rendering in the ActionController namespace, since > > that is the namesapce that Base is defined in??? > > > Well, ActionController module does define a module named Rendering: > > > module ActionController > > module Rendering > > def render(*args) > > raise ::AbstractController::DoubleRenderError if response_body > > super > > self.content_type ||= Mime[lookup_context.rendered_format].to_s > > response_body > > end > > > So now the method render(*args) is included in the Base class as an > > instance method. So when we call, for example, (hypothetically) > > ActionView::Base.new.render :action => "my_action", :layout => false, > > ActionView::Base is instantiated and we invoke the render instance > > method, and since no render method is declared in the Base class > > context, ruby scope chain does lookup into the module Rendering and > > finds the method so calls it. Now, as you may know, it''s possible to > > pass multiple kinds of arguments to render, depending on whether you > > want to render a template or a partial or file, etc. The * syntax does > > the job here, allowing you vary the number of arguments passed. It > > checks if there is already a response to the request, and raises an > > Exception, if so. Otherwise, it calls super. > > > During this super call, I got a little confused. In > > ActionController::Base, there is another module included called > > AbstractController::Layouts. Layouts in turn includes a module called > > Rendering. Since there is a Rendering module within the > > AbstractController namespace, it is found, and it indeed contains a > > method called render. > > > def render(*args, &block) > > options = _normalize_render(*args, &block) > > self.response_body = render_to_body(options) > > end > > > And because render is included in Layouts, it''s included as an > > instance method, and because Layouts was included in > > ActionController::Base, that render method utlimately makes its way up > > to the Base class. Now we have two modules > > (ActionController::Rendering and AbstractController::Rendering that > > are including this method into Base. So why doesn''t one of them get > > overwritten? Why does the call to super work? > > The modules aren''t "included" exactly - they''re just added to the list of > ancestors (try ActionController::Base.ancestors for an eyeful) where > methods are looked up. Modules included later in the source are "higher" on > the list (similar to subclass methods vs. base class methods) and calling > ''super'' simply specifies that the *next* available method up the chain > should be called. This works (as of recent Rails versions) for generated > methods as well: > > class SomeModel < ActiveRecord::Base > has_many :wats > > def wats > # calling ''super'' here hits the generated accessor from has_many > end > end > > There''s one additional wrinkle for many of the modules in Rails itself - > many of them are extended with ActiveSupport::Concern, which tidies a bunch > of things up and (most relevant here) changes the semantics of "include > SomeOtherModule". See this article for more: > > http://opensoul.org/blog/archives/2011/02/07/concerning-activesupport... > > --Matt Jones-- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.
7stud --
2012-Sep-19 18:39 UTC
Re: multiple modules defining same method included into a class
>…looking for a module named Rendering in the ActionController > namespace, since that is the namesapce that Base is defined in???module Rendering def greet puts ''hi'' end end module ActionController class Base include Rendering end end obj = ActionController::Base.new obj.greet --output:-- hi module Rendering def greet puts ''hi'' end end module ActionController module Rendering def greet puts ''bye'' end end class Base include Rendering end end obj = ActionController::Base.new obj.greet --output:-- bye module Rendering def greet puts ''hi'' end end module ActionController module Rendering def greet puts ''bye'' end end class Base include Rendering module Rendering def greet puts "It''s hot down here." end end end end obj = ActionController::Base.new obj.greet --output:-- bye module Rendering def greet puts ''hi'' end end module ActionController module Rendering def greet puts ''bye'' end end class Base module Rendering def greet puts "It''s hot down here." end end include Rendering end end obj = ActionController::Base.new obj.greet --output:-- It''s hot down here. -- Posted via http://www.ruby-forum.com/. -- You received this message because you are subscribed to the Google Groups "Ruby on Rails: Talk" group. To post to this group, send email to rubyonrails-talk-/JYPxA39Uh5TLH3MbocFF+G/Ez6ZCGd0@public.gmane.org To unsubscribe from this group, send email to rubyonrails-talk+unsubscribe@googlegroups.com. For more options, visit https://groups.google.com/groups/opt_out.