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.