Ruby Batteries Included

Ruby Batteries Included

As given at MWRC 2013.

48409ce1953c290351fcb875b20eccbb?s=128

Daniel Huckstep

April 05, 2013
Tweet

Transcript

  1. 4.

    HOW MANY GEMS DOES IT TAKE TO BUILD AN APP?

    $ grep gem Gemfile -c 135
  2. 7.

    find `ruby -e 'puts $:.join(" ")'` -iname '*.rb' -exec grep

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

    find `ruby -e 'puts $:.join(" ")'` -iname '*.rb' -exec grep

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

    Set

  6. 14.

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

    1]) puts s.inspect # => #<Set: {1, 2, 3}>
  7. 16.

    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
  8. 17.

    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
  9. 18.

    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
  10. 20.

    Enumerator a = [1, 2, 3] a.each { |n| puts

    n } Enumerator.new(a).each { |n| puts n }
  11. 21.

    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
  12. 22.

    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
  13. 23.

    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]
  14. 27.

    SimpleDelegator require 'delegate' class FancyString < SimpleDelegator def fancy? self

    == 'fancy' end end s = FancyString.new('fancy') puts s.fancy? # => true
  15. 28.

    SimpleDelegator require 'delegate' class FancyString < SimpleDelegator def fancy? self

    == 'fancy' end end s = FancyString.new('fancy') puts s.fancy? # => true puts s.length # => 5
  16. 31.

    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
  17. 32.

    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!')
  18. 33.

    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
  19. 34.

    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
  20. 35.
  21. 36.
  22. 37.

    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)
  23. 38.

    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
  24. 39.
  25. 40.

    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
  26. 42.

    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
  27. 45.

    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
  28. 46.

    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
  29. 47.
  30. 52.
  31. 53.

    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)
  32. 54.

    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
  33. 55.
  34. 57.
  35. 58.

    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
  36. 64.

    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"
  37. 65.

    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
  38. 66.

    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
  39. 67.

    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
  40. 68.
  41. 70.

    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
  42. 71.

    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
  43. 73.

    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
  44. 76.

    Shellwords require 'shellwords' pattern = '; rm -rf /; '

    capture("randomizer #{pattern.shellescape} /dev/*")
  45. 77.

    Shellwords require 'shellwords' pattern = '; rm -rf /; '

    capture("randomizer #{pattern.shellescape} /dev/*") # Assuming our capture function could take *args like system
  46. 78.

    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
  47. 79.
  48. 81.

    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
  49. 82.

    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
  50. 83.

    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
  51. 84.

    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
  52. 87.
  53. 88.

    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
  54. 89.

    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
  55. 90.

    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.
  56. 91.

    Other Fun Things • Drb => require ‘drb’ • Distributed

    object system. • Find => require ‘find’ • Recursively iterate the filesystem. • Ripper => require ‘ripper’ • Parse ruby files
  57. 92.

    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.
  58. 93.

    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.
  59. 94.

    Other Fun Things • Net::FTP => require ‘net/ftp’ • Talk

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