Skip to content

Instantly share code, notes, and snippets.

@adamcooke
Created November 7, 2014 15:20
Show Gist options
  • Save adamcooke/4ea4bf64f804c2e714c0 to your computer and use it in GitHub Desktop.
Save adamcooke/4ea4bf64f804c2e714c0 to your computer and use it in GitHub Desktop.
The complete code from my DSL tutorial at http://blog.atechmedia.com/ruby-dsls-for-fun/
class AddressBook
def contacts
@contacts ||= []
end
def self.load_from_path(path)
# Create a new AddressBook instance
address_book = self.new
# Create a new AddressBookDSL instance
address_book_dsl = AddressBookDSL.new(address_book)
# Loop through every file in the given path
Dir[File.join(path, '*')].each do |file|
# Read each file and eval the contents in the address
# book's DSL instance.
address_book_dsl.instance_eval(File.read(file), file)
end
# Return the finished address book
address_book
end
end
class Contact
# Define the basic information attributes
attr_accessor :first_name
attr_accessor :last_name
attr_accessor :company
# Define a method to return an array of contact methods for
# this contact.
def contact_methods
@contact_methods ||= []
end
# Define a method to return an array of activities for the
# this contact.
def activities
@activities ||= []
end
end
class ContactMethod
# When we create a new contact method, we'll store the contact
# which it is associated with too.
def initialize(contact)
@contact = contact
end
# Accessor for returning a contact associated with a ContactMethod
attr_reader :contact
# Accessors for getting & setting the various properties which
# are associated with a ContactMethod.
attr_accessor :type
attr_accessor :role
attr_accessor :value
end
class AddressBookDSL
def initialize(address_book)
@address_book = address_book
end
def contact(&block)
# Create a new Contact object instance for our new contact
contact = Contact.new
# Create a new instance of our Contact DSL (defined in a moment)
# and pass it our newly instantiated contact.
contact_dsl = ContactDSL.new(contact)
# Eval the contacts of the `contact` block within the ContactDSL
# instance we just created.
contact_dsl.instance_eval(&block)
# Add the contact to the array of contacts in our address book
@address_book.contacts << contact
end
end
class ContactDSL
def initialize(contact)
@contact = contact
end
# When the name method is called, we'll accept the first and
# last names and set them as appropriate on our contact class.
def name(first_name, last_name)
@contact.first_name = first_name
@contact.last_name = last_name
end
# When the company is set, we'll just do the same and set that
# on the contact.
def company(name)
@contact.company = name
end
# Activities are just passed straight through from the contact
# itself so we just return the contact's activities array.
def activities
@contact.activities
end
# When a contact method is defined, we'll need to create a new
# instance of our ContactMethod class, set the values and then
# add it to our contact's contact_method array.
def contact_method(type, role, value)
method = ContactMethod.new(@contact)
method.type = type
method.role = role
method.value = value
@contact.contact_methods << method
end
def method_missing(name, *values)
if values.empty?
# If no values are provided, we'll just let Ruby raise an error
# as normal.
super
else
# Otherwise, we'll simply call our contact_method method to
# add our method.
contact_method(name, *values)
end
end
end
address_book = AddressBook.new
address_book_dsl = AddressBookDSL.new(address_book)
address_book_dsl.instance_eval do
contact do
name 'Joe', 'Bloggs'
company 'Bloggs Inc'
phone :home, '01234 123123'
phone :work, '01555 123555'
email :home, "[email protected]"
activities << 'Piano Playing'
activities << 'Swag Catching'
end
contact do
name 'Potato', 'Man'
company 'McCain'
phone :mobile, '07123 123123'
end
end
address_book.contacts.each do |contact|
puts "------------------------------------------------------------------------"
puts "#{contact.first_name} #{contact.last_name}"
puts contact.company
contact.contact_methods.each do |method|
puts " * #{method.type}:#{method.role} #{method.value}"
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment