Ruby Batteries Included

Ruby Batteries Included

As given at MWRC 2013.

48409ce1953c290351fcb875b20eccbb?s=128

Daniel Huckstep

April 05, 2013
Tweet

Transcript

  1. RUBY BATTERIES INCLUDED Ruby Can Standard Library, And So Can

    You!
  2. HOW MANY GEMS DOES IT TAKE TO BUILD AN APP?

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

    $ grep gem Gemfile -c
  4. HOW MANY GEMS DOES IT TAKE TO BUILD AN APP?

    $ grep gem Gemfile -c 135
  5. THE STANDARD LIBRARY http://ruby-doc.org/

  6. CORE VS STD-LIB

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

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

    -E '^\w*class ' {} \; | sort | uniq | wc -l
  9. OKAY, NOT ALL OF THEM •Bed Basics •Bath Performance •Beyond

    Beyond
  10. None
  11. http://www.flickr.com/photos/austinevan/3368444745/

  12. Set

  13. Set require 'set'

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

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

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

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

    n } Enumerator.new(a).each { |n| puts n }
  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
  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
  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]
  24. SimpleDelegator

  25. SimpleDelegator require 'delegate'

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

    == 'fancy' end end
  27. SimpleDelegator require 'delegate' class FancyString < SimpleDelegator def fancy? self

    == 'fancy' end end s = FancyString.new('fancy') puts s.fancy? # => true
  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
  29. Forwardable

  30. Forwardable require 'forwardable'

  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
  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!')
  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
  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
  35. None
  36. Benchmark

  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)
  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
  39. Benchmark

  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
  41. RubyVM http://timelessrepo.com/tailin-ruby

  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
  43. RubyVM::InstructionSequence

  44. RubyVM::InstructionSequence RubyVM::InstructionSequence.compile_option = { :tailcall_optimization => true, :trace_instruction => false

    }
  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
  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
  47. Fiddle

  48. Fiddle require 'fiddle' include Fiddle

  49. Fiddle require 'fiddle' include Fiddle libm = DL.dlopen('/usr/lib/libm.dylib')

  50. Fiddle require 'fiddle' include Fiddle libm = DL.dlopen('/usr/lib/libm.dylib') f =

    libm['sqrt']
  51. Fiddle require 'fiddle' include Fiddle libm = DL.dlopen('/usr/lib/libm.dylib') f =

    libm['sqrt'] args = [TYPE_DOUBLE]
  52. Fiddle require 'fiddle' include Fiddle libm = DL.dlopen('/usr/lib/libm.dylib') f =

    libm['sqrt'] args = [TYPE_DOUBLE] ret = TYPE_DOUBLE
  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)
  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
  55. WeakRef

  56. WeakRef require 'weakref' bightml = WeakRef.new(read_html('http:// bigpage.com/')) puts bightml #

    => "<html>..." puts bightml.weakref_alive? # => true
  57. WeakRef require 'weakref' bightml = WeakRef.new(read_html('http:// bigpage.com/')) puts bightml #

    => "<html>..." puts bightml.weakref_alive? # => true GC.start
  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
  59. http://www.flickr.com/photos/bobcanada/4506301467/

  60. SecureRandom

  61. SecureRandom require 'securerandom'

  62. SecureRandom require 'securerandom' puts SecureRandom.base64 # => 9d4va7k0/XhK2EqYMXJ2jg==

  63. SecureRandom require 'securerandom' puts SecureRandom.base64 # => 9d4va7k0/XhK2EqYMXJ2jg== puts SecureRandom.hex

    # => 9e70e038fc7faf20106c304854c21b8a
  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"
  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
  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
  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
  68. GServer

  69. GServer require 'gserver'

  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
  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
  72. Kernel#spawn

  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
  74. Shellwords

  75. Shellwords require 'shellwords'

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

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

    capture("randomizer #{pattern.shellescape} /dev/*") # Assuming our capture function could take *args like system
  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
  79. PStore

  80. PStore require 'pstore' store = PStore.new('randomizer', thread_safe = true) store.ultra_safe

    = true
  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
  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
  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
  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
  85. I heard you like testing...

  86. I heard you like testing...

  87. MiniTest

  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
  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
  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.
  91. Other Fun Things • Drb => require ‘drb’ • Distributed

    object system. • Find => require ‘find’ • Recursively iterate the filesystem. • Ripper => require ‘ripper’ • Parse ruby files
  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.
  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.
  94. Other Fun Things • Net::FTP => require ‘net/ftp’ • Talk

    FTP • Net::HTTP => require ‘net/http’ • Talk HTTP
  95. None
  96. THANKS!! @darkhelmetlive darkhelmet verboselogging.com GetYardstick.com https://www.makesets.com/the-ruby-standard-library