Blog #6

Improve code quality with define_method

DATPMT Jul 06 2023 Tag icon
#Ruby
#Rails

Let's imagine a situation - You need create multiple methods with similar purposes (same name and same logic) but doing so will have repetitions, so it will not be optimal, making the code file become more versbose.

def create_order_cod
  order = Order.not_draft.where(payment_method: :cod).last
  OrderMailer.create_order_email(order.id)
end
def create_order_transfer
  order = Order.not_draft.where(payment_method: :transfer).last
  OrderMailer.create_order_email(order.id)
end
def create_order_paypal
  order = Order.not_draft.where(payment_method: :paypal).last
  OrderMailer.create_order_email(order.id)
end

I had this problem when trying to create email preview methods like above.

# The Goal

Before we start to deal with this, let's state our wish first, which is to create a loop that generates methods by the names we give them.

# Starting

Ruby on Rails supports a method that can help us do just that, it's called define_method.

Just to be clear, we can do this:

define_method(:my_method) do |foo, bar| # or even |*args|
  # do something
end

This means same as:

def my_method(foo, bar)
  # do something
end

If you want to define method parameters that have default values, you need to get a bit more creative and do something like this:

define_method(:my_method) do |foo, bar|
  bar ||= {}
  # do something
end

So with my case, I edited as follows:

Order.payment_methods.each_key do |payment_method|
  define_method("create_order_#{payment_method}") do
    order = Order.not_draft.where(payment_method: payment_method).last
    OrderMailer.create_order_email(order.id)
  end
end

# Conclusion 

Although define_method helps us to solve the long-winded problem, it also comes with the disadvantage that when maintaining the code, it is difficult to find the methods that are generated by this method.
Use it carefully or it will become your own obsession :D