metaprogramming + domain specific languages
%  cat  ddfreyne.txt DENIS  DEFREYNE ============== web:          http://stoneship.org twitter:  ddfreyne nanoc:  a  static  ruby  web  site  publishing  system http://nanoc.stoneship.org/ %  _
what is metaprogramming?
Metaprogramming is the writing of computer programs that write or manipulate other programs (or themselves) as their data.
class  Person    attr_reader  :friends end
class  Document  <  ActiveRecord::Base    has_many  :pages end
task  :test  do    puts  "Hello,  I  am  a  rake  task!" end
example (i)
class  Bob    my_attr_reader  :foo,  :bar end
class  Module    def  my_attr_reader(*fields)        fields.each  do  |field|            class_eval  "                def  #{field}                    @#{field}                end            "        end    end end
class  Module    def  my_attr_reader(*fields)        fields.each  do  |field|            class_eval  "                def  #{field}                    @#{field}                end            "        end    end end
def  #{field}    @#{field} end
def  foo    @foo end
def  bar    @bar end
class  Module    def  my_attr_reader(*fields)        fields.each  do  |field|            class_eval  "                def  #{field}                    @#{field}                end            "        end    end end
DEMO
example (ii)
class  Module    def  my_attr_reader(*fields)        fields.each  do  |field|            class_eval  "                def  #{field}                    @#{field}                end            "        end    end end
class  Module    def  battr_reader(*fields)        fields.each  do  |field|            class_eval  "                def  #{field}?                    !!@#{field}                end            "        end    end end
class  Bob    battr_reader  :foo,  :bar end p  bob.foo? p  bob.bar?
DEMO
example (iii)
<p>Hello.  My  name  is  <%=  @first_name  %>.</p>
template  =  "<p>Hello.  My  name  is  <%=    @first_name  %>.</p>" @first_name  =  "Bob" p  ERB.new(template).result
DEMO
example (iv)
class  Product    def  initialize        @title  =  "My  First  Ruby  Book"        @id        =  "39t8zfeg"    end end template  =  "<p>Product  <%=    @id  %>  has  title  <%=  @title  %>.</p>" product  =  Product.new #  TODO  use  the  product  details   p  ERB.new(template).result
class  Product    def  initialize        @title  =  "My  First  Ruby  Book"        @id        =  "39t8zfeg"    end    def  print        template  =  "<p>Product  <%=            @id  %>  has  title  <%=  @title  %>.</p>"        puts  ERB.new(template).result(binding)    end end
class  Product    def  initialize        @title  =  "My  First  Ruby  Book"        @id        =  "39t8zfeg"    end    def  print        template  =  "<p>Product  <%=            @id  %>  has  title  <%=  @title  %>.</p>"        puts  ERB.new(template).result(binding)    end end
class  Product    def  initialize        @title  =  "My  First  Ruby  Book"        @id        =  "39t8zfeg"    end end
class  Product    def  initialize        @title  =  "My  First  Ruby  Book"        @id        =  "39t8zfeg"    end    def  get_binding        binding    end end
def  get_binding    binding end
def  get_binding    binding end
def  binding    binding end
def  binding    binding end SystemStackError:  stack  level  too  deep
template  =  "<p>Product  <%=    @id  %>  has  title  <%=  @title  %>.</p>" product  =  Product.new p  ERB.new(template).result
template  =  "<p>Product  <%=    @id  %>  has  title  <%=  @title  %>.</p>" product  =  Product.new p  ERB.new(template).result(product.get_binding)
DEMO
example (v)
compile  '/articles/*/'  do    filter  :erb    filter  :bluecloth    layout  'article'    filter  :rubypants end
my-­‐site/    config.yaml    Rules    content/    layouts/    lib/
… simpler process  /oo/  do  |item|    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
class  Item    attr_reader  :identifier    def  initialize(identifier)        @identifier  =  identifier    end end
items  =  [    Item.new('foo'),    Item.new('foobar'),    Item.new('quxbar'),    Item.new('moo') ] magically_load_rules items.each  do  |item|    magically_process(item) end
class  Rule    def  initialize(pattern,  block)        @pattern  =  pattern        @block      =  block    end    def  applicable_to?(item)        item.identifier  =~  @pattern    end    def  apply_to(item)        @block.call(item)    end end
class  Application    def  initialize        @rules  =  []    end    def  load_rules        rules_content  =  File.read('Rules')        dsl  =  DSL.new(@rules)        dsl.instance_eval(rules_content)    end    ⋮
class  Application    def  initialize        @rules  =  []    end    def  load_rules        rules_content  =  File.read('Rules')        dsl  =  DSL.new(@rules)        dsl.instance_eval(rules_content)    end    ⋮
class  Bob    def  initialize        @secret  =  "abc"    end end
bob  =  Bob.new p  bob.secret NoMethodError:  undefined  method  `secret'    for  #<Bob:0x574324>
bob  =  Bob.new p  bob.instance_eval  {  @secret  } abc
bob  =  Bob.new p  bob.instance_eval  "@secret" abc
class  Application    def  initialize        @rules  =  []    end    def  load_rules        rules_content  =  File.read('Rules')        dsl  =  DSL.new(@rules)        dsl.instance_eval(rules_content)    end    ⋮
   ⋮    def  process(item)        rule  =  rules.find  do  |r|            r.applicable_to?(item)        end        rule.apply_to(item)    end end
class  DSL    def  initialize(rules)        @rules  =  rules    end    def  process(pattern,  &block)        @rules  <<  Rule.new(pattern,  block)    end end
class  DSL    def  initialize(rules)        @rules  =  rules    end    def  process(pattern,  &block)        @rules  <<  Rule.new(pattern,  block)    end end
process  /oo/  do  |item|    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
process  /oo/  do  |item|    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
items  =  [    Item.new('foo'),    Item.new('foobar'),    Item.new('quxbar'),    Item.new('moo') ] app  =  App.new app.load_rules items.each  do  |item|    app.process(item) end
DEMO
example (vi)
process  /oo/  do  |item|    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
process  /oo/  do    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
class  RuleContext    def  initialize(item)        @item  =  item    end end
class  Rule    def  initialize(pattern,  block)        @pattern  =  pattern        @block      =  block    end    def  applicable_to?(item)        item.identifier  =~  @pattern    end    def  apply_to(item)        #  original  way:        @block.call(item)    end end
class  Rule    def  initialize(pattern,  block)        @pattern  =  pattern        @block      =  block    end    def  applicable_to?(item)        item.identifier  =~  @pattern    end    def  apply_to(item)        rule_context  =  RuleContext.new(item)        rule_context.instance_eval(&@block)    end end
process  /oo/  do    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{@item.inspect}!" end
process  /oo/  do    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
class  RuleContext    def  initialize(item)        @item  =  item    end end
class  RuleContext    def  initialize(item)        @item  =  item    end    def  item        @item    end end
process  /oo/  do    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
DEMO
‣ The Ruby Object Model and Metaprogramming http://www.pragprog.com/screencasts/v- dtrubyom/the-ruby-object-model-and- metaprogramming ‣ How nanocʼs Rules DSL Works http://stoneship.org/journal/2009/how- nanocs-rules-dsl-works/
you can haz questions?
k thx bai

Metaprogramming + Ds Ls

  • 1.
    metaprogramming + domain specific languages
  • 2.
    %  cat  ddfreyne.txt DENIS DEFREYNE ============== web:          http://stoneship.org twitter:  ddfreyne nanoc:  a  static  ruby  web  site  publishing  system http://nanoc.stoneship.org/ %  _
  • 3.
  • 4.
    Metaprogramming is the writing of computer programs that write or manipulate other programs (or themselves) as their data.
  • 5.
    class  Person   attr_reader  :friends end
  • 6.
    class  Document  < ActiveRecord::Base    has_many  :pages end
  • 7.
    task  :test  do    puts  "Hello,  I  am  a  rake  task!" end
  • 8.
  • 9.
    class  Bob   my_attr_reader  :foo,  :bar end
  • 10.
    class  Module   def  my_attr_reader(*fields)        fields.each  do  |field|            class_eval  "                def  #{field}                    @#{field}                end            "        end    end end
  • 11.
    class  Module   def  my_attr_reader(*fields)        fields.each  do  |field|            class_eval  "                def  #{field}                    @#{field}                end            "        end    end end
  • 12.
    def  #{field}   @#{field} end
  • 13.
    def  foo   @foo end
  • 14.
    def  bar   @bar end
  • 15.
    class  Module   def  my_attr_reader(*fields)        fields.each  do  |field|            class_eval  "                def  #{field}                    @#{field}                end            "        end    end end
  • 16.
  • 17.
  • 18.
    class  Module   def  my_attr_reader(*fields)        fields.each  do  |field|            class_eval  "                def  #{field}                    @#{field}                end            "        end    end end
  • 19.
    class  Module   def  battr_reader(*fields)        fields.each  do  |field|            class_eval  "                def  #{field}?                    !!@#{field}                end            "        end    end end
  • 20.
    class  Bob   battr_reader  :foo,  :bar end p  bob.foo? p  bob.bar?
  • 21.
  • 22.
  • 23.
    <p>Hello.  My  name is  <%=  @first_name  %>.</p>
  • 24.
    template  =  "<p>Hello. My  name  is  <%=    @first_name  %>.</p>" @first_name  =  "Bob" p  ERB.new(template).result
  • 25.
  • 26.
  • 27.
    class  Product   def  initialize        @title  =  "My  First  Ruby  Book"        @id        =  "39t8zfeg"    end end template  =  "<p>Product  <%=    @id  %>  has  title  <%=  @title  %>.</p>" product  =  Product.new #  TODO  use  the  product  details   p  ERB.new(template).result
  • 28.
    class  Product   def  initialize        @title  =  "My  First  Ruby  Book"        @id        =  "39t8zfeg"    end    def  print        template  =  "<p>Product  <%=            @id  %>  has  title  <%=  @title  %>.</p>"        puts  ERB.new(template).result(binding)    end end
  • 29.
    class  Product   def  initialize        @title  =  "My  First  Ruby  Book"        @id        =  "39t8zfeg"    end    def  print        template  =  "<p>Product  <%=            @id  %>  has  title  <%=  @title  %>.</p>"        puts  ERB.new(template).result(binding)    end end
  • 30.
    class  Product   def  initialize        @title  =  "My  First  Ruby  Book"        @id        =  "39t8zfeg"    end end
  • 31.
    class  Product   def  initialize        @title  =  "My  First  Ruby  Book"        @id        =  "39t8zfeg"    end    def  get_binding        binding    end end
  • 32.
    def  get_binding   binding end
  • 33.
    def  get_binding   binding end
  • 34.
    def  binding   binding end
  • 35.
    def  binding   binding end SystemStackError:  stack  level  too  deep
  • 36.
    template  =  "<p>Product <%=    @id  %>  has  title  <%=  @title  %>.</p>" product  =  Product.new p  ERB.new(template).result
  • 37.
    template  =  "<p>Product <%=    @id  %>  has  title  <%=  @title  %>.</p>" product  =  Product.new p  ERB.new(template).result(product.get_binding)
  • 38.
  • 39.
  • 40.
    compile  '/articles/*/'  do    filter  :erb    filter  :bluecloth    layout  'article'    filter  :rubypants end
  • 41.
    my-­‐site/    config.yaml    Rules    content/    layouts/    lib/
  • 42.
    … simpler process  /oo/ do  |item|    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
  • 43.
    class  Item   attr_reader  :identifier    def  initialize(identifier)        @identifier  =  identifier    end end
  • 44.
    items  =  [    Item.new('foo'),    Item.new('foobar'),    Item.new('quxbar'),    Item.new('moo') ] magically_load_rules items.each  do  |item|    magically_process(item) end
  • 45.
    class  Rule   def  initialize(pattern,  block)        @pattern  =  pattern        @block      =  block    end    def  applicable_to?(item)        item.identifier  =~  @pattern    end    def  apply_to(item)        @block.call(item)    end end
  • 46.
    class  Application   def  initialize        @rules  =  []    end    def  load_rules        rules_content  =  File.read('Rules')        dsl  =  DSL.new(@rules)        dsl.instance_eval(rules_content)    end    ⋮
  • 47.
    class  Application   def  initialize        @rules  =  []    end    def  load_rules        rules_content  =  File.read('Rules')        dsl  =  DSL.new(@rules)        dsl.instance_eval(rules_content)    end    ⋮
  • 48.
    class  Bob   def  initialize        @secret  =  "abc"    end end
  • 49.
    bob  =  Bob.new p bob.secret NoMethodError:  undefined  method  `secret'    for  #<Bob:0x574324>
  • 50.
    bob  =  Bob.new p bob.instance_eval  {  @secret  } abc
  • 51.
    bob  =  Bob.new p bob.instance_eval  "@secret" abc
  • 52.
    class  Application   def  initialize        @rules  =  []    end    def  load_rules        rules_content  =  File.read('Rules')        dsl  =  DSL.new(@rules)        dsl.instance_eval(rules_content)    end    ⋮
  • 53.
       ⋮   def  process(item)        rule  =  rules.find  do  |r|            r.applicable_to?(item)        end        rule.apply_to(item)    end end
  • 54.
    class  DSL   def  initialize(rules)        @rules  =  rules    end    def  process(pattern,  &block)        @rules  <<  Rule.new(pattern,  block)    end end
  • 55.
    class  DSL   def  initialize(rules)        @rules  =  rules    end    def  process(pattern,  &block)        @rules  <<  Rule.new(pattern,  block)    end end
  • 56.
    process  /oo/  do |item|    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
  • 57.
    process  /oo/  do |item|    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
  • 58.
    items  =  [    Item.new('foo'),    Item.new('foobar'),    Item.new('quxbar'),    Item.new('moo') ] app  =  App.new app.load_rules items.each  do  |item|    app.process(item) end
  • 59.
  • 60.
  • 61.
    process  /oo/  do |item|    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
  • 62.
    process  /oo/  do    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
  • 63.
    class  RuleContext   def  initialize(item)        @item  =  item    end end
  • 64.
    class  Rule   def  initialize(pattern,  block)        @pattern  =  pattern        @block      =  block    end    def  applicable_to?(item)        item.identifier  =~  @pattern    end    def  apply_to(item)        #  original  way:        @block.call(item)    end end
  • 65.
    class  Rule   def  initialize(pattern,  block)        @pattern  =  pattern        @block      =  block    end    def  applicable_to?(item)        item.identifier  =~  @pattern    end    def  apply_to(item)        rule_context  =  RuleContext.new(item)        rule_context.instance_eval(&@block)    end end
  • 66.
    process  /oo/  do    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{@item.inspect}!" end
  • 67.
    process  /oo/  do    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
  • 68.
    class  RuleContext   def  initialize(item)        @item  =  item    end end
  • 69.
    class  RuleContext   def  initialize(item)        @item  =  item    end    def  item        @item    end end
  • 70.
    process  /oo/  do    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
  • 71.
  • 72.
    ‣ The RubyObject Model and Metaprogramming http://www.pragprog.com/screencasts/v- dtrubyom/the-ruby-object-model-and- metaprogramming ‣ How nanocʼs Rules DSL Works http://stoneship.org/journal/2009/how- nanocs-rules-dsl-works/
  • 73.
  • 74.