• Like

Loading…

Flash Player 9 (or above) is needed to view presentations.
We have detected that you do not have it on your computer. To install it, go here.

Unblocked

  • 5,355 views
Uploaded on

My presentation for RubyConf X. It's a fun romp through blocks and the things we shouldn't do with them.

My presentation for RubyConf X. It's a fun romp through blocks and the things we shouldn't do with them.

More in: Technology
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Be the first to comment
No Downloads

Views

Total Views
5,355
On Slideshare
0
From Embeds
0
Number of Embeds
0

Actions

Shares
Downloads
71
Comments
0
Likes
7

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide






















































































































































































































Transcript

  • 1. unblocked { # ... } Ruby as a child’s plaything
  • 2. James Edward Gray II
  • 3. James Edward Gray II I’ve been in the Ruby community for a long time (before Rails)
  • 4. James Edward Gray II I’ve been in the Ruby community for a long time (before Rails) I’ve worked on: FasterCSV, HighLine, documentation, and a lot more
  • 5. James Edward Gray II I’ve been in the Ruby community for a long time (before Rails) I’ve worked on: FasterCSV, HighLine, documentation, and a lot more I build Rails applications for a living Like Go vs Go with Ryan Bates
  • 6. James Edward Gray II I’ve been in the Ruby community for a long time (before Rails) I’ve worked on: FasterCSV, HighLine, documentation, and a lot more I build Rails applications for a living Like Go vs Go with Ryan Bates I sometimes impersonate Jim Weirich
  • 7. I Used to be Cool
  • 8. I Used to be Cool
  • 9. I Used to be Cool
  • 10. I Used to be Cool
  • 11. I Used to be Cool
  • 12. But now I’m a Dad
  • 13. New dad’s can Only Think About…
  • 14. New dad’s can Only Think About…
  • 15. New dad’s can Only Think About…
  • 16. Summer’s Blocks
  • 17. Summer’s Blocks
  • 18. Summer’s Blocks
  • 19. Summer’s Blocks
  • 20. Why Worry About Zombies?
  • 21. Why Worry About Zombies?
  • 22. Ruby’s Blocks
  • 23. The Doubt
  • 24. Evil Blocks
  • 25. Experiment #1
  • 26. Scenario class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end # def []... end table = Table.new # access and print the 3...
  • 27. Awkward class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](y) @data[y] # row major access end end table = Table.new p table[1][2] # => 3
  • 28. Awkward class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](y) @data[y] # row major access end end table = Table.new p table[1][2] # => 3
  • 29. Awkward class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](y) @data[y] # row major access end end table = Table.new p table[1][2] # => 3 Y X
  • 30. Good Idea class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](x, y) @data[y][x] # probably the most natural end end table = Table.new p table[2, 1] # => 3
  • 31. Good Idea class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](x, y) @data[y][x] # probably the most natural end end table = Table.new p table[2, 1] # => 3
  • 32. Good Idea class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](x, y) @data[y][x] # probably the most natural end end table = Table.new p table[2, 1] # => 3 X Y
  • 33. The Challenge: Bad Idea class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](x) # ??? end end table = Table.new p table[2][1] # => 3
  • 34. The Challenge: Bad Idea class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](x) # ??? end end table = Table.new p table[2][1] # => 3
  • 35. The Challenge: Bad Idea class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](x) # ??? end end table = Table.new p table[2][1] # => 3 X Y
  • 36. The Challenge: Bad Idea class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](x) end end table = Table.new p table[2][1] # => 3
  • 37. The Challenge: Bad Idea class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](x) @data.transpose[x] end end table = Table.new p table[2][1] # => 3
  • 38. The Challenge: Bad Idea class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](x) end end table = Table.new p table[2][1] # => 3
  • 39. The Challenge: Bad Idea class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](x) lambda { |y| @data[y][x] } end end table = Table.new p table[2][1] # => 3
  • 40. Block Speak
  • 41. A Block Without a Block def some_method(&block) # use block here... end
  • 42. A Block Without a Block def some_method(&block) # use block here... end
  • 43. A Block Without a Block def some_method(&block) # use block here... end # close, but not exact def lambda(&block) return block end
  • 44. Closure Keeps us Sane class Total { public static void main(String[] args){ int[] numbers = {1, 2, 3, 4, 5}; int total = 0; for (int n : numbers) { total += n; } System.out.println("Total: " + total); } }
  • 45. Closure Keeps us Sane class Total { public static void main(String[] args){ int[] numbers = {1, 2, 3, 4, 5}; int total = 0; for (int n : numbers) { total += n; } System.out.println("Total: " + total); } }
  • 46. Closure Keeps us Sane class Total { public static void main(String[] args){ int[] numbers = {1, 2, 3, 4, 5}; int total = 0; for (int n : numbers) { total += n; } System.out.println("Total: " + total); } }
  • 47. Closure Keeps us Sane class Total { public static void main(String[] args){ int[] numbers = {1, 2, 3, 4, 5}; int total = 0; for (int n : numbers) { total += n; } System.out.println("Total: " + total); } } total = 0 (1..5).each do |n| total += n end puts "Total: #{total}"
  • 48. The Other Duck Typing def add_two(appendable) appendable << "two" end add_two(["one"]) # => ["one", "two"] add_two("forty ") # => "forty two" open("two.txt", "w") { |f| add_two(f) } File.read("two.txt") # => "two"
  • 49. The Other Duck Typing def add_two(appendable) appendable << "two" end add_two(["one"]) # => ["one", "two"] add_two("forty ") # => "forty two" open("two.txt", "w") { |f| add_two(f) } File.read("two.txt") # => "two"
  • 50. The Other Duck Typing def add_two(appendable) appendable << "two" end add_two(["one"]) # => ["one", "two"] add_two("forty ") # => "forty two" open("two.txt", "w") { |f| add_two(f) } File.read("two.txt") # => "two" def at_two(indexable) indexable[2] end at_two("_abc") # => "b" at_two([0, 1, 2]) # => 2 at_two(1 => :one, 2 => :two) # => :two at_two(lambda { |n| n ** 2 }) # => 4
  • 51. The Other Duck Typing def add_two(appendable) appendable << "two" end add_two(["one"]) # => ["one", "two"] add_two("forty ") # => "forty two" open("two.txt", "w") { |f| add_two(f) } File.read("two.txt") # => "two" def at_two(indexable) indexable[2] end at_two("_abc") # => "b" at_two([0, 1, 2]) # => 2 at_two(1 => :one, 2 => :two) # => :two at_two(lambda { |n| n ** 2 }) # => 4
  • 52. The Other Duck Typing def add_two(appendable) appendable << "two" end add_two(["one"]) # => ["one", "two"] add_two("forty ") # => "forty two" open("two.txt", "w") { |f| add_two(f) } File.read("two.txt") # => "two" def at_two(indexable) indexable[2] end at_two("_abc") # => "b" at_two([0, 1, 2]) # => 2 at_two(1 => :one, 2 => :two) # => :two at_two(lambda { |n| n ** 2 }) # => 4
  • 53. The Solution Explained class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](x) lambda { |y| @data[y][x] } end end table = Table.new p table[2][1] # => 3
  • 54. The Solution Explained class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](x) lambda { |y| @data[y][x] } end end table = Table.new p table[2][1] # => 3
  • 55. The Solution Explained class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](x) lambda { |y| @data[y][x] } end end table = Table.new p table[2][1] # => 3
  • 56. The Solution Explained class Table def initialize @data = [ %w[A B C D], [1, 2, 3, 4], [5, 6, 7, 8] ] end def [](x) lambda { |y| @data[y][x] } end end table = Table.new p table[2][1] # => 3
  • 57. How’s my Driving?
  • 58. How’s my Driving?
  • 59. How’s my Driving?
  • 60. Experiment #2
  • 61. Blocks, Blocks Everywhere Iterators
  • 62. Blocks, Blocks Everywhere at_exit do error = $! if error and not error.is_a? SystemExit puts "You triggered an error." exit! end end fail "Oops." # >> You triggered an error.
  • 63. Blocks, Blocks Everywhere at_exit do error = $! if error and not error.is_a? SystemExit puts "You triggered an error." exit! end end fail "Oops." # >> You triggered an error.
  • 64. Blocks, Blocks Everywhere class Class def with_audit_log Class.new(self) do public_instance_methods.each do |name| define_method(name) do |*args, &block| puts "#{name} called with #{args.inspect}#{' and a block' if block}" super(*args, &block) end end end end end a = Array.with_audit_log.new a << 1 a[0] += 1 # >> << called with [1] # >> [] called with [0] # >> []= called with [0, 2]
  • 65. Blocks, Blocks Everywhere class Class def with_audit_log Class.new(self) do public_instance_methods.each do |name| define_method(name) do |*args, &block| puts "#{name} called with #{args.inspect}#{' and a block' if block}" super(*args, &block) end end end end end a = Array.with_audit_log.new a << 1 a[0] += 1 # >> << called with [1] # >> [] called with [0] # >> []= called with [0, 2]
  • 66. Ruby Structure on the Fly <names> <name nick="JEG2"> <first>James</first> <middle>Edward</middle> <last>Gray</last> <suffix>II</suffix> </name> <name> <first>Dana</first> <middle>Ann</middle> <middle>Leslie</middle> <last>Gray</last> </name> <name> <first>Summer</first> <middle>Leslie</middle> <last>Gray</last> </name> </names>
  • 67. Ruby Robot XML Parser!
  • 68. Who Needs Nokogiri? (Me!) class ClassyXML def parse @io.tap(&:rewind).each(">") do |tag| if %r{ A (?<text>[^<]*) < (?<lend>/?) (?<name>[:a-z_][-w.]*) (?<attributes>(?:s*[:a-z_][-w.]*s*=s*(?:"[^"]*"|'[^']*'))*) s* (?<rend>/?) s* > z }ixo =~ tag yield :tag_content, unescape(text) unless lend == "/" yield :tag_start, name, Hash[ attributes.scan( / ([:a-z_][-w.]*) s* = s* (?:"([^"]*)"|'([^']*)') /ixo ).map do |k, *v| [k, unescape(v.compact.first)] end ] end yield :tag_content, nil if rend == "/" yield :tag_end, name if lend == "/" or rend == "/" end end end end
  • 69. Who Needs Nokogiri? (Me!) class ClassyXML def parse @io.tap(&:rewind).each(">") do |tag| if %r{ A (?<text>[^<]*) < (?<lend>/?) (?<name>[:a-z_][-w.]*) (?<attributes>(?:s*[:a-z_][-w.]*s*=s*(?:"[^"]*"|'[^']*'))*) s* (?<rend>/?) s* > z }ixo =~ tag yield :tag_content, unescape(text) unless lend == "/" yield :tag_start, name, Hash[ attributes.scan( / ([:a-z_][-w.]*) s* = s* (?:"([^"]*)"|'([^']*)') /ixo ).map do |k, *v| [k, unescape(v.compact.first)] end ] end yield :tag_content, nil if rend == "/" yield :tag_end, name if lend == "/" or rend == "/" end end end end
  • 70. Who Needs Nokogiri? (Me!) class ClassyXML def parse @io.tap(&:rewind).each(">") do |tag| if %r{ A (?<text>[^<]*) < (?<lend>/?) (?<name>[:a-z_][-w.]*) (?<attributes>(?:s*[:a-z_][-w.]*s*=s*(?:"[^"]*"|'[^']*'))*) s* (?<rend>/?) s* > z }ixo =~ tag yield :tag_content, unescape(text) unless lend == "/" yield :tag_start, name, Hash[ attributes.scan( / ([:a-z_][-w.]*) s* = s* (?:"([^"]*)"|'([^']*)') /ixo ).map do |k, *v| [k, unescape(v.compact.first)] end ] end yield :tag_content, nil if rend == "/" yield :tag_end, name if lend == "/" or rend == "/" end end end end
  • 71. Who Needs Nokogiri? (Me!) class ClassyXML def parse @io.tap(&:rewind).each(">") do |tag| if %r{ A (?<text>[^<]*) < (?<lend>/?) (?<name>[:a-z_][-w.]*) (?<attributes>(?:s*[:a-z_][-w.]*s*=s*(?:"[^"]*"|'[^']*'))*) s* (?<rend>/?) s* > z }ixo =~ tag yield :tag_content, unescape(text) unless lend == "/" yield :tag_start, name, Hash[ attributes.scan( / ([:a-z_][-w.]*) s* = s* (?:"([^"]*)"|'([^']*)') /ixo ).map do |k, *v| [k, unescape(v.compact.first)] end ] end yield :tag_content, nil if rend == "/" yield :tag_end, name if lend == "/" or rend == "/" end end end end
  • 72. Sprinkle Details to Taste class ClassyXML ESCAPES = {lt: "<", gt: ">", quot: '"', apos: "'", amp: "&"} ESCAPES_RE = /&#{ESCAPES.keys.map { |k| Regexp.escape(k) }.join("|")};/ def initialize(io) @io = io @classes = nil end private def unescape(text) text.gsub(ESCAPES_RE) { |e| ESCAPES[e[1..-2].to_sym] } end end
  • 73. Sprinkle Details to Taste class ClassyXML ESCAPES = {lt: "<", gt: ">", quot: '"', apos: "'", amp: "&"} ESCAPES_RE = /&#{ESCAPES.keys.map { |k| Regexp.escape(k) }.join("|")};/ def initialize(io) @io = io @classes = nil end private def unescape(text) text.gsub(ESCAPES_RE) { |e| ESCAPES[e[1..-2].to_sym] } end end
  • 74. Event Based Parsing require "classy_xml" open("names.xml") do |file| xml = ClassyXML.new(file) xml.parse do |e, ton, a = { }| p event: e, text_or_name: ton, attributes: a if ton =~ /S/ end end # >> {:event=>:tag_start, :text_or_name=>"names", :attributes=>{}} # >> {:event=>:tag_start, :text_or_name=>"name", :attributes=>{"nick"=>"JEG2"}} # >> {:event=>:tag_start, :text_or_name=>"first", :attributes=>{}} # >> {:event=>:tag_content, :text_or_name=>"James", :attributes=>{}} # >> {:event=>:tag_end, :text_or_name=>"first", :attributes=>{}} # >> ...
  • 75. Event Based Parsing require "classy_xml" open("names.xml") do |file| xml = ClassyXML.new(file) xml.parse do |e, ton, a = { }| p event: e, text_or_name: ton, attributes: a if ton =~ /S/ end end # >> {:event=>:tag_start, :text_or_name=>"names", :attributes=>{}} # >> {:event=>:tag_start, :text_or_name=>"name", :attributes=>{"nick"=>"JEG2"}} # >> {:event=>:tag_start, :text_or_name=>"first", :attributes=>{}} # >> {:event=>:tag_content, :text_or_name=>"James", :attributes=>{}} # >> {:event=>:tag_end, :text_or_name=>"first", :attributes=>{}} # >> ...
  • 76. Event Based Parsing require "classy_xml" open("names.xml") do |file| xml = ClassyXML.new(file) xml.parse do |e, ton, a = { }| p event: e, text_or_name: ton, attributes: a if ton =~ /S/ end end # >> {:event=>:tag_start, :text_or_name=>"names", :attributes=>{}} # >> {:event=>:tag_start, :text_or_name=>"name", :attributes=>{"nick"=>"JEG2"}} # >> {:event=>:tag_start, :text_or_name=>"first", :attributes=>{}} # >> {:event=>:tag_content, :text_or_name=>"James", :attributes=>{}} # >> {:event=>:tag_end, :text_or_name=>"first", :attributes=>{}} # >> ...
  • 77. The Guts (Err, Organs)
  • 78. Just in Time Classes class ClassyXML def classes(enhancements = { }) return @classes if @classes stack, attrs = [:root], Hash.new { |as, c| as[c] = [ ] } parse do |event, text_or_name, attributes = { }| if event == :tag_start (attrs[stack.last] << text_or_name).uniq! stack << text_or_name elsif event == :tag_end fail "Malformed XML" unless stack.pop == text_or_name end end collapsed = { } attrs.delete_if do |c, as| c != :root and as.size == 1 and attrs.include?(as.first) and (collapsed[c] = as.first) end @classes = Hash[ attrs.map { |c, as| [c, classify(as, collapsed, enhancements[c.to_sym]) ] }] end end
  • 79. Just in Time Classes class ClassyXML def classes(enhancements = { }) return @classes if @classes stack, attrs = [:root], Hash.new { |as, c| as[c] = [ ] } parse do |event, text_or_name, attributes = { }| if event == :tag_start (attrs[stack.last] << text_or_name).uniq! stack << text_or_name elsif event == :tag_end fail "Malformed XML" unless stack.pop == text_or_name end end collapsed = { } attrs.delete_if do |c, as| c != :root and as.size == 1 and attrs.include?(as.first) and (collapsed[c] = as.first) end @classes = Hash[ attrs.map { |c, as| [c, classify(as, collapsed, enhancements[c.to_sym]) ] }] end end
  • 80. Just in Time Classes class ClassyXML def classes(enhancements = { }) return @classes if @classes stack, attrs = [:root], Hash.new { |as, c| as[c] = [ ] } parse do |event, text_or_name, attributes = { }| if event == :tag_start (attrs[stack.last] << text_or_name).uniq! stack << text_or_name elsif event == :tag_end fail "Malformed XML" unless stack.pop == text_or_name end end collapsed = { } attrs.delete_if do |c, as| c != :root and as.size == 1 and attrs.include?(as.first) and (collapsed[c] = as.first) end @classes = Hash[ attrs.map { |c, as| [c, classify(as, collapsed, enhancements[c.to_sym]) ] }] end end
  • 81. Just in Time Classes class ClassyXML def classes(enhancements = { }) return @classes if @classes stack, attrs = [:root], Hash.new { |as, c| as[c] = [ ] } parse do |event, text_or_name, attributes = { }| if event == :tag_start (attrs[stack.last] << text_or_name).uniq! stack << text_or_name elsif event == :tag_end fail "Malformed XML" unless stack.pop == text_or_name end end collapsed = { } attrs.delete_if do |c, as| c != :root and as.size == 1 and attrs.include?(as.first) and (collapsed[c] = as.first) end @classes = Hash[ attrs.map { |c, as| [c, classify(as, collapsed, enhancements[c.to_sym]) ] }] end end
  • 82. Just in Time Classes class ClassyXML def classes(enhancements = { }) return @classes if @classes stack, attrs = [:root], Hash.new { |as, c| as[c] = [ ] } parse do |event, text_or_name, attributes = { }| if event == :tag_start (attrs[stack.last] << text_or_name).uniq! stack << text_or_name elsif event == :tag_end fail "Malformed XML" unless stack.pop == text_or_name end end collapsed = { } attrs.delete_if do |c, as| c != :root and as.size == 1 and attrs.include?(as.first) and (collapsed[c] = as.first) end @classes = Hash[ attrs.map { |c, as| [c, classify(as, collapsed, enhancements[c.to_sym]) ] }] end end
  • 83. Just in Time Classes class ClassyXML def classes(enhancements = { }) return @classes if @classes stack, attrs = [:root], Hash.new { |as, c| as[c] = [ ] } parse do |event, text_or_name, attributes = { }| if event == :tag_start (attrs[stack.last] << text_or_name).uniq! stack << text_or_name elsif event == :tag_end fail "Malformed XML" unless stack.pop == text_or_name end end collapsed = { } attrs.delete_if do |c, as| c != :root and as.size == 1 and attrs.include?(as.first) and (collapsed[c] = as.first) end @classes = Hash[ attrs.map { |c, as| [c, classify(as, collapsed, enhancements[c.to_sym]) ] }] end end
  • 84. Programming Ruby With Ruby class ClassyXML private def classify(attributes, collapsed, enhancement) Class.new do attributes.each do |a| define_method(:initialize) do |xml_attributes = { }| @attributes = xml_attributes attributes.each do |a| instance_variable_set("@#{a}", [ ]) end end attr_reader :attributes define_method("add_#{collapsed.fetch(a, a)}") do |arg| instance_variable_get("@#{a}") << arg end define_method(a) do iv = instance_variable_get("@#{a}") iv.size > 1 ? iv : iv.first end class_eval(&enhancement) if enhancement end end end end
  • 85. Programming Ruby With Ruby class ClassyXML private def classify(attributes, collapsed, enhancement) Class.new do attributes.each do |a| define_method(:initialize) do |xml_attributes = { }| @attributes = xml_attributes attributes.each do |a| instance_variable_set("@#{a}", [ ]) end end attr_reader :attributes define_method("add_#{collapsed.fetch(a, a)}") do |arg| instance_variable_get("@#{a}") << arg end define_method(a) do iv = instance_variable_get("@#{a}") iv.size > 1 ? iv : iv.first end class_eval(&enhancement) if enhancement end end end end
  • 86. Programming Ruby With Ruby class ClassyXML private def classify(attributes, collapsed, enhancement) Class.new do attributes.each do |a| define_method(:initialize) do |xml_attributes = { }| @attributes = xml_attributes attributes.each do |a| instance_variable_set("@#{a}", [ ]) end end attr_reader :attributes define_method("add_#{collapsed.fetch(a, a)}") do |arg| instance_variable_get("@#{a}") << arg end define_method(a) do iv = instance_variable_get("@#{a}") iv.size > 1 ? iv : iv.first end class_eval(&enhancement) if enhancement end end end end
  • 87. Programming Ruby With Ruby class ClassyXML private def classify(attributes, collapsed, enhancement) Class.new do attributes.each do |a| define_method(:initialize) do |xml_attributes = { }| @attributes = xml_attributes attributes.each do |a| instance_variable_set("@#{a}", [ ]) end end attr_reader :attributes define_method("add_#{collapsed.fetch(a, a)}") do |arg| instance_variable_get("@#{a}") << arg end define_method(a) do iv = instance_variable_get("@#{a}") iv.size > 1 ? iv : iv.first end class_eval(&enhancement) if enhancement end end end end
  • 88. Building the Tree of Objects class ClassyXML def to_objects root = classes[:root].new current_objects, current_attr = [root], nil parse do |event, text_or_name, attributes = { }| if event == :tag_start current_attr = text_or_name if classes.include? text_or_name current_objects << classes[text_or_name].new(attributes) current_objects[-2].send("add_#{current_attr}", current_objects[-1]) end elsif event == :tag_content if text_or_name.nil? or text_or_name =~ /S/ current_objects.last.send("add_#{current_attr}", text_or_name) end elsif event == :tag_end current_objects.pop if classes.include? text_or_name end end root end end
  • 89. Building the Tree of Objects class ClassyXML def to_objects root = classes[:root].new current_objects, current_attr = [root], nil parse do |event, text_or_name, attributes = { }| if event == :tag_start current_attr = text_or_name if classes.include? text_or_name current_objects << classes[text_or_name].new(attributes) current_objects[-2].send("add_#{current_attr}", current_objects[-1]) end elsif event == :tag_content if text_or_name.nil? or text_or_name =~ /S/ current_objects.last.send("add_#{current_attr}", text_or_name) end elsif event == :tag_end current_objects.pop if classes.include? text_or_name end end root end end
  • 90. Building the Tree of Objects class ClassyXML def to_objects root = classes[:root].new current_objects, current_attr = [root], nil parse do |event, text_or_name, attributes = { }| if event == :tag_start current_attr = text_or_name if classes.include? text_or_name current_objects << classes[text_or_name].new(attributes) current_objects[-2].send("add_#{current_attr}", current_objects[-1]) end elsif event == :tag_content if text_or_name.nil? or text_or_name =~ /S/ current_objects.last.send("add_#{current_attr}", text_or_name) end elsif event == :tag_end current_objects.pop if classes.include? text_or_name end end root end end
  • 91. A Drop of Sugar class ClassyXML def self.objectify(path, enhancements = { }) open(path) do |file| new(file).tap { |xml| xml.classes(enhancements) }.to_objects end end end
  • 92. The Taste Test require "classy_xml" xml = ClassyXML.objectify("names.xml") puts "Nicknames:", xml.names.map { |name| name.attributes["nick"] }.compact puts "Shared names:", xml.names.select { |name| Array(name.middle).include?("Leslie") } .map(&:first) # >> Nicknames: # >> JEG2 # >> Shared names: # >> Dana # >> Summer
  • 93. The Taste Test require "classy_xml" xml = ClassyXML.objectify("names.xml") puts "Nicknames:", xml.names.map { |name| name.attributes["nick"] }.compact puts "Shared names:", xml.names.select { |name| Array(name.middle).include?("Leslie") } .map(&:first) # >> Nicknames: # >> JEG2 # >> Shared names: # >> Dana # >> Summer
  • 94. The Taste Test require "classy_xml" xml = ClassyXML.objectify("names.xml") puts "Nicknames:", xml.names.map { |name| name.attributes["nick"] }.compact puts "Shared names:", xml.names.select { |name| Array(name.middle).include?("Leslie") } .map(&:first) require "nokogiri" xml = open("names.xml") { |f| Nokogiri.XML(f) } puts "Nicknames:", xml.xpath("//name/@nick") puts "Shared names:", xml.xpath("//name[middle='Leslie']/first").map(&:text) # >> Nicknames: # >> JEG2 # >> Shared names: # >> Dana # >> Summer
  • 95. The Taste Test require "classy_xml" xml = ClassyXML.objectify("names.xml") puts "Nicknames:", xml.names.map { |name| name.attributes["nick"] }.compact puts "Shared names:", xml.names.select { |name| Array(name.middle).include?("Leslie") } .map(&:first) require "nokogiri" xml = open("names.xml") { |f| Nokogiri.XML(f) } puts "Nicknames:", xml.xpath("//name/@nick") puts "Shared names:", xml.xpath("//name[middle='Leslie']/first").map(&:text) # >> Nicknames: # >> JEG2 # >> Shared names: # >> Dana # >> Summer
  • 96. Let’s try a Different Food Group…
  • 97. iTune’s (Evil) XML <plist version="1.0"> <dict> <!-- ... --> <key>Tracks</key> <dict> <key>1893</key> <dict> <key>Track ID</key><integer>1893</integer> <key>Name</key><string>Blue Clear Sky</string> <key>Artist</key><string>George Strait</string> <key>Album</key><string>Blue Clear Sky</string> <key>Genre</key><string>Country</string> <!-- ... --> </dict> <!-- ... --> </dict> </dict>
  • 98. iTune’s (Evil) XML <plist version="1.0"> <dict> <!-- ... --> <key>Tracks</key> <dict> <key>1893</key> <dict> <key>Track ID</key><integer>1893</integer> <key>Name</key><string>Blue Clear Sky</string> <key>Artist</key><string>George Strait</string> <key>Album</key><string>Blue Clear Sky</string> <key>Genre</key><string>Country</string> <!-- ... --> </dict> <!-- ... --> </dict> </dict>
  • 99. Eat Your Heart Out require "classy_xml" require "date" xml = ClassyXML.objectify( "itunes.xml", :dict => proc do def value; @value ||= [ ] end def add_integer(i) value << i.to_i end def add_true(_) value << true end def add_false(_) value << false end def add_date(d) value << Date.parse(d) end def add_string(s) value << s end def add_dict(d) value << d end def to_hash Hash[@key.zip(@value.map { |v| v.to_hash rescue v })] end end ) p xml.plist.to_hash["Tracks"]["1893"] # >> {"Track ID"=>1893, "Name"=>"Blue Clear Sky", # >> "Artist"=>"George Strait", "Album"=>"Blue Clear Sky", # >> "Genre"=>"Country", ...}
  • 100. Eat Your Heart Out require "classy_xml" require "date" xml = ClassyXML.objectify( "itunes.xml", :dict => proc do def value; @value ||= [ ] end def add_integer(i) value << i.to_i end def add_true(_) value << true end def add_false(_) value << false end def add_date(d) value << Date.parse(d) end def add_string(s) value << s end def add_dict(d) value << d end def to_hash Hash[@key.zip(@value.map { |v| v.to_hash rescue v })] end end ) p xml.plist.to_hash["Tracks"]["1893"] # >> {"Track ID"=>1893, "Name"=>"Blue Clear Sky", # >> "Artist"=>"George Strait", "Album"=>"Blue Clear Sky", # >> "Genre"=>"Country", ...}
  • 101. Eat Your Heart Out require "classy_xml" require "date" xml = ClassyXML.objectify( "itunes.xml", :dict => proc do def value; @value ||= [ ] end def add_integer(i) value << i.to_i end def add_true(_) value << true end def add_false(_) value << false end def add_date(d) value << Date.parse(d) end def add_string(s) value << s end def add_dict(d) value << d end def to_hash Hash[@key.zip(@value.map { |v| v.to_hash rescue v })] end end ) p xml.plist.to_hash["Tracks"]["1893"] # >> {"Track ID"=>1893, "Name"=>"Blue Clear Sky", # >> "Artist"=>"George Strait", "Album"=>"Blue Clear Sky", # >> "Genre"=>"Country", ...}
  • 102. Eat Your Heart Out require "classy_xml" require "date" xml = ClassyXML.objectify( "itunes.xml", :dict => proc do def value; @value ||= [ ] end def add_integer(i) value << i.to_i end def add_true(_) value << true end def add_false(_) value << false end def add_date(d) value << Date.parse(d) end def add_string(s) value << s end def add_dict(d) value << d end def to_hash Hash[@key.zip(@value.map { |v| v.to_hash rescue v })] end end ) p xml.plist.to_hash["Tracks"]["1893"] # >> {"Track ID"=>1893, "Name"=>"Blue Clear Sky", # >> "Artist"=>"George Strait", "Album"=>"Blue Clear Sky", # >> "Genre"=>"Country", ...}
  • 103. Experiment #3
  • 104. Experiment #3
  • 105. Experiment #3
  • 106. Experiment #3
  • 107. Land of Lamdas (Procs) @tasks.map(&:name)
  • 108. Land of Lamdas (Procs) @tasks.map(&:name)
  • 109. Land of Lamdas (Procs) Symbol#to_proc
  • 110. Land of Lamdas (Procs) class Symbol # simplified and not needed with 1.9 or Rails def to_proc lambda { |receiver| receiver.send(self) } end end succ = :succ.to_proc succ["A"] # => "B" succ[1] # => 2 (1..3).map(&:succ) # => [2, 3, 4]
  • 111. Land of Lamdas (Procs) class Symbol # simplified and not needed with 1.9 or Rails def to_proc lambda { |receiver| receiver.send(self) } end end succ = :succ.to_proc succ["A"] # => "B" succ[1] # => 2 (1..3).map(&:succ) # => [2, 3, 4]
  • 112. Land of Lamdas (Procs) class Symbol # simplified and not needed with 1.9 or Rails def to_proc lambda { |receiver| receiver.send(self) } end end succ = :succ.to_proc succ["A"] # => "B" succ[1] # => 2 (1..3).map(&:succ) # => [2, 3, 4]
  • 113. Land of Lamdas (Procs) class Symbol # simplified and not needed with 1.9 or Rails def to_proc lambda { |receiver| receiver.send(self) } end end succ = :succ.to_proc succ["A"] # => "B" succ[1] # => 2 (1..3).map(&:succ) # => [2, 3, 4]
  • 114. Where for art Thou to_proc()?
  • 115. Where for art Thou to_proc()? Method
  • 116. Where for art Thou to_proc()? add_two = 2.method(:+) add_two.class # => Method add_two.call(40) # => 42 add_two.to_proc[40] # => 42 (1..3).map(&add_two) # => [3, 4, 5]
  • 117. Where for art Thou to_proc()? add_two = 2.method(:+) add_two.class # => Method add_two.call(40) # => 42 add_two.to_proc[40] # => 42 (1..3).map(&add_two) # => [3, 4, 5]
  • 118. Where for art Thou to_proc()? add_two = 2.method(:+) add_two.class # => Method add_two.call(40) # => 42 add_two.to_proc[40] # => 42 (1..3).map(&add_two) # => [3, 4, 5]
  • 119. Where for art Thou to_proc()? add_two = 2.method(:+) add_two.class # => Method add_two.call(40) # => 42 add_two.to_proc[40] # => 42 (1..3).map(&add_two) # => [3, 4, 5]
  • 120. How do we Abuse This? Object -> Block
  • 121. How do we Abuse This? Sinatra
  • 122. An Array as a Web Application require "sinatra" q=[] get("/enqueue/:arg", &q.method(:push)) get("/dequeue", &q.method(:shift)) get("/peek", &q.method(:first))
  • 123. An Array as a Web Application require "sinatra" q=[] get("/enqueue/:arg", &q.method(:push)) get("/dequeue", &q.method(:shift)) get("/peek", &q.method(:first))
  • 124. Smoke and Mirrors
  • 125. Smoke and Mirrors
  • 126. Smoke and Mirrors
  • 127. Smoke and Mirrors
  • 128. Smoke and Mirrors
  • 129. Smoke and Mirrors
  • 130. Let’s Peak Behind the Curtain…
  • 131. Requirements require "sinatra" q=[] get("/enqueue/:arg", &q.method(:push)) get("/dequeue", &q.method(:shift)) get("/peek", &q.method(:first))
  • 132. Requirements require "sinatra" q=[] get("/enqueue/:arg", &q.method(:push)) get("/dequeue", &q.method(:shift)) get("/peek", &q.method(:first))
  • 133. Requirements require "sinatra" q=[] get("/enqueue/:arg", &q.method(:push)) get("/dequeue", &q.method(:shift)) get("/peek", &q.method(:first))
  • 134. A Dirty Trick
  • 135. A Dirty Trick
  • 136. A Dirty Trick
  • 137. Think Hard!
  • 138. Think Hard!
  • 139. Think Hard!
  • 140. Think Hard!
  • 141. A Web Application
  • 142. ActiveRecord -> Sinatra
  • 143. Step 1: Reserve a URL Space $LOAD_PATH << File.join(File.dirname(__FILE__), "lib") require "summer/admin" require "summer/wish_list" use Rack::Static, :urls => %w[/css /images], :root => "public" map "/admin" do run Summer::Admin end map "/" do run Summer::WishList end
  • 144. Step 1: Reserve a URL Space $LOAD_PATH << File.join(File.dirname(__FILE__), "lib") require "summer/admin" require "summer/wish_list" use Rack::Static, :urls => %w[/css /images], :root => "public" map "/admin" do run Summer::Admin end map "/" do run Summer::WishList end
  • 145. Step 1: Reserve a URL Space $LOAD_PATH << File.join(File.dirname(__FILE__), "lib") require "summer/admin" require "summer/wish_list" use Rack::Static, :urls => %w[/css /images], :root => "public" map "/admin" do run Summer::Admin end map "/" do run Summer::WishList end
  • 146. Step 2: Map URL’s to Models require "delegate" require "sinatra/base" require "summer/database" module Summer class Admin < Sinatra::Base configure do Database.connect end model = SimpleDelegator.new(Database::Wish.first) before do subdomain = request.host.split(".").first model.__setobj__( Database::Wish. where("name LIKE ?", "%#{subdomain}%"). first ) end get("/edit/:name/:value", &model.method(:update_attribute)) get("/move_to_top", &model.method(:touch)) get("/remove", &model.method(:destroy)) end end
  • 147. Step 2: Map URL’s to Models require "delegate" require "sinatra/base" require "summer/database" module Summer class Admin < Sinatra::Base configure do Database.connect end model = SimpleDelegator.new(Database::Wish.first) before do subdomain = request.host.split(".").first model.__setobj__( Database::Wish. where("name LIKE ?", "%#{subdomain}%"). first ) end get("/edit/:name/:value", &model.method(:update_attribute)) get("/move_to_top", &model.method(:touch)) get("/remove", &model.method(:destroy)) end end
  • 148. Step 2: Map URL’s to Models require "delegate" require "sinatra/base" require "summer/database" module Summer class Admin < Sinatra::Base configure do Database.connect end model = SimpleDelegator.new(Database::Wish.first) before do subdomain = request.host.split(".").first model.__setobj__( Database::Wish. where("name LIKE ?", "%#{subdomain}%"). first ) end get("/edit/:name/:value", &model.method(:update_attribute)) get("/move_to_top", &model.method(:touch)) get("/remove", &model.method(:destroy)) end end
  • 149. The Do Nothing Proxy require "delegate" number = SimpleDelegator.new(2) number + 40 # => 42 number.__setobj__(3) number + 40 # => 43 class Double def initialize(n) @n = n end def to_i; @n * 2 end end double = Double.new(number) double.to_i # => 6 number.__setobj__(21) double.to_i # => 42
  • 150. The Do Nothing Proxy require "delegate" number = SimpleDelegator.new(2) number + 40 # => 42 number.__setobj__(3) number + 40 # => 43 class Double def initialize(n) @n = n end def to_i; @n * 2 end end double = Double.new(number) double.to_i # => 6 number.__setobj__(21) double.to_i # => 42
  • 151. The Do Nothing Proxy require "delegate" number = SimpleDelegator.new(2) number + 40 # => 42 number.__setobj__(3) number + 40 # => 43 class Double def initialize(n) @n = n end def to_i; @n * 2 end end double = Double.new(number) double.to_i # => 6 number.__setobj__(21) double.to_i # => 42
  • 152. The Do Nothing Proxy require "delegate" number = SimpleDelegator.new(2) number + 40 # => 42 number.__setobj__(3) number + 40 # => 43 class Double def initialize(n) @n = n end def to_i; @n * 2 end end double = Double.new(number) double.to_i # => 6 number.__setobj__(21) double.to_i # => 42
  • 153. The Magic Changing Model require "delegate" require "sinatra/base" require "summer/database" module Summer class Admin < Sinatra::Base configure do Database.connect end model = SimpleDelegator.new(Database::Wish.first) before do subdomain = request.host.split(".").first model.__setobj__( Database::Wish. where("name LIKE ?", "%#{subdomain}%"). first ) end get("/edit/:name/:value", &model.method(:update_attribute)) get("/move_to_top", &model.method(:touch)) get("/remove", &model.method(:destroy)) end end
  • 154. The Magic Changing Model require "delegate" require "sinatra/base" require "summer/database" module Summer class Admin < Sinatra::Base configure do Database.connect end model = SimpleDelegator.new(Database::Wish.first) before do subdomain = request.host.split(".").first model.__setobj__( Database::Wish. where("name LIKE ?", "%#{subdomain}%"). first ) end get("/edit/:name/:value", &model.method(:update_attribute)) get("/move_to_top", &model.method(:touch)) get("/remove", &model.method(:destroy)) end end
  • 155. The Magic Changing Model require "delegate" require "sinatra/base" require "summer/database" module Summer class Admin < Sinatra::Base configure do Database.connect end model = SimpleDelegator.new(Database::Wish.first) before do subdomain = request.host.split(".").first model.__setobj__( Database::Wish. where("name LIKE ?", "%#{subdomain}%"). first ) end get("/edit/:name/:value", &model.method(:update_attribute)) get("/move_to_top", &model.method(:touch)) get("/remove", &model.method(:destroy)) end end
  • 156. The Magic Changing Model require "delegate" require "sinatra/base" require "summer/database" module Summer class Admin < Sinatra::Base configure do Database.connect end model = SimpleDelegator.new(Database::Wish.first) before do subdomain = request.host.split(".").first model.__setobj__( Database::Wish. where("name LIKE ?", "%#{subdomain}%"). first ) end get("/edit/:name/:value", &model.method(:update_attribute)) get("/move_to_top", &model.method(:touch)) get("/remove", &model.method(:destroy)) end end
  • 157. The Magic Changing Model require "delegate" require "sinatra/base" require "summer/database" module Summer class Admin < Sinatra::Base configure do Database.connect end model = SimpleDelegator.new(Database::Wish.first) before do subdomain = request.host.split(".").first model.__setobj__( Database::Wish. where("name LIKE ?", "%#{subdomain}%"). first ) end get("/edit/:name/:value", &model.method(:update_attribute)) get("/move_to_top", &model.method(:touch)) get("/remove", &model.method(:destroy)) end end
  • 158. http://rovio.lvh.me:9393/ admin/remove
  • 159. http://rovio.lvh.me:9393/ admin/remove
  • 160. http://shirt.lvh.me:9393/ admin/edit/price/very+reasonable
  • 161. http://shirt.lvh.me:9393/ admin/edit/price/very+reasonable
  • 162. http://bowl.lvh.me:9393/ admin/move_to_top
  • 163. http://bowl.lvh.me:9393/ admin/move_to_top
  • 164. Find me, Summer, or the Slides
  • 165. Find me, Summer, or the Slides Twitter @JEG2 Github /JEG2 http://blog.grayproductions.net/ Blog http://summerlesliegray.com/ SlideShare /JamesEdwardGrayII