Search code examples
rubymethodsmetaprogrammingclass-eval

Creating methods with class_eval


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?


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