Use of extend <module> from within a Class

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/.

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"

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

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

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 do:

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
=> nil
irb(main):053:0> class YY
irb(main):054:1> extend Basic
irb(main):055:1> end
=> YY
irb(main):056:0> YY.mb
=> "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 :slight_smile:

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
=> nil
irb(main):041:0> class XX
irb(main):042:1> extend Basic
irb(main):043:1> end
=> 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
=> 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.

···

On Sat, Dec 5, 2009 at 12:37 AM, Mike Papper <bodarotech@gmail.com> 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
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

Okay, at this point in the class definition self == 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

def self.z

  This is a class method of Use (because of def self.z) inside this
method self is also the class object Use

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

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

···

On Fri, Dec 4, 2009 at 6:37 PM, Mike Papper <bodarotech@gmail.com> 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

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 <bodarotech@gmail.com> > 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/\.

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/\.

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.

···

On Mon, Dec 7, 2009 at 4:26 PM, Mike Papper <bodarotech@gmail.com> 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.

--
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

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 <bodarotech@gmail.com> > 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/\.