fylooi
fylooi

Reputation: 3870

Ruby Metaprogramming: Dynamically defining method based on child instance attribute

I'm creating a presenter base class which is supposed to wrap an ActiveRecord object.

class BasePresenter
  def initialize object
    @object = object
  end

  def method_missing(*args, &block)
    @object.send(*args, &block)
  end

  def self.wrap collection
    collection.map { |item| new item }
  end
end

For each child class, I would like to be able to dynamically define a method based on a child attribute at initialization, so a ListPresenter like:

class ListPresenter < BasePresenter
end

should respond to list_id with the wrapped List object's id.

How would I do that short of defining it on every child class? I've tried the following in def initialize(object), both of which do not work. Would prefer to avoid eval based approaches if possible as I hear it's a code smell.

Class approach (adds the method to BasePresenter, not the child classes)

self.class.send(:define_method, "#{object.class.name.underscore}_id") do
  @object.id
end

Metaclass approach (unable to access instance variables object or @object):

class << self
  define_method "#{@object.class.name.underscore}_id" do
    @object.id
  end
end

Upvotes: 5

Views: 1268

Answers (2)

kiddorails
kiddorails

Reputation: 13014

Use Class#inherited hook to dynamically add a method whenever sub-class inherits BasePresenter.

class BasePresenter
  def self.inherited(sub_klass)
    sub_klass.send(:define_method, "#{sub_klass.name.underscore.split("_")[0...-1].join("_")}_id") do
      instance_variable_get("@object").id
    end
  end
end


class ListPresenter < BasePresenter
end

# Sending a rails model object below
l = ListPresenter.new(User.first)
l.list_id #=> 1

Upvotes: 4

Myst
Myst

Reputation: 19221

I recommend you have a look at Class#inherited.

You can define this method in your base class and it irate over the inheriting classes' methods...

I'll probably edit this once I'm sitting at my computer, but it's quite straightforward.

Upvotes: 2

Related Questions