Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Ruby Batteries Included

Ruby Batteries Included

As given at MWRC 2013.

Daniel Huckstep

April 05, 2013
Tweet

Other Decks in Programming

Transcript

  1. HOW MANY GEMS DOES IT TAKE TO BUILD AN APP?

    $ grep gem Gemfile -c 135
  2. find `ruby -e 'puts $:.join(" ")'` -iname '*.rb' -exec grep

    -E '^\w*class ' {} \; | sort | uniq | wc -l
  3. find `ruby -e 'puts $:.join(" ")'` -iname '*.rb' -exec grep

    -E '^\w*class ' {} \; | sort | uniq | wc -l
  4. Set

  5. Set require 'set' s = Set.new([1, 2, 3, 1, 1,

    1]) puts s.inspect # => #<Set: {1, 2, 3}>
  6. Enumerable class MyFileChanger include Enumerable File = Struct.new(:name, :content) def

    initialize @files = [] end def <<(filename) content = ::File.read(filename) @files << File.new(filename, content) self end def each @files.each do |file| yield([file.name, file.content]) end end end
  7. Enumerable class MyFileChanger include Enumerable File = Struct.new(:name, :content) def

    initialize @files = [] end def <<(filename) content = ::File.read(filename) @files << File.new(filename, content) self end def each @files.each do |file| yield([file.name, file.content]) end end end files = MyFileChanger.new files << '/etc/profile' << '/etc/bashrc' files.each_with_index do |(name, content), index| puts "#{index}. #{name} is #{content.bytesize} bytes" end names = files.map do |(name, content)| name end puts names.inspect size = files.reduce(0) do |sum, (name, content)| sum + content.bytesize end puts size
  8. Enumerable class MyFileChanger include Enumerable File = Struct.new(:name, :content) def

    initialize @files = [] end def <<(filename) content = ::File.read(filename) @files << File.new(filename, content) self end def each @files.each do |file| yield([file.name, file.content]) end end end files = MyFileChanger.new files << '/etc/profile' << '/etc/bashrc' files.each_with_index do |(name, content), index| puts "#{index}. #{name} is #{content.bytesize} bytes" end names = files.map do |(name, content)| name end puts names.inspect size = files.reduce(0) do |sum, (name, content)| sum + content.bytesize end puts size 0. /etc/profile is 189 bytes 1. /etc/bashrc is 745 bytes ["/etc/profile", "/etc/bashrc"] 934
  9. Enumerator a = [1, 2, 3] a.each { |n| puts

    n } Enumerator.new(a).each { |n| puts n }
  10. Enumerator a = [1, 2, 3] a.each { |n| puts

    n } Enumerator.new(a).each { |n| puts n } naturals = Enumerator.new do |y| n = 1 loop do y << n n += 1 end end odds = Enumerator.new do |y| naturals.each do |n| y << n if n % 2 != 0 end end
  11. Enumerator a = [1, 2, 3] a.each { |n| puts

    n } Enumerator.new(a).each { |n| puts n } naturals = Enumerator.new do |y| n = 1 loop do y << n n += 1 end end odds = Enumerator.new do |y| naturals.each do |n| y << n if n % 2 != 0 end end class Integer def prime? return false if self < 2 max = Math.sqrt(self).floor (2..max).none? { |n| self % n == 0 } end end primes = Enumerator.new do |y| y << 2 odds.each do |n| y << n if n.prime? end end
  12. Enumerator a = [1, 2, 3] a.each { |n| puts

    n } Enumerator.new(a).each { |n| puts n } naturals = Enumerator.new do |y| n = 1 loop do y << n n += 1 end end odds = Enumerator.new do |y| naturals.each do |n| y << n if n % 2 != 0 end end class Integer def prime? return false if self < 2 max = Math.sqrt(self).floor (2..max).none? { |n| self % n == 0 } end end primes = Enumerator.new do |y| y << 2 odds.each do |n| y << n if n.prime? end end puts primes.take(10).inspect # => [2, 3, 5, 7, 11, 13, 17, 19, 23, 29]
  13. SimpleDelegator require 'delegate' class FancyString < SimpleDelegator def fancy? self

    == 'fancy' end end s = FancyString.new('fancy') puts s.fancy? # => true
  14. SimpleDelegator require 'delegate' class FancyString < SimpleDelegator def fancy? self

    == 'fancy' end end s = FancyString.new('fancy') puts s.fancy? # => true puts s.length # => 5
  15. Forwardable require 'forwardable' FancyString = Struct.new(:str) do extend Forwardable #

    Single delegation def_delegator :str, :length, :fancy_length # Multiple delegations def_delegators :str, :include?, :match end
  16. Forwardable require 'forwardable' FancyString = Struct.new(:str) do extend Forwardable #

    Single delegation def_delegator :str, :length, :fancy_length # Multiple delegations def_delegators :str, :include?, :match end s = FancyString.new('Hello!')
  17. Forwardable require 'forwardable' FancyString = Struct.new(:str) do extend Forwardable #

    Single delegation def_delegator :str, :length, :fancy_length # Multiple delegations def_delegators :str, :include?, :match end s = FancyString.new('Hello!') puts s.fancy_length # => 6 puts s.include?('!') # => true
  18. Forwardable require 'forwardable' FancyString = Struct.new(:str) do extend Forwardable #

    Single delegation def_delegator :str, :length, :fancy_length # Multiple delegations def_delegators :str, :include?, :match end s = FancyString.new('Hello!') puts s.fancy_length # => 6 puts s.include?('!') # => true puts s.bytesize # => NoMethodError
  19. Benchmark require 'benchmark' results = Benchmark.measure do 1_000_000.times { |i|

    i.to_s } GC.start end puts results # => 0.480000 0.010000 0.490000 ( 0.487643)
  20. Benchmark require 'benchmark' results = Benchmark.measure do 1_000_000.times { |i|

    i.to_s } GC.start end puts results # => 0.480000 0.010000 0.490000 ( 0.487643) n = 5000000 # Simple benchmark Benchmark.bm do |x| x.report { for i in 1..n; a = "1"; end } x.report { n.times do ; a = "1"; end } x.report { 1.upto(n) do ; a = "1"; end } end
  21. Benchmark # Nice reporting labels Benchmark.bm(10) do |x| x.report('for:') {

    for i in 1..n; a = "1"; end } x.report('times:') { n.times do ; a = "1"; end } x.report('upto:') { 1.upto(n) do ; a = "1"; end } end # Warmup Benchmark.bmbm(10) do |x| x.report('for:') { for i in 1..n; a = "1"; end } x.report('times:') { n.times do ; a = "1"; end } x.report('upto:') { 1.upto(n) do ; a = "1"; end } end
  22. RubyVM def acc(i, n, result) if i == -1 result

    else acc(i - 1, n + result, n) end end def fib(i) acc(i, 1, 0) end puts fib(10000) # => stack level too deep (SystemStackError) http://timelessrepo.com/tailin-ruby
  23. RubyVM::InstructionSequence RubyVM::InstructionSequence.compile_option = { :tailcall_optimization => true, :trace_instruction => false

    } RubyVM::InstructionSequence.new(<<-EOF).eval def acc(i, n, result) if i == -1 result else acc(i - 1, n + result, n) end end def fib(i) acc(i, 1, 0) end EOF
  24. RubyVM::InstructionSequence RubyVM::InstructionSequence.compile_option = { :tailcall_optimization => true, :trace_instruction => false

    } RubyVM::InstructionSequence.new(<<-EOF).eval def acc(i, n, result) if i == -1 result else acc(i - 1, n + result, n) end end def fib(i) acc(i, 1, 0) end EOF puts fib(10000) # => A number with 2090 digits
  25. Fiddle require 'fiddle' include Fiddle libm = DL.dlopen('/usr/lib/libm.dylib') f =

    libm['sqrt'] args = [TYPE_DOUBLE] ret = TYPE_DOUBLE sqrt = Function.new(f, args, ret)
  26. Fiddle require 'fiddle' include Fiddle libm = DL.dlopen('/usr/lib/libm.dylib') f =

    libm['sqrt'] args = [TYPE_DOUBLE] ret = TYPE_DOUBLE sqrt = Function.new(f, args, ret) puts sqrt.call(81) # => 9.0
  27. WeakRef require 'weakref' bightml = WeakRef.new(read_html('http:// bigpage.com/')) puts bightml #

    => "<html>..." puts bightml.weakref_alive? # => true GC.start puts bightml.weakref_alive? # => false
  28. SecureRandom require 'securerandom' puts SecureRandom.base64 # => 9d4va7k0/XhK2EqYMXJ2jg== puts SecureRandom.hex

    # => 9e70e038fc7faf20106c304854c21b8a puts SecureRandom.random_bytes.inspect # => "r>0\xD1\xA4\x00t&\x82\xFCEl_\x17w\xF1"
  29. SecureRandom require 'securerandom' puts SecureRandom.base64 # => 9d4va7k0/XhK2EqYMXJ2jg== puts SecureRandom.hex

    # => 9e70e038fc7faf20106c304854c21b8a puts SecureRandom.random_bytes.inspect # => "r>0\xD1\xA4\x00t&\x82\xFCEl_\x17w\xF1" puts SecureRandom.random_number # => 0.47149176126339587
  30. SecureRandom require 'securerandom' puts SecureRandom.base64 # => 9d4va7k0/XhK2EqYMXJ2jg== puts SecureRandom.hex

    # => 9e70e038fc7faf20106c304854c21b8a puts SecureRandom.random_bytes.inspect # => "r>0\xD1\xA4\x00t&\x82\xFCEl_\x17w\xF1" puts SecureRandom.random_number # => 0.47149176126339587 puts SecureRandom.random_number(50) # => 37
  31. SecureRandom require 'securerandom' puts SecureRandom.base64 # => 9d4va7k0/XhK2EqYMXJ2jg== puts SecureRandom.hex

    # => 9e70e038fc7faf20106c304854c21b8a puts SecureRandom.random_bytes.inspect # => "r>0\xD1\xA4\x00t&\x82\xFCEl_\x17w\xF1" puts SecureRandom.random_number # => 0.47149176126339587 puts SecureRandom.random_number(50) # => 37 puts SecureRandom.uuid # => 3a34412c-4798-4985-9a43-7d36a6e0d116
  32. GServer require 'gserver' require 'securerandom' class RandomServer < GServer def

    serve(io) cmd = io.read(4).strip case cmd when 'hex' then io.puts(SecureRandom.hex) when 'uuid' then io.puts(SecureRandom.uuid) end end end
  33. GServer require 'gserver' require 'securerandom' class RandomServer < GServer def

    serve(io) cmd = io.read(4).strip case cmd when 'hex' then io.puts(SecureRandom.hex) when 'uuid' then io.puts(SecureRandom.uuid) end end end server = RandomServer.new(10101) server.start server.join
  34. Kernel#spawn def capture(cmd) r, w = IO.pipe pid = spawn(cmd,

    :out => w) w.close Process.wait(pid) r.read ensure r.close end output = capture('randomizer') print output
  35. Shellwords require 'shellwords' pattern = '; rm -rf /; '

    capture("randomizer #{pattern.shellescape} /dev/*")
  36. Shellwords require 'shellwords' pattern = '; rm -rf /; '

    capture("randomizer #{pattern.shellescape} /dev/*") # Assuming our capture function could take *args like system
  37. Shellwords require 'shellwords' pattern = '; rm -rf /; '

    capture("randomizer #{pattern.shellescape} /dev/*") # Assuming our capture function could take *args like system capture('randomizer', pattern, '/dev/*') # => Nope
  38. PStore require 'pstore' store = PStore.new('randomizer', thread_safe = true) store.ultra_safe

    = true store.transaction do store['App'] = 'The Randomizer' store[:randoms] ||= [] store[:randoms] << Randomizer.uuid end
  39. PStore require 'pstore' store = PStore.new('randomizer', thread_safe = true) store.ultra_safe

    = true store.transaction do store['App'] = 'The Randomizer' store[:randoms] ||= [] store[:randoms] << Randomizer.uuid end store.transaction do puts store[:randoms].inspect # => ["901dcd24-fa10-4194-b25b-8ad49bc875fb"] end
  40. PStore require 'pstore' store = PStore.new('randomizer', thread_safe = true) store.ultra_safe

    = true store.transaction do store['App'] = 'The Randomizer' store[:randoms] ||= [] store[:randoms] << Randomizer.uuid end store.transaction do puts store[:randoms].inspect # => ["901dcd24-fa10-4194-b25b-8ad49bc875fb"] end store.transaction do store[:randoms] = [] store.abort # Or an Exception end
  41. PStore require 'pstore' store = PStore.new('randomizer', thread_safe = true) store.ultra_safe

    = true store.transaction do store['App'] = 'The Randomizer' store[:randoms] ||= [] store[:randoms] << Randomizer.uuid end store.transaction do puts store[:randoms].inspect # => ["901dcd24-fa10-4194-b25b-8ad49bc875fb"] end store.transaction do store[:randoms] = [] store.abort # Or an Exception end store.transaction do puts store[:randoms].inspect # => ["901dcd24-fa10-4194-b25b-8ad49bc875fb"] end
  42. MiniTest require 'minitest/autorun' class MathTest < MiniTest::Unit::TestCase def test_addition assert_equal

    2, 1 + 1 end end describe Integer do it 'should subtract' do (1 - 1).must_equal(0) end end
  43. MiniTest require 'minitest/autorun' class MathTest < MiniTest::Unit::TestCase def test_addition assert_equal

    2, 1 + 1 end end describe Integer do it 'should subtract' do (1 - 1).must_equal(0) end end describe 'Mocks' do it 'should mock things' do mock = MiniTest::Mock.new mock.expect(:length, 10) mock.length.must_equal(10) mock.verify end end
  44. Other Fun Things • OptionParser/optparse, GetoptLong/getoptlong • Two command line

    option parsers! • WEBrick => require ‘webrick’ • Threaded HTTP server. • Rss => require ‘rss’ • Parse and generate RSS feeds. In the stdlib. wat.
  45. Other Fun Things • Drb => require ‘drb’ • Distributed

    object system. • Find => require ‘find’ • Recursively iterate the filesystem. • Ripper => require ‘ripper’ • Parse ruby files
  46. Other Fun Things • ThreadsWait => require ‘thwait’ • Wait

    for a bunch of threads, block called for each when it finishes. • Queue/SizedQueue => require ‘thread’ • << things in, #pop things out. • MonitorMixin => require ‘monitor’ • Thread safety with synchronize block.
  47. Other Fun Things • Net::POP3 => require ‘net/pop’ • Talk

    POP3 email. • Net::IMAP => require ‘net/imap’ • Talk IMAP email. • Net::SMTP => require ‘net/smtp’ • Talk to SMTP servers.
  48. Other Fun Things • Net::FTP => require ‘net/ftp’ • Talk

    FTP • Net::HTTP => require ‘net/http’ • Talk HTTP