3

Here's the deal: I need to extend specifica instances of the class Box with some methods. The methods i need to include live inside modules and i want the Box instance to be able to include the modules dynamically. Now i am using a hook with an eval:

class Box
  def after_initialize
    if self.injected_module.present?
      eval("class << self; include #{self.injected_module}; end")
    end
  end
end

It is working quite well but i really feel dirty when i use eval. I'm looking for something like that:

module_to_inject = self.injected_module
self.eigenclass.class_eval do
   include module_to_inject
end

but i'm not being able to get the eigenclass to run class_eval on without monkeypatching the class like:

class Box; def eigenclass; class << self; self; end end end

Is there a beautiful (and reliable) way for me to do this?

David Moles
  • 48,006
  • 27
  • 136
  • 235
Lucas d. Prim
  • 787
  • 1
  • 5
  • 17

2 Answers2

8

All you need to add methods from modules to specific instances of Box dynamically is the Kernel#extend method:

box.extend MyModule

Also, because the verb "to inject" already has a meaning in Ruby from Enumerable#inject, the best verb for describing this is "to extend".

Aaa
  • 1,854
  • 12
  • 18
3

I can't follow your reasoning there. self.class.class_eval will work just fine in your example, like so:

class Box
  def after_initialize
    self.class.class_eval do
      include(self.injected_module)
    end
  end
end

Edit: clarifying the comments.

Use Object#extend to include methods in a Module as class methods (like defining them in the eigenclass would), like so:

module MyModule
    def method
        puts "called from #{self.inspect}"
    end
end

class Box
    def self.injected_module
        MyModule
    end

    def require_module
        self.class.class_eval do
            extend self.injected_module
        end
    end
end

b = Box.new
b.require_module
Box.method
# prints "called from Box"
fx_
  • 1,932
  • 13
  • 17
  • the problem with this solution is that it adds a methods into the class itself, not into the _eigenclass_. This way if i create another Box instance after executing this code, the instance will get the new method. I'm looking to include the methods as singleton methods to the instance i am creating! – Lucas d. Prim Mar 14 '11 at 02:42
  • I can't follow that reasoning either. You seem to have the concept of "eigenclass" confused. "eigenclass" is synonym for the class, methods called directly on the class, like `Box.method`. Here, however, you're including a Module into a class, defining instance methods. Can you try to clarify what exactly you want? – fx_ Mar 14 '11 at 03:12
  • My best guess is that you're returning a singleton instance and want methods from a Module added as class methods, so you can call `Box.method` instead of calling the method on the instance, like `Box.instance.method`. You should use [Object#extend](http://www.ruby-doc.org/core/classes/Object.html#M001002) then instead of include. – fx_ Mar 14 '11 at 03:18
  • As far as I know, eigenclass is not a synonym for the class [link](http://iruby.me/uploads/blog/2010/08/19/seven-rules.png) - let me put this straight: i have an instance of Box, lets call it _boxie_. I want to add methods to _boxie_ (instance, that can have methods of its own through its _eigenclass_), not to Box(class). The methods i want to add are inside a module _M_ but i can't include them into the instance without using `eval` because i can't include the "instance methods" directly into the eigenclass dynamically. – Lucas d. Prim Mar 14 '11 at 03:44
  • `eigenclass` is `singleton class` in ruby terminology. The `class << self` idiom makes self the context in which to define methods, so you're defining class methods rather than instance methods. Read [this question](http://stackoverflow.com/questions/2505067/class-self-idiom-in-ruby) for more detail. What you linked is the method lookup tree of ruby. You simply need to answer for yourself: do you want the methods in your Module to be instance methods (for boxie) or class methods (for Box) - use `include` for the first and `extend` for the latter. – fx_ Mar 14 '11 at 16:36
  • Ah, I see, Refactor up there actually boiled it down faster and more precise. Very good. – fx_ Mar 14 '11 at 16:44