Someth Victory

Ruby, Rails, and Javascript Developer

Reduce Code Duplication With Metaprogramming

Metaprogramming is a wonderful tool for producing DRY(Don’t Repeat Yourself) code in highly dynamic languages. It is commonly defined as “code that produces code”. Metaprogramming reduce the amount of unnecesssary code, make your code clean, and DRY, and easy to scale and maintain.

Let’s take a look at the example below:

Without Metaprogramming
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Domain < ActiveRecord::Base
  validates :status, presence: true, inclusion: { in: %w(pointed new subodmain) }

  def self.pointed
    where(status: 'pointed')
  end

  def self.new
    where(status: 'new')
  end

  def self.subdomain
    where(status: 'subdomain')
  end

  def pointed?
    status == 'pointed'
  end

  def new?
    status == 'new'
  end

  def subdomain?
    status == 'subdomain'
  end
end

The example above is obvious that this is a maintenance issue in the making because the code is not DRY, and will take more work in scaling.

Let’s say that we want to add another domain status 'transferred', so two methods would need to be created, self.transferred and transferred?.

Now let’s see how we could resolve this issue with Metaprogramming

See example below:

With Metaprogramming
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class Domain < ActiveRecord::Base
  STATUSES = %w( pointed new subdomain )
  validates :status, presence: true, inclusion: { in: STATUSES }

  # create self.pointed, self.new, self.subdomain
  class << self
    STATUSES.each do |status_name|
      define_method "#{status_name}" do
        where(status: status_name)
      end
    end
  end

  # create pointed?, new?, subdomain?
  STATUSES.each do |status_name|
    define_method "#{status_name}?" do
      status == status_name
    end
  end

end

Now the code has been refactorred to use metaprogramming, with define_method. No matter how many domain statuses need to be added, only STATUSES constant needs to be changed, and everything will work normally. The funtionally in Example 2 is working exactly the same as in Example 1. All the methods will be automatically created in runtime.

As a clue, when writing code with metaprogramming, some comments about what the code does should be written for a better understanding.