Question

I am extending a class to accept a DSL. The instance should always respond with a default value, but the code may change. The code below works fine when I call who_am_i, which sets the i_am method, but I can't figure out how to set a default method.

module Helper
  def i_am
    "Default code"
  end
  def who_am_i
    class_eval(<<-EOS, __FILE__, __LINE__ + 1)
      def i_am
        "This was executed in class: \#{self.class.to_s}"
      end
  EOS
  end
end

class Test
  extend Helper
end

t = Test.new
t.i_am

I can make this work by doing this:

class Test2
  extend Helper
  include Helper
end

t2 = Test2.new
t2.i_am

but this seems weird. Thoughts?

Was it helpful?

Solution

You need to use included method/hook. It is being run every time given module is included into other class or module, with this class or module for a param. In your case you want:

module Helper
  def i_am
    "Default code"
  end

  def self.included mod
    mod.extend ClassMethods
  end

  module ClassMethods
    def who_am_i
      class_eval(<<-EOS, __FILE__, __LINE__ + 1)
        def i_am
          "This was executed in class: \#{self.class.to_s}"
        end
    EOS
    end
  end
end

class Tester
  include Helper
end

OTHER TIPS

Ah ha!

The answer is to use module.included() to cause it to extend the class. This way you can use "include Helper", and the "include" part will call "Helper.included" and that is passed the caller, and you use that to extend it!

reference: http://www.railstips.org/blog/archives/2009/05/15/include-vs-extend-in-ruby/

module Helper

  def i_am
    "Default code"
  end

  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def who_am_i
      class_eval(<<-EOS, __FILE__, __LINE__ + 1)
        def i_am
          "This was executed in class: \#{self.class.to_s}"
        end
  EOS
    end
  end
end


class Test
  include Helper
end
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top