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

LittleBIGRuby

jeg2
March 13, 2009

 LittleBIGRuby

This was my presentation about what video games can teach us about code reading. I gave it at MountainWest RubyConf 2009.

jeg2

March 13, 2009
Tweet

More Decks by jeg2

Other Decks in Technology

Transcript

  1. View Slide

  2. Code Reading 101

    View Slide

  3. TWO THINGS TO
    KNOW ABOUT ME

    View Slide

  4. TWO THINGS TO
    KNOW ABOUT ME
    I wrote the TextMate book

    View Slide

  5. TWO THINGS TO
    KNOW ABOUT ME
    I wrote the TextMate book
    My name is Jim Weirich

    View Slide

  6. JAMES EDWARD
    GRAY II

    View Slide

  7. JAMES EDWARD
    GRAY II
    I wrote two books for the Pragmatic Programmers: Best of
    Ruby Quiz and TextMate: Power Editing for the Mac

    View Slide

  8. JAMES EDWARD
    GRAY II
    I wrote two books for the Pragmatic Programmers: Best of
    Ruby Quiz and TextMate: Power Editing for the Mac
    I’ve contributed documentation and patches for some
    standard libraries, which I now help to maintain

    View Slide

  9. JAMES EDWARD
    GRAY II
    I wrote two books for the Pragmatic Programmers: Best of
    Ruby Quiz and TextMate: Power Editing for the Mac
    I’ve contributed documentation and patches for some
    standard libraries, which I now help to maintain
    I built FasterCSV (now CSV), HighLine (with Greg), Elif, and
    a few other libraries people don’t use

    View Slide

  10. JAMES EDWARD
    GRAY II
    I wrote two books for the Pragmatic Programmers: Best of
    Ruby Quiz and TextMate: Power Editing for the Mac
    I’ve contributed documentation and patches for some
    standard libraries, which I now help to maintain
    I built FasterCSV (now CSV), HighLine (with Greg), Elif, and
    a few other libraries people don’t use
    I created the Ruby Quiz and ran it for the first three years

    View Slide

  11. View Slide

  12. View Slide

  13. View Slide

  14. View Slide

  15. HI. I’M JAMES AND
    I READ CODE.

    View Slide

  16. HOW MUCH SHOULD
    YOU READ?

    View Slide

  17. HOW MUCH SHOULD
    YOU READ?
    My opinion based on the Dreyfus Model of Skill Acquisition.

    View Slide

  18. WHY IS CODE
    READING IMPORTANT?

    View Slide

  19. WHY IS CODE
    READING IMPORTANT?
    It can show you common idioms

    View Slide

  20. WHY IS CODE
    READING IMPORTANT?
    It can show you common idioms
    It’s good to practice breaking down possibly challenging
    code, because you will always have to work with other’s code

    View Slide

  21. WHY IS CODE
    READING IMPORTANT?
    It can show you common idioms
    It’s good to practice breaking down possibly challenging
    code, because you will always have to work with other’s code
    Understanding how something works gives you insight into
    any limitations it has

    View Slide

  22. WHY IS CODE
    READING IMPORTANT?
    It can show you common idioms
    It’s good to practice breaking down possibly challenging
    code, because you will always have to work with other’s code
    Understanding how something works gives you insight into
    any limitations it has
    Seeing bad code helps you write better code

    View Slide

  23. WHY IS CODE
    READING IMPORTANT?
    It can show you common idioms
    It’s good to practice breaking down possibly challenging
    code, because you will always have to work with other’s code
    Understanding how something works gives you insight into
    any limitations it has
    Seeing bad code helps you write better code
    Knowledge workers always need more ideas

    View Slide

  24. View Slide

  25. View Slide

  26. INTRODUCING
    RESTCLIENT

    View Slide

  27. WHAT IS RESTCLIENT?

    View Slide

  28. WHAT IS RESTCLIENT?
    Sinatra’s sister library (sometimes called “reverse Sinatra”)

    View Slide

  29. WHAT IS RESTCLIENT?
    Sinatra’s sister library (sometimes called “reverse Sinatra”)
    It provides a very clean interface to RESTful web services

    View Slide

  30. WHAT IS RESTCLIENT?
    Sinatra’s sister library (sometimes called “reverse Sinatra”)
    It provides a very clean interface to RESTful web services
    Simple well-written code (around 500 lines of clear code for
    the core functionality)

    View Slide

  31. WHAT IS RESTCLIENT?
    Sinatra’s sister library (sometimes called “reverse Sinatra”)
    It provides a very clean interface to RESTful web services
    Simple well-written code (around 500 lines of clear code for
    the core functionality)
    Plus a couple of exciting features

    View Slide

  32. BASIC GET
    Reading tweets with Twitter’s API

    View Slide

  33. BASIC GET
    Reading tweets with Twitter’s API
    require "rubygems"
    require "rest_client"
    require "json"
    !
    twitter = RestClient::Resource.new( "http://twitter.com/statuses",
    :user => "JEG2",
    :password => "secret" )
    !
    json = twitter["friends_timeline.json"].get
    tweets = JSON.parse(json)
    tweets.each do |tweet|
    # ...
    end

    View Slide

  34. BASIC GET
    Reading tweets with Twitter’s API
    require "rubygems"
    require "rest_client"
    require "json"
    !
    twitter = RestClient::Resource.new( "http://twitter.com/statuses",
    :user => "JEG2",
    :password => "secret" )
    !
    json = twitter["friends_timeline.json"].get
    tweets = JSON.parse(json)
    tweets.each do |tweet|
    # ...
    end

    View Slide

  35. BASIC POST
    Posting a tweet with Twitter’s API

    View Slide

  36. BASIC POST
    Posting a tweet with Twitter’s API
    require "rubygems"
    require "rest_client"
    require "json"
    !
    twitter = RestClient::Resource.new( "http://twitter.com/statuses",
    :user => "JEG2",
    :password => "secret" )
    !
    json = twitter["update.json"].post(:status => "Hello from #mwrc!")
    tweet = JSON.parse(json)
    # ...

    View Slide

  37. BASIC POST
    Posting a tweet with Twitter’s API
    require "rubygems"
    require "rest_client"
    require "json"
    !
    twitter = RestClient::Resource.new( "http://twitter.com/statuses",
    :user => "JEG2",
    :password => "secret" )
    !
    json = twitter["update.json"].post(:status => "Hello from #mwrc!")
    tweet = JSON.parse(json)
    # ...

    View Slide

  38. NETWORKING CODE
    DONE RIGHT

    View Slide

  39. View Slide

  40. def process_result(res)
    if res.code =~ /\A2\d{2}\z/
    decode res['content-encoding'], res.body if res.body
    elsif %w(301 302 303).include? res.code
    url = res.header['Location']
    !
    if url !~ /^http/
    uri = URI.parse(@url)
    uri.path = "/#{url}".squeeze('/')
    url = uri.to_s
    end
    !
    raise Redirect, url
    elsif res.code == "304"
    raise NotModified, res
    elsif res.code == "401"
    raise Unauthorized, res
    elsif res.code == "404"
    raise ResourceNotFound, res
    else
    raise RequestFailed, res
    end
    end
    def transmit(uri, req, payload)
    setup_credentials(req)
    !
    net = net_http_class.new(uri.host, uri.port)
    net.use_ssl = uri.is_a?(URI::HTTPS)
    net.verify_mode = OpenSSL::SSL::VERIFY_NONE
    net.read_timeout = @timeout if @timeout
    net.open_timeout = @open_timeout if @open_timeout
    !
    display_log request_log
    !
    net.start do |http|
    res = http.request(req, payload)
    display_log response_log(res)
    string = process_result(res)
    !
    if string or @method == :head
    Response.new(string, res)
    else
    nil
    end
    end
    rescue EOFError
    raise RestClient::ServerBrokeConnection
    rescue Timeout::Error
    raise RestClient::RequestTimeout
    end
    def decode(content_encoding, body)
    if content_encoding == 'gzip' and not body.empty?
    Zlib::GzipReader.new(StringIO.new(body)).read
    elsif content_encoding == 'deflate'
    Zlib::Inflate.new.inflate(body)
    else
    body
    end
    end

    View Slide

  41. A RESTFUL SHELL
    (IN 90 LOC)

    View Slide

  42. CURL-ISH REQUESTS
    Fetching the latest tweet from Twitter’s API

    View Slide

  43. CURL-ISH REQUESTS
    Fetching the latest tweet from Twitter’s API
    $ restclient \
    > get http://twitter.com/statuses/friends_timeline.json?count=1 \
    > JEG2 secret
    [{"text":"Sent out first round of Twitter client betas…",
    "user":{"name":"Jeremy McAnally","screen_name":"jeremymcanally",…},
    …}]

    View Slide

  44. RESTFUL IRB
    Interacting with the Twitter API

    View Slide

  45. RESTFUL IRB
    Interacting with the Twitter API
    $ restclient http://twitter.com/statuses JEG2 secret
    >> post "update.json", :status => "The RestClient shell is fun."
    => "{\"text\":\"The RestClient shell is fun.\",…}"
    >> get "friends_timeline.json?count=1"
    => "[{\"text\":\"The RestClient shell is fun.\",…}]"

    View Slide

  46. RESTFUL IRB
    Interacting with the Twitter API
    $ restclient http://twitter.com/statuses JEG2 secret
    >> post "update.json", :status => "The RestClient shell is fun."
    => "{\"text\":\"The RestClient shell is fun.\",…}"
    >> get "friends_timeline.json?count=1"
    => "[{\"text\":\"The RestClient shell is fun.\",…}]"

    View Slide

  47. RESTFUL IRB
    Interacting with the Twitter API
    $ restclient http://twitter.com/statuses JEG2 secret
    >> post "update.json", :status => "The RestClient shell is fun."
    => "{\"text\":\"The RestClient shell is fun.\",…}"
    >> get "friends_timeline.json?count=1"
    => "[{\"text\":\"The RestClient shell is fun.\",…}]"

    View Slide

  48. LOGGING IN RUBY

    View Slide

  49. GENERATING RUBY
    Interactively building a RESTful Ruby script

    View Slide

  50. GENERATING RUBY
    Interactively building a RESTful Ruby script
    $ RESTCLIENT_LOG=twitter_fun.rb restclient …
    >> post "update.json", :status => "The RestClient shell is fun."
    => …
    >> get "friends_timeline.json?count=1"
    => …

    View Slide

  51. GENERATING RUBY
    Interactively building a RESTful Ruby script
    $ RESTCLIENT_LOG=twitter_fun.rb restclient …
    >> post "update.json", :status => "The RestClient shell is fun."
    => …
    >> get "friends_timeline.json?count=1"
    => …

    View Slide

  52. GENERATING RUBY
    Interactively building a RESTful Ruby script
    $ RESTCLIENT_LOG=twitter_fun.rb restclient …
    >> post "update.json", :status => "The RestClient shell is fun."
    => …
    >> get "friends_timeline.json?count=1"
    => …
    # twitter_fun.rb
    RestClient.post "http://twitter.com/statuses/update.json",
    "status=The%20RestClient%20shell%20is%20fun.",
    :content_type=>"application/x-www-form-urlencoded"
    # => 200 OK | application/json 379 bytes
    RestClient.get "http://twitter.com/statuses/friends_timeline.json?count=1"
    # => 200 OK | application/json 381 bytes

    View Slide

  53. View Slide

  54. View Slide

  55. FASTERCSV IS
    THE NEW CSV

    View Slide

  56. THE LESS BORING
    PARTS OF CSV

    View Slide

  57. THE LESS BORING
    PARTS OF CSV
    Tricky data structures are needed

    View Slide

  58. THE LESS BORING
    PARTS OF CSV
    Tricky data structures are needed
    Rows need ordered access for their columns

    View Slide

  59. THE LESS BORING
    PARTS OF CSV
    Tricky data structures are needed
    Rows need ordered access for their columns
    Users also like to work with them by header name

    View Slide

  60. THE LESS BORING
    PARTS OF CSV
    Tricky data structures are needed
    Rows need ordered access for their columns
    Users also like to work with them by header name
    Column names often repeat

    View Slide

  61. THE LESS BORING
    PARTS OF CSV
    Tricky data structures are needed
    Rows need ordered access for their columns
    Users also like to work with them by header name
    Column names often repeat
    Now that we have m17n, CSV parses in the encoding of your
    data (no transcoding is done on your data)

    View Slide

  62. THE ARRAY-HASH-
    WITH-DUPLICATES
    DATA THING

    View Slide

  63. CSV::ROW
    The various ways to refer to data

    View Slide

  64. CSV::ROW
    The various ways to refer to data
    require "csv" # using Ruby 1.9
    !
    data = <Console,Units Sold 2007,Percent,Units Sold 2008,Percent
    Wii,"719,141",49.4%,"1,184,651",49.6%
    XBox 360,"333,084",22.9%,"743,976",31.1%
    PlayStation 3,"404,900",27.8%,"459,777",19.3%
    END_DATA
    ps3 = CSV.parse(data, :headers => true, :header_converters => :symbol)[-1]
    !
    ps3[0] # => "PlayStation 3"
    ps3[:percent] # => "27.8%"
    ps3[:percent, 3] # => "19.3%"
    ps3[:percent, ps3.index(:units_sold_2008)] # => "19.3%"

    View Slide

  65. CSV::ROW
    The various ways to refer to data
    require "csv" # using Ruby 1.9
    !
    data = <Console,Units Sold 2007,Percent,Units Sold 2008,Percent
    Wii,"719,141",49.4%,"1,184,651",49.6%
    XBox 360,"333,084",22.9%,"743,976",31.1%
    PlayStation 3,"404,900",27.8%,"459,777",19.3%
    END_DATA
    ps3 = CSV.parse(data, :headers => true, :header_converters => :symbol)[-1]
    !
    ps3[0] # => "PlayStation 3"
    ps3[:percent] # => "27.8%"
    ps3[:percent, 3] # => "19.3%"
    ps3[:percent, ps3.index(:units_sold_2008)] # => "19.3%"

    View Slide

  66. M17N IN ACTION

    View Slide

  67. View Slide

  68. @io = if data.is_a? String then StringIO.new(data) else data end
    @encoding = if @io.respond_to? :internal_encoding
    @io.internal_encoding || @io.external_encoding
    elsif @io.is_a? StringIO
    @io.string.encoding
    end
    @encoding ||= Encoding.default_internal || Encoding.default_external

    View Slide

  69. @io = if data.is_a? String then StringIO.new(data) else data end
    @encoding = if @io.respond_to? :internal_encoding
    @io.internal_encoding || @io.external_encoding
    elsif @io.is_a? StringIO
    @io.string.encoding
    end
    @encoding ||= Encoding.default_internal || Encoding.default_external

    View Slide

  70. @io = if data.is_a? String then StringIO.new(data) else data end
    @encoding = if @io.respond_to? :internal_encoding
    @io.internal_encoding || @io.external_encoding
    elsif @io.is_a? StringIO
    @io.string.encoding
    end
    @encoding ||= Encoding.default_internal || Encoding.default_external
    def encode_re(*chunks)
    Regexp.new(encode_str(*chunks))
    end
    !
    def encode_str(*chunks)
    chunks.map { |chunk| chunk.encode(@encoding.name) }.join
    end

    View Slide

  71. @io = if data.is_a? String then StringIO.new(data) else data end
    @encoding = if @io.respond_to? :internal_encoding
    @io.internal_encoding || @io.external_encoding
    elsif @io.is_a? StringIO
    @io.string.encoding
    end
    @encoding ||= Encoding.default_internal || Encoding.default_external
    def encode_re(*chunks)
    Regexp.new(encode_str(*chunks))
    end
    !
    def encode_str(*chunks)
    chunks.map { |chunk| chunk.encode(@encoding.name) }.join
    end

    View Slide

  72. @io = if data.is_a? String then StringIO.new(data) else data end
    @encoding = if @io.respond_to? :internal_encoding
    @io.internal_encoding || @io.external_encoding
    elsif @io.is_a? StringIO
    @io.string.encoding
    end
    @encoding ||= Encoding.default_internal || Encoding.default_external
    sample = read_to_char(1024)
    sample += read_to_char(1) if sample[-1..-1] == encode_str("\r") and
    not @io.eof?
    !
    if sample =~ encode_re("\r\n?|\n")
    @row_sep = $&
    break
    end
    def encode_re(*chunks)
    Regexp.new(encode_str(*chunks))
    end
    !
    def encode_str(*chunks)
    chunks.map { |chunk| chunk.encode(@encoding.name) }.join
    end

    View Slide

  73. @io = if data.is_a? String then StringIO.new(data) else data end
    @encoding = if @io.respond_to? :internal_encoding
    @io.internal_encoding || @io.external_encoding
    elsif @io.is_a? StringIO
    @io.string.encoding
    end
    @encoding ||= Encoding.default_internal || Encoding.default_external
    sample = read_to_char(1024)
    sample += read_to_char(1) if sample[-1..-1] == encode_str("\r") and
    not @io.eof?
    !
    if sample =~ encode_re("\r\n?|\n")
    @row_sep = $&
    break
    end
    def encode_re(*chunks)
    Regexp.new(encode_str(*chunks))
    end
    !
    def encode_str(*chunks)
    chunks.map { |chunk| chunk.encode(@encoding.name) }.join
    end

    View Slide

  74. OTHER POINTS
    OF INTEREST

    View Slide

  75. OTHER POINTS
    OF INTEREST
    The parser

    View Slide

  76. OTHER POINTS
    OF INTEREST
    The parser
    Ruby 1.9’s CSV library uses primarily one ugly regular
    expression from Master Regular Expressions

    View Slide

  77. OTHER POINTS
    OF INTEREST
    The parser
    Ruby 1.9’s CSV library uses primarily one ugly regular
    expression from Master Regular Expressions
    The old FasterCSV has switched to a non-regex parser to
    dodge some regex engine weaknesses

    View Slide

  78. OTHER POINTS
    OF INTEREST
    The parser
    Ruby 1.9’s CSV library uses primarily one ugly regular
    expression from Master Regular Expressions
    The old FasterCSV has switched to a non-regex parser to
    dodge some regex engine weaknesses
    FasterCSV::Table is another interesting data structure that
    can work in columns or rows

    View Slide

  79. View Slide

  80. View Slide

  81. BJ, SLAVE,
    AND TERMINATOR

    View Slide

  82. WHY THESE
    LIBRARIES?

    View Slide

  83. WHY THESE
    LIBRARIES?
    They teach how to build multiprocessing Unix software

    View Slide

  84. WHY THESE
    LIBRARIES?
    They teach how to build multiprocessing Unix software
    Covers Thread and fork(), apart and together

    View Slide

  85. WHY THESE
    LIBRARIES?
    They teach how to build multiprocessing Unix software
    Covers Thread and fork(), apart and together
    Using pipes

    View Slide

  86. WHY THESE
    LIBRARIES?
    They teach how to build multiprocessing Unix software
    Covers Thread and fork(), apart and together
    Using pipes
    Signal handling

    View Slide

  87. WHY THESE
    LIBRARIES?
    They teach how to build multiprocessing Unix software
    Covers Thread and fork(), apart and together
    Using pipes
    Signal handling
    And much more

    View Slide

  88. WHY THESE
    LIBRARIES?
    They teach how to build multiprocessing Unix software
    Covers Thread and fork(), apart and together
    Using pipes
    Signal handling
    And much more
    Robust code written by an expert

    View Slide

  89. HOW TO ASK YOUR
    CHILD TO KILL YOU

    View Slide

  90. View Slide

  91. def terminate options = {}, &block
    options = { :seconds => Float(options).to_i } unless Hash === options
    !
    seconds = getopt :seconds, options
    trap = getopt :trap, options,
    lambda{ eval("raise(::Terminator::Error, '#{ seconds }s')", block) }
    !
    handler = Signal.trap(signal, &trap)
    !
    plot_to_kill pid, :in => seconds, :with => signal
    !
    begin
    block.call
    ensure
    Signal.trap(signal, handler)
    end
    end

    View Slide

  92. def terminate options = {}, &block
    options = { :seconds => Float(options).to_i } unless Hash === options
    !
    seconds = getopt :seconds, options
    trap = getopt :trap, options,
    lambda{ eval("raise(::Terminator::Error, '#{ seconds }s')", block) }
    !
    handler = Signal.trap(signal, &trap)
    !
    plot_to_kill pid, :in => seconds, :with => signal
    !
    begin
    block.call
    ensure
    Signal.trap(signal, handler)
    end
    end

    View Slide

  93. View Slide

  94. def plot_to_kill pid, options = {}
    seconds = getopt :in, options
    signal = getopt :with, options
    process.puts [pid, seconds, signal].join(' ')
    process.flush
    end

    View Slide

  95. def plot_to_kill pid, options = {}
    seconds = getopt :in, options
    signal = getopt :with, options
    process.puts [pid, seconds, signal].join(' ')
    process.flush
    end

    View Slide

  96. def plot_to_kill pid, options = {}
    seconds = getopt :in, options
    signal = getopt :with, options
    process.puts [pid, seconds, signal].join(' ')
    process.flush
    end
    fattr :process do
    process = IO.popen "#{ ruby } #{ program.inspect }", 'w+'
    at_exit do
    begin
    Process.kill -9, process.pid
    rescue Object
    end
    end
    process.sync = true
    process
    end

    View Slide

  97. def plot_to_kill pid, options = {}
    seconds = getopt :in, options
    signal = getopt :with, options
    process.puts [pid, seconds, signal].join(' ')
    process.flush
    end
    fattr :process do
    process = IO.popen "#{ ruby } #{ program.inspect }", 'w+'
    at_exit do
    begin
    Process.kill -9, process.pid
    rescue Object
    end
    end
    process.sync = true
    process
    end

    View Slide

  98. View Slide

  99. fattr :program do
    code = <<-code
    while(( line = STDIN.gets ))
    pid, seconds, signal, *ignored = line.strip.split
    !
    pid = Float(pid).to_i
    seconds = Float(seconds)
    signal = Float(signal).to_i rescue String(signal)
    !
    sleep seconds
    !
    begin
    Process.kill signal, pid
    rescue Object
    end
    end
    code
    tmp = Tempfile.new "#{ ppid }-#{ pid }-#{ rand }"
    tmp.write code
    tmp.close
    tmp.path
    end

    View Slide

  100. fattr :program do
    code = <<-code
    while(( line = STDIN.gets ))
    pid, seconds, signal, *ignored = line.strip.split
    !
    pid = Float(pid).to_i
    seconds = Float(seconds)
    signal = Float(signal).to_i rescue String(signal)
    !
    sleep seconds
    !
    begin
    Process.kill signal, pid
    rescue Object
    end
    end
    code
    tmp = Tempfile.new "#{ ppid }-#{ pid }-#{ rand }"
    tmp.write code
    tmp.close
    tmp.path
    end

    View Slide

  101. fattr :program do
    code = <<-code
    while(( line = STDIN.gets ))
    pid, seconds, signal, *ignored = line.strip.split
    !
    pid = Float(pid).to_i
    seconds = Float(seconds)
    signal = Float(signal).to_i rescue String(signal)
    !
    sleep seconds
    !
    begin
    Process.kill signal, pid
    rescue Object
    end
    end
    code
    tmp = Tempfile.new "#{ ppid }-#{ pid }-#{ rand }"
    tmp.write code
    tmp.close
    tmp.path
    end

    View Slide

  102. fattr :program do
    code = <<-code
    while(( line = STDIN.gets ))
    pid, seconds, signal, *ignored = line.strip.split
    !
    pid = Float(pid).to_i
    seconds = Float(seconds)
    signal = Float(signal).to_i rescue String(signal)
    !
    sleep seconds
    !
    begin
    Process.kill signal, pid
    rescue Object
    end
    end
    code
    tmp = Tempfile.new "#{ ppid }-#{ pid }-#{ rand }"
    tmp.write code
    tmp.close
    tmp.path
    end

    View Slide

  103. fattr :program do
    code = <<-code
    while(( line = STDIN.gets ))
    pid, seconds, signal, *ignored = line.strip.split
    !
    pid = Float(pid).to_i
    seconds = Float(seconds)
    signal = Float(signal).to_i rescue String(signal)
    !
    sleep seconds
    !
    begin
    Process.kill signal, pid
    rescue Object
    end
    end
    code
    tmp = Tempfile.new "#{ ppid }-#{ pid }-#{ rand }"
    tmp.write code
    tmp.close
    tmp.path
    end

    View Slide

  104. fattr :program do
    code = <<-code
    while(( line = STDIN.gets ))
    pid, seconds, signal, *ignored = line.strip.split
    !
    pid = Float(pid).to_i
    seconds = Float(seconds)
    signal = Float(signal).to_i rescue String(signal)
    !
    sleep seconds
    !
    begin
    Process.kill signal, pid
    rescue Object
    end
    end
    code
    tmp = Tempfile.new "#{ ppid }-#{ pid }-#{ rand }"
    tmp.write code
    tmp.close
    tmp.path
    end

    View Slide

  105. fattr :program do
    code = <<-code
    while(( line = STDIN.gets ))
    pid, seconds, signal, *ignored = line.strip.split
    !
    pid = Float(pid).to_i
    seconds = Float(seconds)
    signal = Float(signal).to_i rescue String(signal)
    !
    sleep seconds
    !
    begin
    Process.kill signal, pid
    rescue Object
    end
    end
    code
    tmp = Tempfile.new "#{ ppid }-#{ pid }-#{ rand }"
    tmp.write code
    tmp.close
    tmp.path
    end

    View Slide

  106. OTHER POINTS
    OF INTEREST

    View Slide

  107. OTHER POINTS
    OF INTEREST
    bj – A robust background priority queue for Rails

    View Slide

  108. OTHER POINTS
    OF INTEREST
    bj – A robust background priority queue for Rails
    Noticing changes from the outside world via signals

    View Slide

  109. OTHER POINTS
    OF INTEREST
    bj – A robust background priority queue for Rails
    Noticing changes from the outside world via signals
    Managing and monitoring an external job

    View Slide

  110. OTHER POINTS
    OF INTEREST
    bj – A robust background priority queue for Rails
    Noticing changes from the outside world via signals
    Managing and monitoring an external job
    slave – Trivial multiprocessing with built-in IPC

    View Slide

  111. OTHER POINTS
    OF INTEREST
    bj – A robust background priority queue for Rails
    Noticing changes from the outside world via signals
    Managing and monitoring an external job
    slave – Trivial multiprocessing with built-in IPC
    How to set up a “heartbeat” between processes

    View Slide

  112. OTHER POINTS
    OF INTEREST
    bj – A robust background priority queue for Rails
    Noticing changes from the outside world via signals
    Managing and monitoring an external job
    slave – Trivial multiprocessing with built-in IPC
    How to set up a “heartbeat” between processes
    How to run DRb over Unix domain sockets

    View Slide

  113. View Slide

  114. View Slide

  115. THE ART OF
    CODE READING

    View Slide

  116. PROCESS TIPS

    View Slide

  117. PROCESS TIPS
    Take a deep breath and relax

    View Slide

  118. PROCESS TIPS
    Take a deep breath and relax
    Not all code sucks

    View Slide

  119. PROCESS TIPS
    Take a deep breath and relax
    Not all code sucks
    Don’t start with Rails

    View Slide

  120. PROCESS TIPS
    Take a deep breath and relax
    Not all code sucks
    Don’t start with Rails
    There’s a ton of great stuff in there

    View Slide

  121. PROCESS TIPS
    Take a deep breath and relax
    Not all code sucks
    Don’t start with Rails
    There’s a ton of great stuff in there
    But it’s a big and complex beast

    View Slide

  122. PROCESS TIPS
    Take a deep breath and relax
    Not all code sucks
    Don’t start with Rails
    There’s a ton of great stuff in there
    But it’s a big and complex beast
    Have a goal

    View Slide

  123. GETTING THE CODE

    View Slide

  124. GETTING THE CODE
    gem unpack GEM_NAME

    View Slide

  125. GETTING THE CODE
    gem unpack GEM_NAME
    Use anonymous VCS access to pull a local copy of the code

    View Slide

  126. GETTING THE CODE
    gem unpack GEM_NAME
    Use anonymous VCS access to pull a local copy of the code
    Open it in your standard environment as you would if you
    were going to edit it

    View Slide

  127. FINDING THINGS

    View Slide

  128. FINDING THINGS
    Try the conventions first

    View Slide

  129. FINDING THINGS
    Try the conventions first
    Executables are probably in the bin/ directory

    View Slide

  130. FINDING THINGS
    Try the conventions first
    Executables are probably in the bin/ directory
    Look for MyModule::MyClass in lib/my_module/
    my_class.rb

    View Slide

  131. FINDING THINGS
    Try the conventions first
    Executables are probably in the bin/ directory
    Look for MyModule::MyClass in lib/my_module/
    my_class.rb
    Look for methods in super classes and mixed in modules

    View Slide

  132. FINDING THINGS
    Try the conventions first
    Executables are probably in the bin/ directory
    Look for MyModule::MyClass in lib/my_module/
    my_class.rb
    Look for methods in super classes and mixed in modules
    But remember Ruby is quite dynamic

    View Slide

  133. FINDING THINGS
    Try the conventions first
    Executables are probably in the bin/ directory
    Look for MyModule::MyClass in lib/my_module/
    my_class.rb
    Look for methods in super classes and mixed in modules
    But remember Ruby is quite dynamic
    Hunt for some “core extensions”

    View Slide

  134. UNDERSTANDING
    THE CODE

    View Slide

  135. UNDERSTANDING
    THE CODE
    Start with the tests/specs if there are any

    View Slide

  136. UNDERSTANDING
    THE CODE
    Start with the tests/specs if there are any
    Check for an “examples/” directory

    View Slide

  137. UNDERSTANDING
    THE CODE
    Start with the tests/specs if there are any
    Check for an “examples/” directory
    Try to load and play with certain classes in isolation

    View Slide

  138. UNDERSTANDING
    THE CODE
    Start with the tests/specs if there are any
    Check for an “examples/” directory
    Try to load and play with certain classes in isolation
    irb -r a_class_to_play_with

    View Slide

  139. UNDERSTANDING
    THE CODE
    Start with the tests/specs if there are any
    Check for an “examples/” directory
    Try to load and play with certain classes in isolation
    irb -r a_class_to_play_with
    Remember Ruby’s reflection methods, like methods()

    View Slide

  140. QUESTIONS?
    About code or other important topics…

    View Slide