vikas95prasad
vikas95prasad

Reputation: 1332

How to dynamically create methods with parameter in ruby?

How can I dynamically create methods like this using ruby metaprogramming ?

class CommentBridge < Bridge

  def id(comment)
    comment.id
  end

  def message(comment)
    comment.message
  end

  def votes_count(comment)
    comment.votes_count
  end

end

I tried this but it is not working.

  ['id', 'message', 'votes_count'].each do |method|
    define_method "#{method}" do |parameter|
      method(parameter.method)
    end
  end

Upvotes: 2

Views: 696

Answers (2)

Nick Roz
Nick Roz

Reputation: 4230

I do not think that you need different comment every time (probably you do). So I'd recommend to simply get rid of this comment argument. There are the options.

Using RubyOnRails (I see you question is tagged so) you can use delegate (as @SimpleLime has already commented)

class CommentBridge < Bridge
  attr_reader :comment
  def initialize(comment_)
    @comment = comment_)
  end
  delegate :id, :message, :votes_count, to: :comment
end

In case of pure Ruby 2 use Forwardable:

class CommentBridge
  extend Forwardable
  attr_reader :comment
  def initialize(comment_)
    @comment = comment_)
  end
  def_delegators :comment, :id, :message, :votes_count
end 

If you want to provide additional methods on top of you comment object and forward all the rest methods use SimpleDelegator (assuming that this Brigde in namgin means that your class is just a wrapper):

class CommentDecorator < SimpleDelegator
  def hello
    'hello'
  end
end

comment = Commend.find(params[:id])
decorated_comment = CommentDecorator.new(comment)

You can also define method missing:

class CommentBridge < Bridge
  attr_reader :comment
  def initialize(comment_)
    @comment = comment_)
  end

  def method_missing(m, *args)
    if [:id, :message, :comment].include?(m)
      comment.public_send(method, *args)
    else
      super
    end
  end
end

Finally, you can create your own delegation-DSL on top of define_method, but I think this is the extra in that case.

I don't think that method_missing or define_method inside loop is neat although it works.

Upvotes: 0

mrzasa
mrzasa

Reputation: 23307

You should use public_send to call methods based on their name:

  ['id', 'message', 'votes_count'].each do |method|
    define_method "#{method}" do |parameter|
      parameter.public_send(method)
    end
  end

Upvotes: 3

Related Questions