Posted about 5 years ago. Visible to the public.

Advanced Ruby: Metaprogramming and DSLs [3d]

Rubymonk training

Read the following Rubymonk articles:

For each chapter in each article:

  • Play with the introduced Ruby feature using a simple Ruby script or IRB console
  • Talk with your mentor what you learned.

Exercise: Roll your own DSL

This entire exercise should be implemented using pure Ruby, without any gems.

Write an Addressbook class that can be used like this:

book = book.add_contact 'Henning Koch' book.add_contact 'Tobias Kraze' book.contacts # => ['Henning Koch', 'Tobias Kraze']

Now change Addressbook so contacts can be defined with a custom DSL:

book = Addressbook.parse do contact 'Henning Koch' contact 'Tobias Kraze' end book.contacts # => ['Henning Koch', 'Tobias Kraze']

Now allow each contact to have a phone and email attribute:

book = Addressbook.parse do contact 'Henning Koch' do phone '12345' email '' end contact 'Tobias Kraze' do phone '67890' email '' end end book.find('Henning Koch') # => { :phone => '12345', :email => '' } book.find('Tobias Kraze') # => { :phone => '67890', :email => '' }

Now change Addressbook so contacts can be accessed by their underscored names:

book.henning_koch # => { :phone => '12345', :email => '' } book.tobias_kraze # => { :phone => '67890', :email => '' }

Now change Addressbook so each contact becomes their own Contact instance which responds to #phone and #email:

book.henning_koch # => Contact<#....> # => '12345' # => ''

Now allow arbitrary fields, not just phone and email:

book = Addressbook.parse do contact 'Henning Koch' do phone '12345' glasses true shirt 'red' end end book.henning_koch.shirt # => 'red'

Exercise: DSL styles

The DSL above could also be implemented using this syntax:

book = Addressbook.parse do |ab| 'Henning Koch' do |c| '12345' c.glasses true c.shirt 'red' end end book.henning_koch.shirt # => 'red'

Change your implementation to work like this.

What are the advantages of this style of DSL? What are the drawbacks? Which do you prefer?

You have probably encountered examples of both styles before. Name a few.

Excercise: Modularity

Consider the following example from the Modularity README:

# app/models/article.rb class Article < ActiveRecord::Base include DoesStripFields[:name, :brand] end # app/models/shared/does_strip_fields.rb module DoesStripFields as_trait do |*fields| fields.each do |field| define_method("#{field}=") do |value| self[field] = value.strip end end end end

Go through the Modularity source code and understand how the implementation works. In particular, understand this syntax:

include DoesStripFields[:name, :brand]

What exactly is included here? How does Modularity enable parameterized modules? How do the square brackets work?

Owner of this card:

Henning Koch
Last edit:
5 months ago
by Henning Koch
Posted by Henning Koch to makandra Curriculum
This website uses short-lived cookies to improve usability.
Accept or learn more