$30 off During Our Annual Pro Sale. View Details »

Ruby Batteries Included

Ruby Batteries Included

As given at MWRC 2013.

Daniel Huckstep

April 05, 2013
Tweet

Other Decks in Programming

Transcript

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  5. THE STANDARD LIBRARY
    http://ruby-doc.org/

    View Slide

  6. CORE VS STD-LIB

    View Slide

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

    View Slide

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

    View Slide

  9. OKAY, NOT ALL OF THEM
    •Bed Basics
    •Bath Performance
    •Beyond Beyond

    View Slide

  10. View Slide

  11. http://www.flickr.com/photos/austinevan/3368444745/

    View Slide

  12. Set

    View Slide

  13. Set
    require 'set'

    View Slide

  14. Set
    require 'set'
    s = Set.new([1, 2, 3, 1, 1, 1])
    puts s.inspect
    # => #

    View Slide

  15. Enumerable

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  19. Enumerator

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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]

    View Slide

  24. SimpleDelegator

    View Slide

  25. SimpleDelegator
    require 'delegate'

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  29. Forwardable

    View Slide

  30. Forwardable
    require 'forwardable'

    View Slide

  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

    View Slide

  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!')

    View Slide

  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

    View Slide

  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

    View Slide

  35. View Slide

  36. Benchmark

    View Slide

  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)

    View Slide

  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

    View Slide

  39. Benchmark

    View Slide

  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

    View Slide

  41. RubyVM
    http://timelessrepo.com/tailin-ruby

    View Slide

  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

    View Slide

  43. RubyVM::InstructionSequence

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  47. Fiddle

    View Slide

  48. Fiddle
    require 'fiddle'
    include Fiddle

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

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

    View Slide

  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)

    View Slide

  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

    View Slide

  55. WeakRef

    View Slide

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

    View Slide

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

    View Slide

  58. WeakRef
    require 'weakref'
    bightml = WeakRef.new(read_html('http://
    bigpage.com/'))
    puts bightml # => "..."
    puts bightml.weakref_alive? # => true
    GC.start
    puts bightml.weakref_alive? # => false

    View Slide

  59. http://www.flickr.com/photos/bobcanada/4506301467/

    View Slide

  60. SecureRandom

    View Slide

  61. SecureRandom
    require 'securerandom'

    View Slide

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

    View Slide

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

    View Slide

  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"

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  68. GServer

    View Slide

  69. GServer
    require 'gserver'

    View Slide

  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

    View Slide

  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

    View Slide

  72. Kernel#spawn

    View Slide

  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

    View Slide

  74. Shellwords

    View Slide

  75. Shellwords
    require 'shellwords'

    View Slide

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

    View Slide

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

    View Slide

  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

    View Slide

  79. PStore

    View Slide

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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  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

    View Slide

  85. I heard you like testing...

    View Slide

  86. I heard you like testing...

    View Slide

  87. MiniTest

    View Slide

  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

    View Slide

  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

    View Slide

  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.

    View Slide

  91. Other Fun Things
    • Drb => require ‘drb’
    • Distributed object system.
    • Find => require ‘find’
    • Recursively iterate the filesystem.
    • Ripper => require ‘ripper’
    • Parse ruby files

    View Slide

  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.

    View Slide

  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.

    View Slide

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

    View Slide

  95. View Slide

  96. THANKS!!
    @darkhelmetlive
    darkhelmet
    verboselogging.com
    GetYardstick.com
    https://www.makesets.com/the-ruby-standard-library

    View Slide