Using define_method to cleanup your code

In ruby you can define methods at runtime, to do that, you can use for example the define_method to create at runtime new instance methods.

Why would that be useful?

A classic use is to avoid code repetition. Imagine you have a class that is a basic mixin to help you to make external http requests.

module Request
  def get(url, payload: {}, headers: {})
    headers = define_headers(headers)
    execute(:get, url, payload, headers)
  end

  def post(url, payload: {}, headers: {})
    headers = define_headers(headers)
    execute(:post, url, payload, headers)
  end

  def put(url, payload: {}, headers: {})
    headers = define_headers(headers)
    execute(:put, url, payload, headers)
  end

  def delete(url, payload: {}, headers: {})
    headers = define_headers(headers)
    execute(:delete, url, payload, headers)
  end

  def define_headers(headers)
    default_headers = {
      'Authorization' => "Bearer token",
      'Accept' => 'application/json'
    }
    default_headers.merge(headers)
  end

  private

  def execute(method, url, payload, headers)
    RestClient::Request.execute(method: method, url: url, payload: payload, headers: headers)
  end
end

Can you see how much code are we repeating just because of one single parameter?

One way to avoid that is using define_method to create the methods at runtime. Take a look at the following code:

module Request
  [:get, :post, :put, :delete].each do |verb|
    define_method verb do |url, payload: {}, headers: {}|
      headers = define_headers(headers)
      execute(verb, url, payload, headers)
    end
  end

  def define_headers(headers)
    default_headers = {
      'Authorization' => "Bearer token",
      'Accept' => 'application/json'
    }
    default_headers.merge(headers)
  end

  private

  def execute(method, url, payload, headers)
    RestClient::Request.execute(method: method, url: url, payload: payload, headers: headers)
  end
end

As you can see, we just have to pass the method name as a parameter, and then, in the block definition you pass what will be your method’s parameters.