Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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
like image 318
fylooi Avatar asked Apr 26 '15 08:04

fylooi


2 Answers

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
like image 135
kiddorails Avatar answered Oct 13 '22 21:10

kiddorails


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.

like image 33
Myst Avatar answered Oct 13 '22 21:10

Myst