Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Metaprogramming And DSLs

762 views

Published on

Presentation given at Arrrrcamp.

Published in: Technology, Business
  • Be the first to comment

Metaprogramming And DSLs

  1. 1. metaprogramming + domain specific languages
  2. 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. 3. what is metaprogramming?
  4. 4. Metaprogramming is the writing of computer programs that write or manipulate other programs (or themselves) as their data.
  5. 5. class  Person    attr_reader  :friends end
  6. 6. class  Document  <  ActiveRecord::Base    has_many  :pages end
  7. 7. task  :test  do    puts  "Hello,  I  am  a  rake  task!" end
  8. 8. example (i)
  9. 9. class  Bob    my_attr_reader  :foo,  :bar end
  10. 10. class  Module    def  my_attr_reader(*fields)        fields.each  do  |field|            class_eval  "                def  #{field}                    @#{field}                end            "        end    end end
  11. 11. class  Module    def  my_attr_reader(*fields)        fields.each  do  |field|            class_eval  "                def  #{field}                    @#{field}                end            "        end    end end
  12. 12. def  #{field}    @#{field} end
  13. 13. def  foo    @foo end
  14. 14. def  bar    @bar end
  15. 15. class  Module    def  my_attr_reader(*fields)        fields.each  do  |field|            class_eval  "                def  #{field}                    @#{field}                end            "        end    end end
  16. 16. DEMO
  17. 17. example (ii)
  18. 18. class  Module    def  my_attr_reader(*fields)        fields.each  do  |field|            class_eval  "                def  #{field}                    @#{field}                end            "        end    end end
  19. 19. class  Module    def  battr_reader(*fields)        fields.each  do  |field|            class_eval  "                def  #{field}?                    !!@#{field}                end            "        end    end end
  20. 20. class  Bob    battr_reader  :foo,  :bar end p  bob.foo? p  bob.bar?
  21. 21. DEMO
  22. 22. example (iii)
  23. 23. <p>Hello.  My  name  is  <%=  @first_name  %>.</p>
  24. 24. template  =  "<p>Hello.  My  name  is  <%=    @first_name  %>.</p>" @first_name  =  "Bob" p  ERB.new(template).result
  25. 25. DEMO
  26. 26. example (iv)
  27. 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. 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. 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. 30. class  Product    def  initialize        @title  =  "My  First  Ruby  Book"        @id        =  "39t8zfeg"    end end
  31. 31. class  Product    def  initialize        @title  =  "My  First  Ruby  Book"        @id        =  "39t8zfeg"    end    def  get_binding        binding    end end
  32. 32. def  get_binding    binding end
  33. 33. def  get_binding    binding end
  34. 34. def  binding    binding end
  35. 35. def  binding    binding end SystemStackError:  stack  level  too  deep
  36. 36. template  =  "<p>Product  <%=    @id  %>  has  title  <%=  @title  %>.</p>" product  =  Product.new p  ERB.new(template).result
  37. 37. template  =  "<p>Product  <%=    @id  %>  has  title  <%=  @title  %>.</p>" product  =  Product.new p  ERB.new(template).result(product.get_binding)
  38. 38. DEMO
  39. 39. example (v)
  40. 40. compile  '/articles/*/'  do    filter  :erb    filter  :bluecloth    layout  'article'    filter  :rubypants end
  41. 41. my-­‐site/    config.yaml    Rules    content/    layouts/    lib/
  42. 42. … simpler process  /oo/  do  |item|    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
  43. 43. class  Item    attr_reader  :identifier    def  initialize(identifier)        @identifier  =  identifier    end end
  44. 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. 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. 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. 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. 48. class  Bob    def  initialize        @secret  =  "abc"    end end
  49. 49. bob  =  Bob.new p  bob.secret NoMethodError:  undefined  method  `secret'    for  #<Bob:0x574324>
  50. 50. bob  =  Bob.new p  bob.instance_eval  {  @secret  } abc
  51. 51. bob  =  Bob.new p  bob.instance_eval  "@secret" abc
  52. 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. 53.    ⋮    def  process(item)        rule  =  rules.find  do  |r|            r.applicable_to?(item)        end        rule.apply_to(item)    end end
  54. 54. class  DSL    def  initialize(rules)        @rules  =  rules    end    def  process(pattern,  &block)        @rules  <<  Rule.new(pattern,  block)    end end
  55. 55. class  DSL    def  initialize(rules)        @rules  =  rules    end    def  process(pattern,  &block)        @rules  <<  Rule.new(pattern,  block)    end end
  56. 56. process  /oo/  do  |item|    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
  57. 57. process  /oo/  do  |item|    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
  58. 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. 59. DEMO
  60. 60. example (vi)
  61. 61. process  /oo/  do  |item|    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
  62. 62. process  /oo/  do    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
  63. 63. class  RuleContext    def  initialize(item)        @item  =  item    end end
  64. 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. 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. 66. process  /oo/  do    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{@item.inspect}!" end
  67. 67. process  /oo/  do    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
  68. 68. class  RuleContext    def  initialize(item)        @item  =  item    end end
  69. 69. class  RuleContext    def  initialize(item)        @item  =  item    end    def  item        @item    end end
  70. 70. process  /oo/  do    puts  "I  am  rule  /oo/!"    puts  "I  am  processing  #{item.inspect}!" end
  71. 71. DEMO
  72. 72. ‣ 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/
  73. 73. you can haz questions?
  74. 74. k thx bai

×