In ruby, we have the power to create code that generates code. This can be useful in some situations (let’s say we have a hash response from an API and we want to create a method for each key dinamycally).

# really long response with data for a person

{ address: "06482 Wallingford CT", age: 12, nacionality: "german", gender: "male",
  religion: "Buddhism", city: "UIO" }

Since the code for fetching an specific value is the same for all values: fetch_from_response(key), we can use define_method for each key we know

class Person
  KEYS = %i(address, age, nacionality, gender, religion, city)

  def initialize(id, request)
    @id = id
    @response = request
  end

  KEYS.each do |key|
    define_method(key) { fetch_from_response key }
  end

  private

  def fetch_from_response(name)
    @request.response.fetch(name)
  end
end

This will create new methods for Person, depending on what keys we have

request = Request.new api_key: 'any key'

any_person = Person.new("1234", request)
any_person.methods - Object.methods
# => [:address, :age, :nacionality, :gender, :religion, :city]

any_person.address # => "06482 Wallingford CT"

Ruby also provides this and other usefull methods for defining object/instance and class methods

Object Methods

As we saw early, we can use define_method for creating new object methods, but since it is a private method it can only be used inside a class definition. If we want to define it outside we can use class_eval

Person.class_eval do
  def name
    # more code
  end
end

# defining multiple methods

Person.class_eval do
  KEYS.each do |key|
    define_method(key) do
      # code...
    end
  end
end

Inside the block the code is evaluated as if we were inside the class

Instance Methods

Defining a method over an instance creates a method for that specific ‘object’

# we're using the same instance we created before
any_person.is_a? Person # => True

def any_person.name
  # code code code!!!
end

The same can be achieved opening it’s eigennclass

class << any_person
  def name
    # coooode....
  end
end

This method lives as long as that instance exists

Class Methods

Similar to instance methods we can define a new one over the class or with an eigennclass

def Person.name
  # code code code!!!
end

# or

class << Person
  def name
    # coooode....
  end
end

Last but not least, I should remind you that defining methods with metaprogramming can be slow, even DANGEROUS!!!!!!, so use it wisely, remember :

With great power comes great responsability

Comments