Skip to content

Instantly share code, notes, and snippets.

@clicube
Created September 25, 2012 13:20
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save clicube/3781776 to your computer and use it in GitHub Desktop.
Save clicube/3781776 to your computer and use it in GitHub Desktop.
DSL generator for ruby
class DSLObject < BasicObject
include ::Kernel
end
class DSLContext
attr_reader :params
def initialize &block
@params = {}
@obj = DSLObject.new
@obj_class = class << @obj; self end;
instance_eval &block if block
end
def define_method name, &block
@obj_class.class_eval do
define_method name, &block
end
end
def define_param name
param_proc = proc do |val|
if !val || val.length == 0
@params[name]
elsif val.length == 1
@params[name] = val[0]
else
@params[name] = val
end
end
@obj_class.class_eval do
define_method name do |*args|
param_proc.call args
end
end
end
def define_context name, &block
param_proc = proc do |args, &block2|
c = DSLContext.new
class << c
def define_pre_method &block
@pre_method_proc = block
end
def pre_method
@pre_method_proc.call @params if @pre_method_proc
end
def define_post_method &block
@post_method_proc = block
end
def post_method
@post_method_proc.call @params if @post_method_proc
end
end
c.instance_eval &block
c.pre_method
c.eval &block2
c.post_method
@params[name] ||= []
if !args || args.length == 0
@params[name] << c.params
elsif args.length == 1
@params[name] << [args[0], c.params]
else
@params[name] << [args, c.params]
end
end
@obj_class.class_eval do
define_method name do |*args, &block|
param_proc.call *args, &block
end
end
end
def eval *args, &block
if block
@obj.instance_eval &block
else
@obj.instance_eval args[0]
end
end
end
if __FILE__ == $0
dsl = DSLContext.new do
define_param "simple_param"
define_context "multi_param" do
define_param "mp_1"
define_param "mp_2"
define_post_method do |params|
puts "(post_method) params: #{params}"
end
end
define_method "say" do |val|
puts "#{val}!! "*10
end
end
str = <<-EOS
simple_param "nyan"
multi_param {
mp_1 "nyan1"
mp_2 "nyan2"
}
multi_param {
mp_1 "wan1"
mp_2 "wan2"
}
say "nyan"
EOS
dsl.eval str
puts "========"
puts "params:"
p dsl.params
end
require 'stringio'
require_relative 'dsl'
if __FILE__ == $0
html = StringIO.new
dsl = DSLContext.new do
define_context "html" do
define_pre_method { html.puts "<html>" }
define_post_method { html.puts "</html>" }
define_context "head" do
define_param "charset"
define_param "title"
define_context "css" do
define_param "href"
define_param "media"
end
define_context "script" do
define_param "src"
end
define_post_method do |param|
html.puts "<head>"
html.puts "<meta charset=\"#{param['charset']}\" />"
html.puts "<title>#{param['title']}</title>"
param['css'].each do |css|
html.puts "<link href=\"#{css['href']}\" rel=\"stylesheet\" media=\"#{css['media']}\" />"
end
param['script'].each do |script|
html.puts "<script type=\"text/javascript\" src=\"#{script['src']}\"></script>"
end
html.puts "</head>"
end
end
define_context "body" do
define_pre_method { html.puts "<body>" }
define_post_method { html.puts "</body>" }
%w[h1 h2 h3 h4 h5 p a pre div].each do |tag|
define_method tag do |*args, &block|
if args.length>0
attr = ""
if args.length > 1
args[1].each do |key,val|
attr << " #{key}=\"#{val}\""
end
end
html.puts "<#{tag}#{attr}>#{args[0]}</#{tag}>"
elsif block
html.puts "<#{tag}>"
block.call
html.puts "</#{tag}>"
end
end
end
end
end
define_method "output" do
puts html.string
end
end
dsl_str = <<-EOS
html{
head{
charset "utf-8"
title "page title"
css{
href "screen.css"
media "screen"
}
css{
href "print.css"
media "print"
}
script{
src "jquery.js"
}
script{
src "some_script.js"
}
}
body{
h1 "h1 title"
div{
p "nyan nyan nyan"
p "wan wan wan"
}
a "hyper link text", "href"=>"http://nyan.cat/"
}
}
output
EOS
dsl.eval dsl_str
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment