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!

Unblocked

on

  • 5,902 views

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.

Statistics

Views

Total Views
5,902
Views on SlideShare
5,892
Embed Views
10

Actions

Likes
7
Downloads
71
Comments
0

2 Embeds 10

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

Accessibility

Categories

Upload Details

Uploaded via as Apple Keynote

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment
  • <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 />

Unblocked Unblocked Presentation Transcript

  • unblocked { # ... } Ruby as a child’s plaything
  • James Edward Gray II
  • James Edward Gray II I’ve been in the Ruby community for a long time (before Rails)
  • 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
  • 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
  • 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
  • I Used to be Cool
  • I Used to be Cool
  • I Used to be Cool
  • I Used to be Cool
  • I Used to be Cool
  • But now I’m a Dad
  • New dad’s can Only Think About…
  • New dad’s can Only Think About…
  • New dad’s can Only Think About…
  • Summer’s Blocks
  • Summer’s Blocks
  • Summer’s Blocks
  • Summer’s Blocks
  • Why Worry About Zombies?
  • Why Worry About Zombies?
  • Ruby’s Blocks
  • The Doubt
  • Evil Blocks
  • Experiment #1
  • 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...
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • Block Speak
  • A Block Without a Block def some_method(&block) # use block here... end
  • A Block Without a Block def some_method(&block) # use block here... end
  • A Block Without a Block def some_method(&block) # use block here... end # close, but not exact def lambda(&block) return block end
  • 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); } }
  • 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); } }
  • 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); } }
  • 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}"
  • 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"
  • 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"
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • How’s my Driving?
  • How’s my Driving?
  • How’s my Driving?
  • Experiment #2
  • Blocks, Blocks Everywhere Iterators
  • 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.
  • 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.
  • 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]
  • 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]
  • 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>
  • Ruby Robot XML Parser!
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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=>{}} # >> ...
  • 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=>{}} # >> ...
  • 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=>{}} # >> ...
  • The Guts (Err, Organs)
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • Let’s try a Different Food Group…
  • 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>
  • 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>
  • 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", ...}
  • 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", ...}
  • 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", ...}
  • 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", ...}
  • Experiment #3
  • Experiment #3
  • Experiment #3
  • Experiment #3
  • Land of Lamdas (Procs) @tasks.map(&:name)
  • Land of Lamdas (Procs) @tasks.map(&:name)
  • Land of Lamdas (Procs) Symbol#to_proc
  • 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]
  • 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]
  • 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]
  • 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]
  • Where for art Thou to_proc()?
  • Where for art Thou to_proc()? Method
  • 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]
  • 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]
  • 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]
  • 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]
  • How do we Abuse This? Object -> Block
  • How do we Abuse This? Sinatra
  • 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))
  • 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))
  • Smoke and Mirrors
  • Smoke and Mirrors
  • Smoke and Mirrors
  • Smoke and Mirrors
  • Smoke and Mirrors
  • Smoke and Mirrors
  • Let’s Peak Behind the Curtain…
  • Requirements require "sinatra" q=[] get("/enqueue/:arg", &q.method(:push)) get("/dequeue", &q.method(:shift)) get("/peek", &q.method(:first))
  • Requirements require "sinatra" q=[] get("/enqueue/:arg", &q.method(:push)) get("/dequeue", &q.method(:shift)) get("/peek", &q.method(:first))
  • Requirements require "sinatra" q=[] get("/enqueue/:arg", &q.method(:push)) get("/dequeue", &q.method(:shift)) get("/peek", &q.method(:first))
  • A Dirty Trick
  • A Dirty Trick
  • A Dirty Trick
  • Think Hard!
  • Think Hard!
  • Think Hard!
  • Think Hard!
  • A Web Application
  • ActiveRecord -> Sinatra
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • 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
  • http://rovio.lvh.me:9393/ admin/remove
  • http://rovio.lvh.me:9393/ admin/remove
  • http://shirt.lvh.me:9393/ admin/edit/price/very+reasonable
  • http://shirt.lvh.me:9393/ admin/edit/price/very+reasonable
  • http://bowl.lvh.me:9393/ admin/move_to_top
  • http://bowl.lvh.me:9393/ admin/move_to_top
  • Find me, Summer, or the Slides
  • Find me, Summer, or the Slides Twitter @JEG2 Github /JEG2 http://blog.grayproductions.net/ Blog http://summerlesliegray.com/ SlideShare /JamesEdwardGrayII