Use of extend <module> from within a Class

Discussion in 'Ruby' started by Mike Papper, Dec 4, 2009.

  1. Mike Papper

    Mike Papper Guest

    I would like to include a module into a 'static' class - such that all
    the modules methods are class methods. The problem is that code in the
    module that uses "self.class.XYZ' breaks and when this code is included,
    ruby cannot find the XYZ method.

    Here is an example:

    module Basic
    def methoda
    puts "AAAAAAAAAAA"
    end

    def mb
    self.class.cee
    end

    module ClassMethods
    attr_writer :cee

    def cee
    puts "CCCCCCCCCCCCC"
    end
    end
    end

    class Use
    extend Basic

    def self.z
    mb
    end
    end

    If I do the following:
    Use.z

    I get this error:
    NoMethodError: undefined method `cee' for Class:Class
    from (irb):100:in `mb'
    from (irb):116:in `z'
    from (irb):119

    --------------
    The context for this is in Rails, I want to use
    ActionController::UrlWriter.url_for outside of the controller. And
    within a static class. However, it complains with this error:

    NoMethodError: undefined method `default_url_options' for Class:Class

    Heres the url_for method in ActionController::UrlWriter
    def url_for(options)
    options = self.class.default_url_options.merge(options) # ERROR
    OCCURS HERE

    url = ''
    ...
    ...
    end
    --------------

    I think my understanding of modules and using extend (or include) is
    faulty or at least doesnt cover the subtleties of when a modle uses
    "self.class" and its included in another class, then what becomes of
    self.class in this case??

    Any input into how this stuff works in Ruby is appreciated,

    Mike
    --
    Posted via http://www.ruby-forum.com/.
     
    Mike Papper, Dec 4, 2009
    #1
    1. Advertising

  2. On Sat, Dec 5, 2009 at 12:37 AM, Mike Papper <> wrote:
    > I would like to include a module into a 'static' class - such that all
    > the modules methods are class methods.


    One basic way to do this is to have the class extend the module:

    irb(main):017:0> module Z
    irb(main):018:1> def test; "testZ"; end
    irb(main):019:1> end
    =3D> nil
    irb(main):020:0> class C
    irb(main):021:1> extend Z
    irb(main):022:1> end
    =3D> C
    irb(main):023:0> C.test
    =3D> "testZ"


    > The problem is that code in the
    > module that uses "self.class.XYZ' breaks and when this code is included,
    > ruby cannot find the XYZ method.
    >
    > Here is an example:
    >
    > module Basic
    > =A0def methoda
    > =A0 =A0puts "AAAAAAAAAAA"
    > =A0end
    >
    > =A0def mb
    > =A0 =A0self.class.cee


    This is wrong. self.class is Class, and the Class class doesn't have a
    method "cee".

    > =A0end
    >
    > =A0module ClassMethods
    > =A0 =A0attr_writer :cee
    >
    > =A0 =A0def cee
    > =A0 =A0 =A0puts "CCCCCCCCCCCCC"
    > =A0 =A0end
    > =A0end
    > end


    I think you are confusing this usage. The name ClassMethods is a
    convention used to denote a set of methods that will be added to the
    class as class methods when you include this module. This is usually
    done as:

    module Test
    module ClassMethods
    def test; "test"; end
    end

    def self.included mod
    mod.extend ClassMethods
    end
    end

    If you simply want a method defined in the singleton class of your module d=
    o:

    module Basic
    def self.cee
    "CCCCCCC"
    end
    end

    If you want to call it from a method of the module:

    module Basic
    def mb
    Basic.cee
    end
    def self.cee
    "CCCCC"
    end
    end

    If you then want mb to be a class method of the classes you choose,
    you can use extend instead of include:

    irb(main):045:0> module Basic
    irb(main):046:1> def mb
    irb(main):047:2> Basic.cee
    irb(main):048:2> end
    irb(main):049:1> def self.cee
    irb(main):050:2> "CCCCCCCCCCCCC"
    irb(main):051:2> end
    irb(main):052:1> end
    =3D> nil
    irb(main):053:0> class YY
    irb(main):054:1> extend Basic
    irb(main):055:1> end
    =3D> YY
    irb(main):056:0> YY.mb
    =3D> "CCCCCCCCCCCCC"


    > I think my understanding of modules and using extend (or include) is
    > faulty or at least doesnt cover the subtleties of when a modle uses
    > "self.class" and its included in another class, then what becomes of
    > self.class in this case??


    You can ask Ruby :)

    irb(main):036:0> module Basic
    irb(main):037:1> def mb
    irb(main):038:2> puts self
    irb(main):039:2> end
    irb(main):040:1> end
    =3D> nil
    irb(main):041:0> class XX
    irb(main):042:1> extend Basic
    irb(main):043:1> end
    =3D> XX
    irb(main):044:0> XX.mb
    XX

    So, how it works is that when a class extends a module, the methods
    get added to the singleton class of the class. This means that the
    method is now in the lookup path of the class methods. But (and this
    is the same with the normal inheritance) self inside those methods is
    still the original object that received the method call, so in this
    case the class XX. Another example:

    irb(main):058:0> class Class
    irb(main):059:1> def test_class; puts self; end
    irb(main):060:1> end
    =3D> nil
    irb(main):061:0> XX.test_class
    XX

    This is with simple inheritance (XX inherits from Class). With
    extended modules it's the same, as shown above.
    Basically:

    1.- extend includes the methods in that module to the singleton class
    of the receiving object.
    2.- classes are objects
    3.- so when a class extends a module, the module instance methods (is
    that the correct way to refer to those?) get added to the singleton
    class of the class (commonly known as class methods).

    This stuff is tricky, and I don't know if I explained myself very
    well, so please ask if there's something not clear enough.

    Hope this helps,

    Jesus.
     
    Jesús Gabriel y Galán, Dec 4, 2009
    #2
    1. Advertising

  3. Mike Papper

    Mike Papper Guest

    Hi and thanks, please note that I am extending the module from within my
    class. the problem is exactly with the call of the form self.class...

    concerning what you said about self.class (inside the module):

    def mb
    self.class.cee

    I used this in my example because the rails module that I am extending
    does this. It is included using the method named 'helper' and that must
    do special things to make it work in the context of rails+controller.

    What I really want to do is use url_for of the rails'
    ActionController::UrlWriter. as it happens, that module does have
    ClassMethods and within there a method that calls self.class (which
    fails when I extend the module).

    Mike
    ----

    Jesús Gabriel y Galán wrote:
    > On Sat, Dec 5, 2009 at 12:37 AM, Mike Papper <>
    > wrote:
    >> I would like to include a module into a 'static' class - such that all
    >> the modules methods are class methods.

    >
    > One basic way to do this is to have the class extend the module:
    >
    > irb(main):017:0> module Z
    > irb(main):018:1> def test; "testZ"; end
    > irb(main):019:1> end
    > => nil
    > irb(main):020:0> class C
    > irb(main):021:1> extend Z
    > irb(main):022:1> end
    > => C
    > irb(main):023:0> C.test
    > => "testZ"
    >
    >
    >>
    >> �def mb
    >> � �self.class.cee

    >
    > This is wrong. self.class is Class, and the Class class doesn't have a
    > method "cee".
    >
    >> �end
    >>
    >> �module ClassMethods
    >> � �attr_writer :cee
    >>
    >> � �def cee
    >> � � �puts "CCCCCCCCCCCCC"
    >> � �end
    >> �end
    >> end

    >


    --
    Posted via http://www.ruby-forum.com/.
     
    Mike Papper, Dec 5, 2009
    #3
  4. On Fri, Dec 4, 2009 at 6:37 PM, Mike Papper <> wrote:
    > I would like to include a module into a 'static' class - such that all
    > the modules methods are class methods. The problem is that code in the
    > module that uses "self.class.XYZ' breaks and when this code is included,
    > ruby cannot find the XYZ method.
    >
    > Here is an example:
    >
    > module Basic
    > =A0def methoda
    > =A0 =A0puts "AAAAAAAAAAA"
    > =A0end
    >
    > =A0def mb
    > =A0 =A0self.class.cee
    > =A0end
    >
    > =A0module ClassMethods
    > =A0 =A0attr_writer :cee
    >
    > =A0 =A0def cee
    > =A0 =A0 =A0puts "CCCCCCCCCCCCC"
    > =A0 =A0end
    > =A0end
    > end


    > class Use
    > =A0extend Basic


    Okay, at this point in the class definition self =3D=3D Use

    and extend Basic

    mixes the Basic module into the singleton class of Use, so all of the
    methods of Basic (i.e. mb and methoda) are now CLASS methods of Use

    There is no actual usage of Basic::ClassMethods

    > =A0def self.z

    This is a class method of Use (because of def self.z) inside this
    method self is also the class object Use
    > =A0 =A0mb
    > =A0end
    > end
    >
    > If I do the following:
    > =A0Use.z
    >
    > I get this error:
    > NoMethodError: undefined method `cee' for Class:Class
    > =A0 =A0 =A0 =A0from (irb):100:in `mb'
    > =A0 =A0 =A0 =A0from (irb):116:in `z'
    > =A0 =A0 =A0 =A0from (irb):119


    so what happened.

    in Basic#mb we have

    def mb
    self.class.cee
    end

    and self is still the class object Use, but Use.class is class, which
    is one issue. Even if the mb method had self.cee, Use doesn't have cee
    as a class method because although Basic::ClassMethods was defined, it
    was never referenced.

    As Jesus pointed out, you appeared to be confused with the way Rails,
    and lots of other ruby code causes modules to add both instance and
    class methods to classes which include them.

    The normal pattern is

    module SomeModule
    # define any instance methods here

    module ClassMethods
    # define any class methods here
    end

    # and here is the crucial step this gets called when some class
    # includes the module
    def self.included(some_class)
    some_class.extend(ClassMethods)
    end
    end

    So if you want to make a module which adds only class methods either

    1) define the module 'normally' and in the class which wants the
    methods as class methods

    class Foo
    extend MyGreatModuleOfClassMethods
    end

    or

    2) Use the pattern above, leave the instance methods out, and put all
    of the methods in the inner ClassMethods module (actually the name of
    this module is irrelevant except for clarity. then in the using class

    class Foo
    include MyGreatModuleWhichOnlyHasClassMethodsRightNow
    end

    HTH




    --=20
    Rick DeNatale

    Blog: http://talklikeaduck.denhaven2.com/
    Twitter: http://twitter.com/RickDeNatale
    WWR: http://www.workingwithrails.com/person/9021-rick-denatale
    LinkedIn: http://www.linkedin.com/in/rickdenatale
     
    Rick DeNatale, Dec 5, 2009
    #4
  5. Mike Papper

    Mike Papper Guest

    Thanks for the clarification and sample.

    So, I cannot simply extend the ActionController::UrlWriter module in my
    class because that module does this:

    self.class.default_url_options.merge(options)

    However, within a rails controller, this module can be included as a
    "helper". Does anyone know how the magic rails helper function makes
    this call to self.class.default_url work? I've looked at the code for
    helper but cannot really make any sense of it.

    Mike

    ...
    > The normal pattern is
    >
    > module SomeModule
    > # define any instance methods here
    >
    > module ClassMethods
    > # define any class methods here
    > end
    >
    > # and here is the crucial step this gets called when some class
    > # includes the module
    > def self.included(some_class)
    > some_class.extend(ClassMethods)
    > end
    > end
    >
    > So if you want to make a module which adds only class methods either
    >
    > 1) define the module 'normally' and in the class which wants the
    > methods as class methods
    >
    > class Foo
    > extend MyGreatModuleOfClassMethods
    > end
    >
    > or
    >
    > 2) Use the pattern above, leave the instance methods out, and put all
    > of the methods in the inner ClassMethods module (actually the name of
    > this module is irrelevant except for clarity. then in the using class
    >
    > class Foo
    > include MyGreatModuleWhichOnlyHasClassMethodsRightNow
    > end
    >
    > HTH
    >
    >
    >
    >
    > --
    > Rick DeNatale
    >
    > Blog: http://talklikeaduck.denhaven2.com/
    > Twitter: http://twitter.com/RickDeNatale
    > WWR: http://www.workingwithrails.com/person/9021-rick-denatale
    > LinkedIn: http://www.linkedin.com/in/rickdenatale


    --
    Posted via http://www.ruby-forum.com/.
     
    Mike Papper, Dec 7, 2009
    #5
  6. On Mon, Dec 7, 2009 at 4:26 PM, Mike Papper <> wrote:
    > Thanks for the clarification and sample.
    >
    > So, I cannot simply extend the ActionController::UrlWriter module in my
    > class because that module does this:
    >
    > self.class.default_url_options.merge(options)
    >
    > However, within a rails controller, this module can be included as a
    > "helper". Does anyone know how the magic rails helper function makes
    > this call to self.class.default_url work? I've looked at the code for
    > helper but cannot really make any sense of it.


    MyController < ActionController::Base
    include UrlWriter # include not extend
    end

    If you read the code in actionpack/lib/action_controller/helpers.rb it
    really isn't that hard to see what's going on

    if in the controller you write say

    helper :url_writer

    then the helper method takes the case branch for a symbol. which is:

    file_name = arg.to_s.underscore + '_helper'
    class_name = file_name.camelize

    begin
    require_dependency(file_name)
    rescue LoadError => load_error
    requiree = / -- (.*?)(\.rb)?$/.match(load_error.message).to_a[1]
    if requiree == file_name
    msg = "Missing helper file helpers/#{file_name}.rb"
    raise LoadError.new(msg).copy_blame!(load_error)
    else
    raise
    end
    end

    add_template_helper(class_name.constantize)

    So it computes a file name from the argument, in this case
    "url_writer.rb", and a class (actually a Module) name "UrlWriter"

    Then require dependency loads the file if necessary, and finally.

    class_name.constantize turns teh class name into the actual module,
    and passes it as the argument to add_template_helper which is defined
    above as:

    def add_template_helper(helper_module) #:nodoc:
    master_helper_module.module_eval { include helper_module }
    end

    So it is effectively the same thing as what I first wrote.

    --
    Rick DeNatale

    Blog: http://talklikeaduck.denhaven2.com/
    Twitter: http://twitter.com/RickDeNatale
    WWR: http://www.workingwithrails.com/person/9021-rick-denatale
    LinkedIn: http://www.linkedin.com/in/rickdenatale
     
    Rick DeNatale, Dec 7, 2009
    #6
  7. Mike Papper

    Mike Papper Guest

    Hi and thanks for explaining the helper method. I understand whats going
    on except for

    master_helper_module.module_eval

    -what is master_helper_module ? (Presumably it has the effect of
    "include ModuleName - but how that makes self.class.XYZ work is still
    not understood).

    ----------------
    Back to the UrlWriter module...

    I did some tests and noticed that self.included is not called when a
    module is extended (versus included). For UrlWriter, this means that
    this code doesnt get executed:
    def self.included(base) #:nodoc:
    ActionController::Routing::Routes.install_helpers(base)
    base.mattr_accessor :default_url_options
    base.default_url_options ||= default_url_options
    end

    I changed my code to explicitly call that method after I extended, ala:
    def self.staticmethod
    extend ActionController::UrlWriter
    ActionController::UrlWriter.included(self) # the class
    self.default_url_options = {} # doesnt seem to do much

    but still this line from UrlWriter.url_for fails:

    self.class.default_url_options.merge(options)

    As you said, self.class refers to Class so I dont see how that line
    works when the module is included as a helper. Can you explain this??

    Mike

    Rick Denatale wrote:
    > On Mon, Dec 7, 2009 at 4:26 PM, Mike Papper <>
    > wrote:
    >> helper but cannot really make any sense of it.

    > MyController < ActionController::Base
    > include UrlWriter # include not extend
    > end
    >
    > If you read the code in actionpack/lib/action_controller/helpers.rb it
    > really isn't that hard to see what's going on
    >
    > if in the controller you write say
    >
    > helper :url_writer
    >
    > then the helper method takes the case branch for a symbol. which is:
    >
    > file_name = arg.to_s.underscore + '_helper'
    > class_name = file_name.camelize
    >
    > begin
    > require_dependency(file_name)
    > rescue LoadError => load_error
    > requiree = / --
    > (.*?)(\.rb)?$/.match(load_error.message).to_a[1]
    > if requiree == file_name
    > msg = "Missing helper file helpers/#{file_name}.rb"
    > raise LoadError.new(msg).copy_blame!(load_error)
    > else
    > raise
    > end
    > end
    >
    > add_template_helper(class_name.constantize)
    >
    > So it computes a file name from the argument, in this case
    > "url_writer.rb", and a class (actually a Module) name "UrlWriter"
    >
    > Then require dependency loads the file if necessary, and finally.
    >
    > class_name.constantize turns teh class name into the actual module,
    > and passes it as the argument to add_template_helper which is defined
    > above as:
    >
    > def add_template_helper(helper_module) #:nodoc:
    > master_helper_module.module_eval { include helper_module }
    > end
    >
    > So it is effectively the same thing as what I first wrote.
    >
    > --
    > Rick DeNatale
    >
    > Blog: http://talklikeaduck.denhaven2.com/
    > Twitter: http://twitter.com/RickDeNatale
    > WWR: http://www.workingwithrails.com/person/9021-rick-denatale
    > LinkedIn: http://www.linkedin.com/in/rickdenatale


    --
    Posted via http://www.ruby-forum.com/.
     
    Mike Papper, Dec 8, 2009
    #7
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Replies:
    6
    Views:
    557
  2. §ä´M¦Û¤vªº¤@¤ù¤Ñ

    python extend c++ module

    §ä´M¦Û¤vªº¤@¤ù¤Ñ, Jun 29, 2009, in forum: Python
    Replies:
    0
    Views:
    311
    §ä´M¦Û¤vªº¤@¤ù¤Ñ
    Jun 29, 2009
  3. Shen, Yu-Teh

    python extend c++ module

    Shen, Yu-Teh, Jun 30, 2009, in forum: Python
    Replies:
    0
    Views:
    266
    Shen, Yu-Teh
    Jun 30, 2009
  4. Shen, Yu-Teh
    Replies:
    3
    Views:
    808
    Philip Semanchuk
    Jul 3, 2009
  5. Patrick Gundlach

    extend(Module) and inheritance

    Patrick Gundlach, Jun 11, 2005, in forum: Ruby
    Replies:
    2
    Views:
    101
    Patrick Gundlach
    Jun 11, 2005
Loading...

Share This Page