DSLs in Ruby
From WhyNotWiki
DSLs in Ruby edit (Category edit)
[edit] acts_as_state_machine
Bruce Tate (2007-03-13). Crossing borders: Extensions in Rails: The anatomy of an acts_as plug-in (http://www-128.ibm.com/developerworks/java/library/j-cb03137/index.html).
Like many Rails plug-ins, acts_as_state_machine uses a combination of Ruby capabilities and exclusive Rails features to provide not just a library, but also a domain-specific language (DSL), providing a nice experience for users.
...
Using the plug-in, I can decorate my class object directly, using a DSL to represent the various states, transitions between them, and the events that fire those transitions. [...]
class Nonprofit < ActiveRecord::Base acts_as_state_machine :initial => :created, :column => 'status' # These are all of the states for the existing system. state :submitted state :processing state :nonprofit_reviewing state :accepted event :accept do transitions :from => :processing, :to => :accepted transitions :from => :nonprofit_reviewing, :to => :accepted end event :receive do transitions :from => :submitted, :to => :processing end event :send_for_review do transitions :from => :processing, :to => :nonprofit_reviewing transitions :from => :nonprofit_reviewing, :to => :processing transitions :from => :accepted, :to => :nonprofit_reviewing endEach transition is a method invocation followed by a hash table. In Ruby, you represent a hash map as key => value pairs, separated by commas, and {enclosed in braces}. When you use a hash map as a function call's last parameter, the braces are optional. You can see that the methods -- state, transition, and event -- combined with closures and hash maps, make a nice DSL.
[edit] Builder templates
http://api.rubyonrails.org/classes/ActionView/Base.html
xml.rss("version" => "2.0", "xmlns:dc" => "http://purl.org/dc/elements/1.1/") do
xml.channel do
xml.title(@feed_title)
xml.link(@url)
xml.description "Basecamp: Recent items"
xml.language "en-us"
xml.ttl "40"
for item in @recent_items
xml.item do
xml.title(item_title(item))
xml.description(item_description(item)) if item_description(item)
xml.pubDate(item_pubDate(item))
xml.guid(@person.firm.account.url + @recent_items.url(item))
xml.link(@person.firm.account.url + @recent_items.url(item))
xml.tag!("dc:creator", item.author_name) if item_has_creator?(item)
end
end
end
end
XML Comments are supported …
xml_markup.comment! "This is a comment" #=> <!-- This is a comment -->XML processing instructions are supported …
xml_markup.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8" #=> <?xml version="1.0" encoding="UTF-8"?>Nested entity declarations are allowed. For example:
@xml_markup.declare! :DOCTYPE, :chapter do |x| x.declare! :ELEMENT, :chapter, :"(title,para+)" x.declare! :ELEMENT, :title, :"(#PCDATA)" x.declare! :ELEMENT, :para, :"(#PCDATA)" end #=> <!DOCTYPE chapter [ <!ELEMENT chapter (title,para+)> <!ELEMENT title (#PCDATA)> <!ELEMENT para (#PCDATA)> ]>
