Garrett Hall
Garrett Hall

Reputation: 30022

How do you dynamically create a class with a given method and method body in Ruby?

In Ruby how would you define a method

def make_class(method_name, method_body, s_value)

returning a class with the following implementation

class Anonymous
    def method_name(args)
        method_body(args)
    end

    def to_s
       return s_value
    end
end

If you can link to any resources you found useful on basic Ruby metaprogramming that would be great as well.

Upvotes: 2

Views: 413

Answers (4)

Michael Kohl
Michael Kohl

Reputation: 66837

Easiest way I can think of:

def make_class(method_name, method_body, s_value)
  klass = Class.new
  klass.class_eval "def #{method_name} ; #{method_body} ; end"
  klass.class_eval "def to_s ; #{s_value} ; end"
  klass
end

Usage:

>> Anonymous = make_class(:foobar, "puts 'foo'", 23)
=> Anonymous
>> a = Anonymous.new
=> 23
>> a.foobar
foo
=> nil
>> a.to_s
=> 23

Edit: ok, I was a bit too simplistic here, this doesn't handle the args for the method.

Upvotes: 1

vimsha
vimsha

Reputation: 77

def make_class(method_name, method_body, s_value)
  Class.new {
    define_method method_name do |*args|
      eval(method_body)
    end

    define_method :to_s do
      s_value
    end
  }
end

"Metaprogramming Ruby: Program Like the Ruby Pros" is a very good book to learn about ruby metaprogramming.

Upvotes: 2

KL-7
KL-7

Reputation: 47618

You can use smth like that:

def make_class(s_value, method_name, &method_body)    
  Class.new do   
    define_method method_name, method_body    

    define_method :to_s do    
      s_value    
    end    
  end
end    

klass = make_class 'foo instance', :foo do |*args|    
  "called foo with #{args.inspect}"    
end    

k = klass.new     
puts k.to_s                 # => foo instance
puts k.foo [1, 2], 'hello'  # => called foo with [[1, 2], "hello"]

In that case you are supposed to pass your method's body as a block (you can replace |*args| with any list of arguments you want to have as parameters for you method). If you want to pass method_body not as a block but as a string then eval is your friend.

Upvotes: 5

phoet
phoet

Reputation: 18835

i would not recommend doing this, but you could...

def make_class(method_name, method_body, s_value)
  eval("
  class Anonymous
      def #{method_name}(args)
          #{method_body}(args)
      end

      def to_s
         return '#{s_value}'
      end
  end
  ")
end

make_class(:bla, :puts, 'bla')
Anonymous.new.bla('moin')
puts Anonymous.new.to_s

returning

moin
bla

Upvotes: 0

Related Questions