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.

Like this presentation? Why not share!

Like this? Share it with your network

Share

Unblocked

  • 5,975 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,975
On Slideshare
5,965
From Embeds
10
Number of Embeds
2

Actions

Shares
Downloads
71
Comments
0
Likes
7

Embeds 10

http://speakerrate.com 8
http://www.onlydoo.com 2

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
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />
  • <br />

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