Steve Gooberman-Hill
Steve Gooberman-Hill

Reputation: 513

adding class specific functionality in ruby modules

possibly I'm not explaining the concept very well, but I'm looking to add class methods to a series of ruby classes to enable them to hold class specific information which will then be called by individual instance methods of the classes.

I can make it work, but it is a bit ugly. Can anyone as it requires 2 modules, one included and the other extended (see example code below).

Can anyone think of a more elegant way of implementing this functionality ?

Thanks

Steve

This module is extended to give class methods but adding an instance member to each class it is included in

module My1
   def my_methods (*sym_array)
     @my_methods=sym_array
   end

   def method_list
      @my_methods
   end
end

This module is included to give instance methods

module My2
   def foo
      self.class.method_list.each { |m| self.send m }
   end
end

Now use the modules - the ugliness is having to use an include and extend statement to allow me to pass a set of symbols to a class method which will then be implemented in an instance

class Foo
   extend My1
   include My2

   my_methods :baz

   def baz
      puts "Baz!"
   end
end

class Bar
   extend My1
   include My2

   my_methods :frodo

   def frodo
      puts "Frodo!"
   end

end

class Wibble < Bar
   extend My1
   include My2

   my_methods :wobble

  def wobble
     puts "Wobble!"
  end
end

Here is the required output - note that each class has its own instance @my_methods so the behaviour is different for the derived class Wibble < Bar

f=Foo.new
b=Bar.new
w=Wibble.new

f.foo #=> "Bar!"
b.foo #=> "Frodo!"
w.foo #=> "Wobble!"

Upvotes: 1

Views: 121

Answers (2)

Samnang
Samnang

Reputation: 5616

I would suggest to use a hook from module instead:

module MyModule
  def self.included(klass)
    klass.extend ClassMethods
  end

  def foo
    self.class.method_list.each{ |m| self.send m }
  end

  module ClassMethods
    attr_reader :method_list

    def my_methods(*sym_array)
      @method_list = sym_array
    end
  end
end

So it simplifies to call include only a module whenever you want the functionality to given classes.

class Foo
  include MyModule

   my_methods :baz

   def baz
      puts "Baz!"
   end
end

Upvotes: 1

Frederick Cheung
Frederick Cheung

Reputation: 84114

When a module is included, a hook is called on it. You can use that to do the extend you want.

module M1
  def self.included(base)
    base.extend(M2)
  end
end

People often call that second module M1::ClassMethods. If you're using rails, ActiveSupport::Concern encapsulates this pattern

Upvotes: 3

Related Questions