Question

I want to write a method which takes one parameter and creates another method, named with this parameter. Here is my code

class Class
  def createMethod(attr_name)
    attr_name = attr_name.to_s
    class_eval %Q{
        def #{attr_name}
            puts "bar"
        end
    }
  end
end

p Class.new.createMethod("foo").respond_to?(:foo)

Unfortunately, respond_to?(:foo) evaluates to false. What's wrong?

Was it helpful?

Solution

This is because class_eval is a class method and you're calling it in the context of an instance. You can do this instead:

class Class
  def createMethod(attr_name)
    attr_name = attr_name.to_s
    self.class.class_eval %Q{
        def #{attr_name}
            puts "bar"
        end
    }
    self # Return yourself if you want to allow chaining methods
  end
end

Here's the output from irb when doing this:

irb(main):001:0> class Class
irb(main):002:1>   def createMethod(attr_name)
irb(main):003:2>     attr_name = attr_name.to_s
irb(main):004:2>     self.class.class_eval %Q{
irb(main):005:2"         def #{attr_name}
irb(main):006:2"             puts "bar"
irb(main):007:2"         end
irb(main):008:2"     }
irb(main):009:2>   end
irb(main):010:1> end
=> nil
irb(main):011:0> clazz = Class.new
=> #<Class:0x007fd86495cd58>
irb(main):012:0> clazz.respond_to?(:foo)
=> false
irb(main):013:0> clazz.createMethod("foo")
=> nil
irb(main):014:0> clazz.respond_to?(:foo)
=> true
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top