Dynamic method definition with ruby’s .define_method
By: Camille Feghali
Tags: Flatiron School, Software Development, Ruby, Full Stack, Activerecord

It’s been two weeks since the start of class here at the Flatiron school’s software engineering immersive, and we’ve already started using Ruby’s very powerful ActiveRecord module that allows developers to map object relations to databases using very little code.

While working on a mini-project to get accustomed to the gem, I set up my tables, migrations, and relationships like so:

class Customer < ActiveRecord::Base

​

has_many :reviews

has_many :restaurants, through: :reviews



end

​

class Restaurant < ActiveRecord::Base ​ has_many :reviews

has_many :customers, through: :reviews

​

end

​

class Review < ActiveRecord::Base

​

belongs_to :restaurant

belongs_to :customer



end

I ran a .methods on my Customer model to know what new methods it inherited from ActiveRecord. I realized something very peculiar.

Notice the following methods.

:after_remove_for_reviews?,

:after_remove_for_reviews=,

:before_add_for_restaurants,

:before_add_for_restaurants?,

:before_add_for_restaurants=,

ActiveRecord defined custom methods according to the relationships established between my models. That means that if my models were named Squirrel and Acorn , these methods would be named after_remove_for_acorns? and before_add_for_squirrels etc - We will not talk about what these methods do in this blogpost - but that means that in ruby, there is a way to define methods dynamically! and that is something that I find very exciting.

In comes the .define_method method.

The define_method method allows us to do just that, define a method — but how is it different from the traditional way of placing your logic between a def and end block?

Let’s see how it works with a simple example. Say we have the following Mouse class:

class Mouse

​

define_method("eat_cheese") do |argument|

"The mouse is eating #{argument} cheese"

end



define_method("eat_wires") do |argument|

"The mouse is eating #{argument} wires"

end

​

define_method("eat_nuts") do |argument|

"The mouse is eating #{argument} nuts"

end

end

​

jerry = Mouse.new

puts jerry.eat_cheese("cheddar")

puts jerry.eat_wires("electrical")

puts jerry.eat_nuts("hazel")

​

#==> The mouse is eating cheddar cheese

#==> The mouse is eating electrical wires

#==> The mouse is eating hazel nuts

Now this is all good and dandy, but, really we could’ve just defined the methods the traditional way with def and end statements right?

The true power of define_method is unlocked when used to handle the creation of methods based on a particular naming. Take the following example:

class Mouse

["cheese", "wires", "nuts"].each do |food|

define_method("eat_#{food}") do |argument|

"The mouse is eating #{argument} #{food}"

end

end

end

​

jerry = Mouse.new

puts jerry.eat_cheese("cheddar")

puts jerry.eat_wires("electrical")

puts jerry.eat_nuts("hazel")

​

#==> The mouse is eating cheddar cheese

#==> The mouse is eating electrical wires

#==> The mouse is eating hazel nuts

Not only did we do the same job with much fewer lines of code, we could change the array elements, and our methods would change names — And that’s awesome because it allows developers to write dynamic code that can adapt to different situations.

Reference:

http://rubymonk.com/learning/books/2-metaprogramming-ruby/chapters/25-dynamic-methods/lessons/72-define-method