|
Summary
I work on many projects that require a rather detailed control flow description. For every project, this used to make me pause and consider how to get all this detailed configuration data into the application. Now, Ruby as a DSL is near the top of the list of possibilities, and usually solves the problem quickly and efficiently.
When I was doing Ruby training, I would take the class through a problem solving technique where we would describe the problem in plain English, then in pseudo code, and then in Ruby. But, in some cases, the pseudo code would be valid Ruby code. I think that the high readability quotient of Ruby makes it an ideal language for use as a DSL. And as Ruby becomes known by more people, DSLs written in Ruby will be a favorable way of communicating with an application.
Code listing for project ProjectBuilder DSL:
% cat project_builder.rb
require 'fileutils'
class ProjectBuilder
PROJECT_TEMPLATE_DSL = "project_template.dsl"
attr_reader :name
TEMPLATES = {
:exe =>
<<-EOT
#!/usr/bin/env ruby
require 'rubygems'
require 'commandline
require '%name%'
class %name.capitalize%App < CommandLine::Application
def initialize
end
def main
end
end#class %name.capitalize%App
EOT
}
def initialize(name)
@name = name
@top_level_dir = Dir.pwd
@project_dir = File.join(@top_level_dir, @name)
FileUtils.mkdir_p(@project_dir)
@cwd = @project_dir
end
def create_project
yield
end
def self.load(project_name, dsl=PROJECT_TEMPLATE_DSL)
proj = new(project_name)
proj = proj.instance_eval(File.read(dsl), dsl)
proj
end
def dir(dir_name)
old_cwd = @cwd
@cwd = File.join(@cwd, dir_name)
FileUtils.mkdir_p(@cwd)
yield self if block_given?
ensure
@cwd = old_cwd
end
def touch(*file_names)
file_names.flatten.each { |file|
FileUtils.touch(File.join(@cwd, "#{file}"))
}
end
def create_rb_file(file_names)
file_names.each { |file| touch(file + ".rb") }
end
def create_from_template(template_id, filename)
File.open(File.join(@cwd, filename), "w+") { |f|
str = TEMPLATES[template_id]
str.gsub!(/%[^%]+%/) { |m| instance_eval m[1..-2] }
f.puts str
}
end
end#class ProjectBuilder
# Execute as:
# ruby create-project.rb project_name |
|