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

Practical Debugging

Practical Debugging

People give ruby a bad reputation for speed, efficiency, weak typing, etc. But one of the biggest benefits of an interpreted language is the ability to debug and introspect quickly without compilation. Oftentimes developers reach for heavy-handed libraries to debug their application when they could just as easily get the information they need by using tools they already have.

In this talk you will learn practical techniques to make debugging easier. You will see how simple techniques from the ruby standard library can greatly increase your ability to keep your codebase clean and bug-free.

Kevin Newton

April 25, 2017
Tweet

More Decks by Kevin Newton

Other Decks in Programming

Transcript

  1. Practical Debugging

    View full-size slide

  2. ~/debugging $ bin/minesweeper
    ... board.rb:49:in `block (2 levels) in build_cells':
    undefined method `text' for nil:NilClass (NoMethodError)
    from ... board.rb:43:in `times'
    from ... board.rb:43:in `each'
    from ... board.rb:43:in `map'
    from ... board.rb:43:in `block in build_cells'
    from ... board.rb:42:in `times'
    from ... board.rb:42:in `each'
    from ... board.rb:42:in `flat_map'
    from ... board.rb:42:in `build_cells'
    from ... board.rb:17:in `start'
    from ... minesweeper.rb:8:in `start'
    from bin/minesweeper:7:in `’
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  3. Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  4. Remain calm
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  5. What would
    StackOverflow do?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  6. What would
    StackOverflow do?
    #WWSOD
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  7. Use pry
    Use byebug
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  8. Use pry
    Use byebug
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  9. Use pry
    Use byebug
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  10. Use pry
    Use byebug
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  11. Use pry
    Use byebug
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  12. Use pry
    Use the rub
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  13. Use pry
    Use the ruby debugger
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  14. Use pry
    Use the ruby debugger
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  15. Use pry
    Use the ruby debugger
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  16. Use pry
    Use the ruby debugger
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  17. At the end of the day, people
    want bug-free code. They
    don’t care how it got that way.
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  18. You don’t get style
    points in debugging.
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  19. The ruby standard library
    has every tool you need to
    debug effectively.
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  20. Debugging
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  21. What kind of problem
    are you solving?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  22. Interface problem
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  23. Interface problem
    State problem
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  24. Interface problem
    State problem
    Flow problem
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  25. Interface problems
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  26. Interface problems occur when you
    don’t understand the dependent
    structure of methods or constants.
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  27. Why is this thing nil?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  28. Why is this thing nil?
    Why can’t I call the method I want?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  29. Why is this thing nil?
    Why can’t I call the method I want?
    What are the constants I can reference?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  30. Why is this thing nil?
    Why can’t I call the method I want?
    What are the constants I can reference?
    What can this object see and do?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  31. Why is this thing nil?
    Why can’t I call the method I want?
    What are the constants I can reference?
    What can this object see and do?
    Oh god oh god what is this gem even doing?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  32. Why is this thing nil?
    Why can’t I call the method I want?
    What are the constants I can reference?
    What can this object see and do?
    Oh god oh god what is this gem even doing?

    I can’t even.
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  33. Why is this thing nil?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  34. ~/debugging $ bin/minesweeper
    ... board.rb:57:in `[]': no implicit conversion from nil to
    integer (TypeError)
    from ... board.rb:57:in `block (3 levels) in build_cells'
    from ... board.rb:57:in `count'
    from ... board.rb:57:in `block (2 levels) in build_cells'
    from ... board.rb:44:in `times'
    from ... board.rb:44:in `each'
    from ... board.rb:44:in `map'
    from ... board.rb:44:in `block in build_cells'
    from ... board.rb:43:in `times'
    from ... board.rb:43:in `each'
    from ... board.rb:43:in `flat_map'
    from ... board.rb:43:in `build_cells'
    from ... board.rb:17:in `start'
    from ... minesweeper.rb:9:in `start'
    from bin/minesweeper:7:in `'
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  35. ~/debugging $ bin/minesweeper
    ... board.rb:57:in `[]': no implicit conversion from nil to
    integer (TypeError)
    from ... board.rb:57:in `block (3 levels) in build_cells'
    from ... board.rb:57:in `count'
    from ... board.rb:57:in `block (2 levels) in build_cells'
    from ... board.rb:44:in `times'
    from ... board.rb:44:in `each'
    from ... board.rb:44:in `map'
    from ... board.rb:44:in `block in build_cells'
    from ... board.rb:43:in `times'
    from ... board.rb:43:in `each'
    from ... board.rb:43:in `flat_map'
    from ... board.rb:43:in `build_cells'
    from ... board.rb:17:in `start'
    from ... minesweeper.rb:9:in `start'
    from bin/minesweeper:7:in `'
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  36. module MineSweeper
    class Board
    def build_cells(tk_root)
    ...
    neighbors = neighbors_for(ycoord, xcoord)
    mine_count = neighbors.count { |neighbor| mines[neighbor] }
    ...
    end
    def neighbors_for(ycoord, xcoord)
    [
    index_for(ycoord - 1, xcoord - 1),
    index_for(ycoord - 1, xcoord),
    ...
    ]
    end
    def index_for(ycoord, xcoord)
    return nil if ycoord < 0 || xcoord < 0 ||
    ycoord >= height || xcoord >= width
    ycoord * width + xcoord
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  37. module MineSweeper
    class Board
    def build_cells(tk_root)
    ...
    neighbors = neighbors_for(ycoord, xcoord)
    mine_count = neighbors.count { |neighbor| mines[neighbor] }
    ...
    end
    def neighbors_for(ycoord, xcoord)
    [
    index_for(ycoord - 1, xcoord - 1),
    index_for(ycoord - 1, xcoord),
    ...
    ]
    end
    def index_for(ycoord, xcoord)
    return nil if ycoord < 0 || xcoord < 0 ||
    ycoord >= height || xcoord >= width
    ycoord * width + xcoord
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  38. module MineSweeper
    class Board
    def build_cells(tk_root)
    ...
    neighbors = neighbors_for(ycoord, xcoord)
    mine_count = neighbors.count { |neighbor| mines[neighbor] }
    ...
    end
    def neighbors_for(ycoord, xcoord)
    [
    index_for(ycoord - 1, xcoord - 1),
    index_for(ycoord - 1, xcoord),
    ...
    ]
    end
    def index_for(ycoord, xcoord)
    return nil if ycoord < 0 || xcoord < 0 ||
    ycoord >= height || xcoord >= width
    ycoord * width + xcoord
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  39. module MineSweeper
    class Board
    def build_cells(tk_root)
    ...
    neighbors = neighbors_for(ycoord, xcoord)
    mine_count = neighbors.count { |neighbor| mines[neighbor] }
    ...
    end
    def neighbors_for(ycoord, xcoord)
    [
    index_for(ycoord - 1, xcoord - 1),
    index_for(ycoord - 1, xcoord),
    ...
    ]
    end
    def index_for(ycoord, xcoord)
    return nil if ycoord < 0 || xcoord < 0 ||
    ycoord >= height || xcoord >= width
    ycoord * width + xcoord
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  40. module MineSweeper
    class Board
    def build_cells(tk_root)
    ...
    neighbors = neighbors_for(ycoord, xcoord)
    mine_count = neighbors.count { |neighbor| mines[neighbor] }
    ...
    end
    def neighbors_for(ycoord, xcoord)
    [
    index_for(ycoord - 1, xcoord - 1),
    index_for(ycoord - 1, xcoord),
    ...
    ]
    end
    def index_for(ycoord, xcoord)
    return nil if ycoord < 0 || xcoord < 0 ||
    ycoord >= height || xcoord >= width
    ycoord * width + xcoord
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  41. module MineSweeper
    class Board
    def build_cells(tk_root)
    ...
    neighbors = neighbors_for(ycoord, xcoord)
    mine_count = neighbors.count { |neighbor| mines[neighbor] }
    ...
    end
    def neighbors_for(ycoord, xcoord)
    [
    index_for(ycoord - 1, xcoord - 1),
    index_for(ycoord - 1, xcoord),
    ...
    ]
    end
    def index_for(ycoord, xcoord)
    return nil if ycoord < 0 || xcoord < 0 ||
    ycoord >= height || xcoord >= width
    ycoord * width + xcoord
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  42. module MineSweeper
    class Board
    def build_cells(tk_root)
    ...
    neighbors = neighbors_for(ycoord, xcoord)
    mine_count = neighbors.count { |neighbor| mines[neighbor] }
    ...
    end
    def neighbors_for(ycoord, xcoord)
    [
    index_for(ycoord - 1, xcoord - 1),
    index_for(ycoord - 1, xcoord),
    ...
    ]
    end
    Option[Int] index_for(ycoord, xcoord)
    return nil if ycoord < 0 || xcoord < 0 ||
    ycoord >= height || xcoord >= width
    ycoord * width + xcoord
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  43. module MineSweeper
    class Board
    def build_cells(tk_root)
    ...
    neighbors = neighbors_for(ycoord, xcoord)
    mine_count = neighbors.count { |neighbor| mines[neighbor] }
    ...
    end
    def neighbors_for(ycoord, xcoord)
    [
    index_for(ycoord - 1, xcoord - 1),
    index_for(ycoord - 1, xcoord),
    ...
    ]
    end
    Optional index_for(ycoord, xcoord)
    return nil if ycoord < 0 || xcoord < 0 ||
    ycoord >= height || xcoord >= width
    ycoord * width + xcoord
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  44. module MineSweeper
    class Board
    def build_cells(tk_root)
    ...
    neighbors = neighbors_for(ycoord, xcoord)
    mine_count = neighbors.count { |neighbor| mines[neighbor] }
    ...
    end
    def neighbors_for(ycoord, xcoord)
    [
    index_for(ycoord - 1, xcoord - 1),
    index_for(ycoord - 1, xcoord),
    ...
    ]
    end
    Maybe Int index_for(ycoord, xcoord)
    return nil if ycoord < 0 || xcoord < 0 ||
    ycoord >= height || xcoord >= width
    ycoord * width + xcoord
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  45. module MineSweeper
    class Board
    def build_cells(tk_root)
    ...
    neighbors = neighbors_for(ycoord, xcoord)
    mine_count = neighbors.count { |neighbor| mines[neighbor] }
    ...
    end
    def neighbors_for(ycoord, xcoord)
    [
    index_for(ycoord - 1, xcoord - 1),
    index_for(ycoord - 1, xcoord),
    ...
    ]
    end
    def index_for(ycoord, xcoord)
    return nil if ycoord < 0 || xcoord < 0 ||
    ycoord >= height || xcoord >= width
    ycoord * width + xcoord
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  46. module MineSweeper
    class Board
    def build_cells(tk_root)
    ...
    neighbors = neighbors_for(ycoord, xcoord)
    mine_count = neighbors.count { |neighbor| mines[neighbor] }
    ...
    end
    def neighbors_for(ycoord, xcoord)
    [
    index_for(ycoord - 1, xcoord - 1),
    index_for(ycoord - 1, xcoord),
    ...
    ].compact
    end
    def index_for(ycoord, xcoord)
    return nil if ycoord < 0 || xcoord < 0 ||
    ycoord >= height || xcoord >= width
    ycoord * width + xcoord
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  47. Why can’t I call the
    method I want?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  48. ~/debugging $ bin/minesweeper
    ... tk-0.1.2/lib/tk.rb:4984:in `rescue in method_missing':
    unknown option 'mines' for #@path=".w00200"> (deleted widget?) (NameError)
    from ... tk-0.1.2/lib/tk.rb:4980:in `method_missing'
    from ... minesweeper/board.rb:63:in `block in
    build_status_label'
    from ... minesweeper/board.rb:62:in `new'
    from ... minesweeper/board.rb:62:in `build_status_label'
    from ... minesweeper/board.rb:18:in `start'
    from ... minesweeper.rb:8:in `start'
    from bin/minesweeper:7:in `’
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  49. ~/debugging $ bin/minesweeper
    ... tk-0.1.2/lib/tk.rb:4984:in `rescue in method_missing':
    unknown option 'mines' for #@path=".w00200"> (deleted widget?) (NameError)
    from ... tk-0.1.2/lib/tk.rb:4980:in `method_missing'
    from ... minesweeper/board.rb:63:in `block in
    build_status_label'
    from ... minesweeper/board.rb:62:in `new'
    from ... minesweeper/board.rb:62:in `build_status_label'
    from ... minesweeper/board.rb:18:in `start'
    from ... minesweeper.rb:8:in `start'
    from bin/minesweeper:7:in `’
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  50. ~/debugging $ bin/minesweeper
    ... tk-0.1.2/lib/tk.rb:4984:in `rescue in method_missing':
    unknown option 'mines' for #@path=".w00200"> (deleted widget?) (NameError)
    from ... tk-0.1.2/lib/tk.rb:4980:in `method_missing'
    from ... minesweeper/board.rb:63:in `block in
    build_status_label'
    from ... minesweeper/board.rb:62:in `new'
    from ... minesweeper/board.rb:62:in `build_status_label'
    from ... minesweeper/board.rb:18:in `start'
    from ... minesweeper.rb:8:in `start'
    from bin/minesweeper:7:in `’
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  51. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    TkLabel.new(tk_root) do
    text "#{mines} mines left"
    grid(column: 0, row: 0, columnspan: width)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  52. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    TkLabel.new(tk_root) do
    text "#{mines} mines left"
    grid(column: 0, row: 0, columnspan: width)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  53. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    TkLabel.new(tk_root) do
    p methods.grep(/mines/)
    p methods
    exit
    text "#{mines} mines left"
    grid(column: 0, row: 0, columnspan: width)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  54. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    TkLabel.new(tk_root) do
    p methods.grep(/mines/)
    p methods
    exit
    text "#{mines} mines left"
    grid(column: 0, row: 0, columnspan: width)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  55. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    TkLabel.new(tk_root) do
    p methods.grep(/mines/)
    p methods
    exit
    text "#{mines} mines left"
    grid(column: 0, row: 0, columnspan: width)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  56. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    TkLabel.new(tk_root) do
    p methods.grep(/mines/)
    puts methods.inspect
    exit
    text "#{mines} mines left"
    grid(column: 0, row: 0, columnspan: width)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  57. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    TkLabel.new(tk_root) do
    p methods.grep(/mines/)
    p methods
    exit
    text "#{mines} mines left"
    grid(column: 0, row: 0, columnspan: width)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  58. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    TkLabel.new(tk_root) do
    p methods.grep(/mines/)
    p methods
    exit
    text "#{mines} mines left"
    grid(column: 0, row: 0, columnspan: width)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  59. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    TkLabel.new(tk_root) do
    p methods.grep(/mines/)
    p methods
    exit
    text "#{mines} mines left"
    grid(column: 0, row: 0, columnspan: width)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  60. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    TkLabel.new(tk_root) do
    p methods.grep(/mines/)
    p methods
    exit
    text "#{mines} mines left"
    grid(column: 0, row: 0, columnspan: width)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  61. ~/debugging $ bin/minesweeper
    []
    [:raise, :exist?, :subcommand, :bind_class, :database
    _classname, :database_class, :pack_in, :pack, :unpack
    , :pack_configure, :pack_config, :pack_propagate, :pa
    ck_info, :pack_slaves, :grid_in, :grid_anchor, :grid_
    bbox, :grid_config, :grid_configure, :grid_columnconf
    ig, :grid_columnconfigure, :grid_rowconfig, :grid_row
    configure, :grid_columnconfiginfo, :grid_rowconfiginf
    o, :grid_column, :grid_row, :grid_info, :grid_locatio
    n, :thread_wait, :grid_propagate, :pack_forget, :grid
    _remove, :grid, :grid_slaves, :grid_forget, :ungrid,
    :place, :grid_size, :place_forget, :unplace, :place_c
    onfig, :place_configure, :set_focus, :place_in, :lowe
    r_window, :current_grab, :raise_window, ... ]
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  62. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    TkLabel.new(tk_root) do
    p methods.grep(/mines/)
    p methods
    exit
    text "#{mines} mines left"
    grid(column: 0, row: 0, columnspan: width)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  63. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    TkLabel.new(tk_root) do
    text "#{mines} mines left"
    grid(column: 0, row: 0, columnspan: width)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  64. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    initial_status = "#{mines} mines left"
    column_span = width
    TkLabel.new(tk_root) do
    text initial_status
    grid(column: 0, row: 0, columnspan: column_span)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  65. What are the constants
    I can reference?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  66. module MineSweeper
    module Cell
    class Base
    end
    end
    class Cell::Mine < Base
    end
    class Cell::Neighbor < Base
    end
    class Cell::Empty < Base
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  67. ~/debugging $ ruby examples/module_nesting_1.rb
    examples/module_nesting_1.rb:7:in
    `': uninitialized constant
    MineSweeper::Base (NameError)
    from examples/module_nesting_1.rb:1:in `'
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  68. ~/debugging $ ruby examples/module_nesting_1.rb
    examples/module_nesting_1.rb:7:in
    `': uninitialized constant
    MineSweeper::Base (NameError)
    from examples/module_nesting_1.rb:1:in `'
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  69. module MineSweeper
    module Cell
    class Base
    end
    end
    class Cell::Mine < Base
    end
    class Cell::Neighbor < Base
    end
    class Cell::Empty < Base
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  70. module MineSweeper
    module Cell
    class Base
    end
    end
    puts Module.nesting; exit
    class Cell::Mine < Base
    end
    class Cell::Neighbor < Base
    end
    class Cell::Empty < Base
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  71. ~/debugging $ ruby examples/module_nesting_1.rb
    MineSweeper
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  72. module MineSweeper
    module Cell
    class Base
    end
    end
    puts Module.nesting; exit
    class Cell::Mine < Base
    end
    class Cell::Neighbor < Base
    end
    class Cell::Empty < Base
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  73. module MineSweeper
    module Cell
    class Base
    end
    puts Module.nesting; exit
    class Mine < Base
    end
    class Neighbor < Base
    end
    class Empty < Base
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  74. module MineSweeper
    module Cell
    class Base
    end
    puts Module.nesting; exit
    class Mine < Base
    end
    class Neighbor < Base
    end
    class Empty < Base
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  75. ~/debugging $ ruby examples/module_nesting_2.rb
    MineSweeper::Cell
    MineSweeper
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  76. What can this object
    see and do?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  77. def puts_constants(constant)
    puts constant.name
    constant.constants.each do |identifier|
    child_constant = constant.const_get(identifier)
    if child_constant != constant && child_constant.is_a?(Class)
    puts_constants(child_constant)
    end
    end
    end
    puts_constants(MineSweeper)
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  78. def puts_constants(constant)
    puts constant.name
    constant.constants.each do |identifier|
    child_constant = constant.const_get(identifier)
    if child_constant != constant && child_constant.is_a?(Class)
    puts_constants(child_constant)
    end
    end
    end
    puts_constants(MineSweeper)
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  79. def puts_constants(constant)
    puts constant.name
    constant.constants.each do |identifier|
    child_constant = constant.const_get(identifier)
    if child_constant != constant && child_constant.is_a?(Class)
    puts_constants(child_constant)
    end
    end
    end
    puts_constants(MineSweeper)
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  80. def puts_constants(constant)
    puts constant.name
    constant.constants.each do |identifier|
    child_constant = constant.const_get(identifier)
    if child_constant != constant && child_constant.is_a?(Class)
    puts_constants(child_constant)
    end
    end
    end
    puts_constants(MineSweeper)
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  81. def puts_constants(constant)
    puts constant.name
    constant.constants.each do |identifier|
    child_constant = constant.const_get(identifier)
    if child_constant != constant && child_constant.is_a?(Class)
    puts_constants(child_constant)
    end
    end
    end
    puts_constants(MineSweeper)
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  82. ~/debugging $ ruby -Ilib -rminesweeper
    examples/constants.rb
    MineSweeper
    MineSweeper::Board
    MineSweeper::Cell
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  83. def puts_constants(constant)
    puts constant.name
    constant.constants.each do |identifier|
    child_constant = constant.const_get(identifier)
    if child_constant != constant && child_constant.is_a?(Class)
    puts_constants(child_constant)
    end
    end
    end
    puts_constants(MineSweeper)
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  84. def puts_constants(constant)
    puts constant.name
    constant.constants.each do |identifier|
    child_constant = constant.const_get(identifier)
    if child_constant != constant && child_constant.is_a?(Class)
    puts_constants(child_constant)
    end
    end
    end
    puts_constants(ActiveRecord)
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  85. ~/debugging $ ruby -Iactiverecord -ractive_record
    examples/constants.rb
    ActiveRecord
    ActiveRecord::LazyAttributeHash
    ActiveRecord::StatementInvalid
    ActiveRecord::MigrationProxy
    Process::Tms
    ActiveRecord::Attribute
    ActiveRecord::Base
    ActiveRecord::Store::StringKeyedHashAccessor
    ActiveRecord::Store::IndifferentHashAccessor
    ActiveRecord::Store::IndifferentCoder
    ActiveRecord::Store::HashAccessor
    ActiveRecord::Reflection::ThroughReflection
    ...
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  86. ~/debugging $ ruby -Ilib -rminesweeper -e \
    > "p MineSweeper::Board.instance_methods"
    [:status, :start, :width, :height, :mines, :cells, :click,
    :update_status, :instance_of?, :kind_of?, :is_a?, :tap, :p
    ublic_send, :remove_instance_variable, :public_method, :si
    ngleton_method, :instance_variable_set, :define_singleton_
    method, :method, :extend, :to_enum, :enum_for, :<=>, :===,
    :=~, :!
    ~, :eql?, :respond_to?, :freeze, :inspect, :object_id, :se
    nd, :display, :to_s, :nil?, :hash, :class, :singleton_clas
    s, :clone, :dup, :itself, :taint, :tainted?, :untaint, :un
    trust, :untrusted?, :trust, :frozen?, :methods, :singleton
    _methods, :protected_methods, :private_methods, ...]
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  87. ~/debugging $ ruby -Ilib -rminesweeper -e \
    > "p MineSweeper::Board.instance_methods"
    [:status, :start, :width, :height, :mines, :cells, :click,
    :update_status, :instance_of?, :kind_of?, :is_a?, :tap, :p
    ublic_send, :remove_instance_variable, :public_method, :si
    ngleton_method, :instance_variable_set, :define_singleton_
    method, :method, :extend, :to_enum, :enum_for, :<=>, :===,
    :=~, :!
    ~, :eql?, :respond_to?, :freeze, :inspect, :object_id, :se
    nd, :display, :to_s, :nil?, :hash, :class, :singleton_clas
    s, :clone, :dup, :itself, :taint, :tainted?, :untaint, :un
    trust, :untrusted?, :trust, :frozen?, :methods, :singleton
    _methods, :protected_methods, :private_methods, ...]
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  88. ~/debugging $ ruby -Ilib -rminesweeper -e \
    > "p MineSweeper::Board.instance_methods"
    [:status, :start, :width, :height, :mines, :cells, :click,
    :update_status, :instance_of?, :kind_of?, :is_a?, :tap, :p
    ublic_send, :remove_instance_variable, :public_method, :si
    ngleton_method, :instance_variable_set, :define_singleton_
    method, :method, :extend, :to_enum, :enum_for, :<=>, :===,
    :=~, :!
    ~, :eql?, :respond_to?, :freeze, :inspect, :object_id, :se
    nd, :display, :to_s, :nil?, :hash, :class, :singleton_clas
    s, :clone, :dup, :itself, :taint, :tainted?, :untaint, :un
    trust, :untrusted?, :trust, :frozen?, :methods, :singleton
    _methods, :protected_methods, :private_methods, ...]
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  89. ~/debugging $ ruby -Ilib -rminesweeper -e \
    > "p MineSweeper::Board.instance_methods"
    [:status, :start, :width, :height, :mines, :cells, :click,
    :update_status, :instance_of?, :kind_of?, :is_a?, :tap, :p
    ublic_send, :remove_instance_variable, :public_method, :si
    ngleton_method, :instance_variable_set, :define_singleton_
    method, :method, :extend, :to_enum, :enum_for, :<=>, :===,
    :=~, :!
    ~, :eql?, :respond_to?, :freeze, :inspect, :object_id, :se
    nd, :display, :to_s, :nil?, :hash, :class, :singleton_clas
    s, :clone, :dup, :itself, :taint, :tainted?, :untaint, :un
    trust, :untrusted?, :trust, :frozen?, :methods, :singleton
    _methods, :protected_methods, :private_methods, ...]
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  90. ~/debugging $ ruby -Ilib -rminesweeper -e \
    > "p MineSweeper::Board.instance_methods(false)"
    [:status, :start, :width, :height, :mines, :cells, :click,
    :update_status]
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  91. ~/debugging $ ruby -Ilib -rminesweeper -e \
    > "p MineSweeper::Board.private_instance_methods"
    [:initialize, :build_cells, :build_status_label, :index_fo
    r, :neighbors_for, :DelegateClass, :TkPack, :TkGrid, :TkPl
    ace, :sprintf, :format, :Integer, :Float, :String, :Array,
    :Hash, :fail, :iterator?, :__method__, :catch, :__dir__, :
    loop, :global_variables, :throw, :block_given?, :raise, :_
    _callee__, :eval, :Rational, :trace_var, :untrace_var, :Co
    mplex, :at_exit, :set_trace_func, :gem, :select, :caller,
    :caller_locations, :`, :test, :fork, :exit, :sleep, :respo
    nd_to_missing?, :gem_original_require, :load, :exec, :exit
    !, :system, :spawn, :abort, :syscall, :open, :printf, :pri
    nt, :putc, :puts, :gets, :readlines, :readline, ...]
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  92. ~/debugging $ ruby -Ilib -rminesweeper -e \
    > "p MineSweeper::Board.private_instance_methods(false)"
    [:initialize, :build_cells, :build_status_label, :index_fo
    r, :neighbors_for]
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  93. Oh god oh god what is
    this gem even doing?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  94. ~/debugging $ bin/minesweeper
    ... tk-0.1.2/lib/tk.rb:2054:in `_invoke': ambiguous option
    "-colum": must be -column, -columnspan, -in, -ipadx, -
    ipady, -padx, -pady, -row, -rowspan, or -sticky
    (RuntimeError)
    from ... tk-0.1.2/lib/tk.rb:2054:in `_ip_invoke_core'
    from ... tk-0.1.2/lib/tk.rb:2090:in `_tk_call_core'
    from ... tk-0.1.2/lib/tk.rb:2118:in
    `tk_call_without_enc'
    from ... tk-0.1.2/lib/tk/grid.rb:97:in `configure'
    from ... tk-0.1.2/lib/tk.rb:5301:in `grid'
    from ... minesweeper/board.rb:67:in `block in
    build_status_label'
    from ... minesweeper/board.rb:65:in `new'
    from ... minesweeper/board.rb:65:in `build_status_label'
    from ... minesweeper/board.rb:18:in `start'
    from ... minesweeper.rb:7:in `start'
    from bin/minesweeper:7:in `'
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  95. ~/debugging $ bin/minesweeper
    ... tk-0.1.2/lib/tk.rb:2054:in `_invoke': ambiguous option
    "-colum": must be -column, -columnspan, -in, -ipadx, -
    ipady, -padx, -pady, -row, -rowspan, or -sticky
    (RuntimeError)
    from ... tk-0.1.2/lib/tk.rb:2054:in `_ip_invoke_core'
    from ... tk-0.1.2/lib/tk.rb:2090:in `_tk_call_core'
    from ... tk-0.1.2/lib/tk.rb:2118:in
    `tk_call_without_enc'
    from ... tk-0.1.2/lib/tk/grid.rb:97:in `configure'
    from ... tk-0.1.2/lib/tk.rb:5301:in `grid'
    from ... minesweeper/board.rb:67:in `block in
    build_status_label'
    from ... minesweeper/board.rb:65:in `new'
    from ... minesweeper/board.rb:65:in `build_status_label'
    from ... minesweeper/board.rb:18:in `start'
    from ... minesweeper.rb:7:in `start'
    from bin/minesweeper:7:in `'
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  96. ~/debugging $ bin/minesweeper
    ... tk-0.1.2/lib/tk.rb:2054:in `_invoke': ambiguous option
    "-colum": must be -column, -columnspan, -in, -ipadx, -
    ipady, -padx, -pady, -row, -rowspan, or -sticky
    (RuntimeError)
    from ... tk-0.1.2/lib/tk.rb:2054:in `_ip_invoke_core'
    from ... tk-0.1.2/lib/tk.rb:2090:in `_tk_call_core'
    from ... tk-0.1.2/lib/tk.rb:2118:in
    `tk_call_without_enc'
    from ... tk-0.1.2/lib/tk/grid.rb:97:in `configure'
    from ... tk-0.1.2/lib/tk.rb:5301:in `grid'
    from ... minesweeper/board.rb:67:in `block in
    build_status_label'
    from ... minesweeper/board.rb:65:in `new'
    from ... minesweeper/board.rb:65:in `build_status_label'
    from ... minesweeper/board.rb:18:in `start'
    from ... minesweeper.rb:7:in `start'
    from bin/minesweeper:7:in `'
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  97. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    initial_status = "#{mines} mines left"
    column_span = width
    TkLabel.new(tk_root) do
    text initial_status
    grid(colum: 0, row: 0, columnspan: column_span)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  98. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    initial_status = "#{mines} mines left"
    column_span = width
    TkLabel.new(tk_root) do
    text initial_status
    grid(colum: 0, row: 0, columnspan: column_span)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  99. ~/debugging $ bin/console
    irb(main):001:0>
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  100. ~/debugging $ bin/console
    irb(main):001:0> method = TkLabel.instance_method(:grid)
    => #
    irb(main):002:0>
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  101. ~/debugging $ bin/console
    irb(main):001:0> method = TkLabel.instance_method(:grid)
    => #
    irb(main):002:0> method.source_location
    => ["... ruby/gems/2.4.0/gems/tk-0.1.2/lib/tk.rb", 5298]
    irb(main):003:0>
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  102. ~/debugging $ bin/console
    irb(main):001:0> method = TkLabel.instance_method(:grid)
    => #
    irb(main):002:0> method.source_location
    => ["... ruby/gems/2.4.0/gems/tk-0.1.2/lib/tk.rb", 5298]
    irb(main):003:0>
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  103. ~/debugging $ bin/console
    irb(main):001:0> method = TkLabel.instance_method(:grid)
    => #
    irb(main):002:0> method.source_location
    => ["... ruby/gems/2.4.0/gems/tk-0.1.2/lib/tk.rb", 5298]
    irb(main):003:0>
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  104. ~/debugging $ bin/console
    irb(main):001:0> method = TkLabel.instance_method(:grid)
    => #
    irb(main):002:0> method.source_location
    => ["... ruby/gems/2.4.0/gems/tk-0.1.2/lib/tk.rb", 5298]
    irb(main):003:0>
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  105. class TkWindow...
    def grid(keys = nil)
    #tk_call 'grid', epath, *hash_kv(keys)
    if keys
    TkGrid.configure(self, keys)
    else
    TkGrid.configure(self)
    end
    self
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  106. class TkWindow...
    def grid(keys = nil)
    #tk_call 'grid', epath, *hash_kv(keys)
    if keys
    TkGrid.configure(self, keys)
    else
    TkGrid.configure(self)
    end
    self
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  107. ~/debugging $ bin/console
    irb(main):001:0> method = TkLabel.instance_method(:grid)
    => #
    irb(main):002:0> method.source_location
    => ["... gems/2.4.0/gems/tk-0.1.2/lib/tk.rb", 5298]
    irb(main):003:0>
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  108. ~/debugging $ bin/console
    irb(main):001:0> method = TkLabel.instance_method(:grid)
    => #
    irb(main):002:0> method.source_location
    => ["... gems/2.4.0/gems/tk-0.1.2/lib/tk.rb", 5298]
    irb(main):003:0> method = TkGrid.method(:configure)
    => #
    irb(main):004:0>
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  109. ~/debugging $ bin/console
    irb(main):001:0> method = TkLabel.instance_method(:grid)
    => #
    irb(main):002:0> method.source_location
    => ["... gems/2.4.0/gems/tk-0.1.2/lib/tk.rb", 5298]
    irb(main):003:0> method = TkGrid.method(:configure)
    => #
    irb(main):004:0> method.source_location
    => ["... gems/2.4.0/gems/tk-0.1.2/lib/tk/grid.rb", 59]
    irb(main):005:0>
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  110. ~/debugging $ bin/console
    irb(main):001:0> method = TkLabel.instance_method(:grid)
    => #
    irb(main):002:0> method.source_location
    => ["... gems/2.4.0/gems/tk-0.1.2/lib/tk.rb", 5298]
    irb(main):003:0> method = TkGrid.method(:configure)
    => #
    irb(main):004:0> method.source_location
    => ["... gems/2.4.0/gems/tk-0.1.2/lib/tk/grid.rb", 59]
    irb(main):005:0>
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  111. module TkGrid
    ...
    def configure(*args)
    ...
    opts.each{|k, v|
    params.push("-#{k}")
    params.push(_epath(v)) # have to use 'epath' (hash_kv() is unavailable)
    }
    if Tk::TCL_MAJOR_VERSION < 8 ||
    (Tk::TCL_MAJOR_VERSION == 8 && Tk::TCL_MINOR_VERSION <= 3)
    if params[0] == '-' || params[0] == 'x' || params[0] == '^'
    tk_call_without_enc('grid', *params)
    else
    tk_call_without_enc('grid', 'configure', *params)
    end
    else
    tk_call_without_enc('grid', 'configure', *params)
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  112. module TkGrid
    ...
    def configure(*args)
    ...
    opts.each{|k, v|
    params.push("-#{k}")
    params.push(_epath(v)) # have to use 'epath' (hash_kv() is unavailable)
    }
    if Tk::TCL_MAJOR_VERSION < 8 ||
    (Tk::TCL_MAJOR_VERSION == 8 && Tk::TCL_MINOR_VERSION <= 3)
    if params[0] == '-' || params[0] == 'x' || params[0] == '^'
    tk_call_without_enc('grid', *params)
    else
    tk_call_without_enc('grid', 'configure', *params)
    end
    else
    tk_call_without_enc('grid', 'configure', *params)
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  113. module TkGrid
    ...
    def configure(*args)
    ...
    opts.each{|k, v|
    params.push("-#{k}")
    params.push(_epath(v)) # have to use 'epath' (hash_kv() is unavailable)
    }
    if Tk::TCL_MAJOR_VERSION < 8 ||
    (Tk::TCL_MAJOR_VERSION == 8 && Tk::TCL_MINOR_VERSION <= 3)
    if params[0] == '-' || params[0] == 'x' || params[0] == '^'
    tk_call_without_enc('grid', *params)
    else
    tk_call_without_enc('grid', 'configure', *params)
    end
    else
    tk_call_without_enc('grid', 'configure', *params)
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  114. module TkGrid
    ...
    def configure(*args)
    ...
    opts.each{|k, v|
    params.push("-#{k}")
    params.push(_epath(v)) # have to use 'epath' (hash_kv() is unavailable)
    }
    p params
    if Tk::TCL_MAJOR_VERSION < 8 ||
    (Tk::TCL_MAJOR_VERSION == 8 && Tk::TCL_MINOR_VERSION <= 3)
    if params[0] == '-' || params[0] == 'x' || params[0] == '^'
    tk_call_without_enc('grid', *params)
    else
    tk_call_without_enc('grid', 'configure', *params)
    end
    else
    tk_call_without_enc('grid', 'configure', *params)
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  115. ~/debugging $ bin/minesweeper
    [".w00000", "-column", 0, "-row", 1]
    [".w00001", "-column", 1, "-row", 1]
    ...
    [".w00197", "-column", 17, "-row", 10]
    [".w00198", "-column", 18, "-row", 10]
    [".w00199", "-column", 19, "-row", 10]
    [".w00200", "-colum", 0, "-row", 0, "-columnspan", 20]
    ... tk-0.1.2/lib/tk.rb:2054:in `_invoke': ambiguous
    option "-colum": must be -column, -columnspan, -in, -
    ipadx, -ipady, -padx, -pady, -row, -rowspan, or -sticky
    (RuntimeError)
    ...
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  116. ~/debugging $ bin/minesweeper
    [".w00000", "-column", 0, "-row", 1]
    [".w00001", "-column", 1, "-row", 1]
    ...
    [".w00197", "-column", 17, "-row", 10]
    [".w00198", "-column", 18, "-row", 10]
    [".w00199", "-column", 19, "-row", 10]
    [".w00200", "-colum", 0, "-row", 0, "-columnspan", 20]
    ... tk-0.1.2/lib/tk.rb:2054:in `_invoke': ambiguous
    option "-colum": must be -column, -columnspan, -in, -
    ipadx, -ipady, -padx, -pady, -row, -rowspan, or -sticky
    (RuntimeError)
    ...
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  117. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    initial_status = "#{mines} mines left"
    column_span = width
    TkLabel.new(tk_root) do
    text initial_status
    grid(colum: 0, row: 0, columnspan: column_span)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  118. module MineSweeper
    class Board
    attr_reader :width, :mines
    ...
    private
    ...
    def build_status_label(tk_root)
    initial_status = "#{mines} mines left"
    column_span = width
    TkLabel.new(tk_root) do
    text initial_status
    grid(column: 0, row: 0, columnspan: column_span)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  119. ~/debugging $ bundle exec gem pristine tk
    Restoring gems to pristine condition...
    Restored tk-0.1.2
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  120. Lessons learned

    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  121. Account for every
    return type

    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  122. Account for constant
    and method lookup

    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  123. Take advantage of constant
    and method introspection

    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  124. Take advantage of the fact
    that gems aren't bytecode

    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  125. State problems
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  126. State problems occur when the
    assumptions you made about the
    current state of the program are
    incorrect.
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  127. How does this value change at this point?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  128. How does this value change at this point?
    What has been initialized at this point?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  129. How does this value change at this point?
    What has been initialized at this point?
    How many objects are allocated in this method?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  130. How does this value
    change at this point?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  131. module MineSweeper
    class Cell
    attr_reader :button
    ...
    private
    ...
    def update(board)
    button.text = display(board).to_s
    board.update_status
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  132. module MineSweeper
    class Cell
    attr_reader :button
    ...
    private
    ...
    def update(board)
    button.text = display(board).to_s
    board.update_status
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  133. module MineSweeper
    class Cell
    attr_reader :button
    ...
    private
    ...
    def update(board)
    ivars =
    instance_variables.map do |ivar|
    [ivar, instance_variable_get(ivar)]
    end
    puts "STATE=#{ivars.to_h.inspect}"
    puts "CURRENT=#{button.text} | NEW=#{display(board).to_s}"
    button.text = display(board).to_s
    board.update_status
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  134. module MineSweeper
    class Cell
    attr_reader :button
    ...
    private
    ...
    def update(board)
    ivars =
    instance_variables.map do |ivar|
    [ivar, instance_variable_get(ivar)]
    end
    puts "STATE=#{ivars.to_h.inspect}"
    puts "CURRENT=#{button.text} | NEW=#{display(board).to_s}"
    button.text = display(board).to_s
    board.update_status
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  135. module MineSweeper
    class Cell
    attr_reader :button
    ...
    private
    ...
    def update(board)
    ivars =
    instance_variables.map do |ivar|
    [ivar, instance_variable_get(ivar)]
    end
    puts "STATE=#{ivars.to_h.inspect}"
    puts "CURRENT=#{button.text} | NEW=#{display(board).to_s}"
    button.text = display(board).to_s
    board.update_status
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  136. module MineSweeper
    class Cell
    attr_reader :button
    ...
    private
    ...
    def update(board)
    ivars =
    instance_variables.map do |ivar|
    [ivar, instance_variable_get(ivar)]
    end
    puts "STATE=#{ivars.to_h.inspect}"
    puts "CURRENT=#{button.text} | NEW=#{display(board).to_s}"
    button.text = display(board).to_s
    board.update_status
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  137. module MineSweeper
    class Cell
    attr_reader :button
    ...
    private
    ...
    def update(board)
    ivars =
    instance_variables.map do |ivar|
    [ivar, instance_variable_get(ivar)]
    end
    puts "STATE=#{ivars.to_h.inspect}"
    puts "CURRENT=#{button.text} | NEW=#{display(board).to_s}"
    button.text = display(board).to_s
    board.update_status
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  138. ~/debugging $ bin/minesweeper
    STATE={:@button=>#@path=".w00009">, :@mine_count=>1, :@mine=>false, :@neigh
    bors=>[8, 10, 28, 29, 30], :@clicked=>true}
    CURRENT= | NEW=1
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  139. ~/debugging $ bin/minesweeper
    STATE={:@button=>#@path=".w00009">, :@mine_count=>1, :@mine=>false, :@neigh
    bors=>[8, 10, 28, 29, 30], :@clicked=>true}
    CURRENT= | NEW=1
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  140. ~/debugging $ bin/minesweeper
    STATE={:@button=>#@path=".w00009">, :@mine_count=>1, :@mine=>false, :@neigh
    bors=>[8, 10, 28, 29, 30], :@clicked=>true}
    CURRENT= | NEW=1
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  141. ~/debugging $ bin/minesweeper
    STATE={:@button=>#@path=".w00009">, :@mine_count=>1, :@mine=>false, :@neigh
    bors=>[8, 10, 28, 29, 30], :@clicked=>true}
    CURRENT= | NEW=1
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  142. ~/debugging $ bin/minesweeper
    STATE={:@button=>#@path=".w00009">, :@mine_count=>1, :@mine=>false, :@neigh
    bors=>[8, 10, 28, 29, 30], :@clicked=>true}
    CURRENT= | NEW=1
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  143. What has been initialized
    at this point?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  144. ~/debugging $ bin/minesweeper
    ... board.rb:62:in `build_status_label': undefined
    method `count' for nil:NilClass (NoMethodError)
    from ... board.rb:17:in `start'
    from ... minesweeper.rb:7:in `start'
    from bin/minesweeper:7:in `'
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  145. ~/debugging $ bin/minesweeper
    ... board.rb:62:in `build_status_label': undefined
    method `count' for nil:NilClass (NoMethodError)
    from ... board.rb:17:in `start'
    from ... minesweeper.rb:7:in `start'
    from bin/minesweeper:7:in `'
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  146. module MineSweeper
    class Board
    attr_reader :width, :cells
    ...
    private
    def build_status_label(tk_root)
    initial_status = "#{cells.count(&:mines?)} mines left"
    column_span = width
    TkLabel.new(tk_root) do
    text initial_status
    grid(column: 0, row: 0, columnspan: column_span)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  147. module MineSweeper
    class Board
    attr_reader :width, :cells
    ...
    private
    def build_status_label(tk_root)
    initial_status = "#{cells.count(&:mines?)} mines left"
    column_span = width
    TkLabel.new(tk_root) do
    text initial_status
    grid(column: 0, row: 0, columnspan: column_span)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  148. module MineSweeper
    class Board
    attr_reader :width, :cells
    ...
    private
    def build_status_label(tk_root)
    require 'irb'; binding.irb
    initial_status = "#{cells.count(&:mines?)} mines left"
    column_span = width
    TkLabel.new(tk_root) do
    text initial_status
    grid(column: 0, row: 0, columnspan: column_span)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  149. module MineSweeper
    class Board
    attr_reader :width, :cells
    ...
    private
    def build_status_label(tk_root)
    require 'irb'; binding.irb
    initial_status = "#{cells.count(&:mines?)} mines left"
    column_span = width
    TkLabel.new(tk_root) do
    text initial_status
    grid(column: 0, row: 0, columnspan: column_span)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  150. module MineSweeper
    class Board
    attr_reader :width, :cells
    ...
    private
    def build_status_label(tk_root)
    require 'irb'; binding.irb
    initial_status = "#{cells.count(&:mines?)} mines left"
    column_span = width
    TkLabel.new(tk_root) do
    text initial_status
    grid(column: 0, row: 0, columnspan: column_span)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  151. ~/debugging $ bin/minesweeper
    irb(#):001:0>
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  152. ~/debugging $ bin/minesweeper
    irb(#<...>):001:0>
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  153. ~/debugging $ bin/minesweeper
    irb(#<...>):001:0> instance_variables
    => [:@width, :@height, :@mines]
    irb(#<...>):002:0>
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  154. ~/debugging $ bin/minesweeper
    irb(#<...>):001:0> instance_variables
    => [:@width, :@height, :@mines]
    irb(#<...>):002:0> cells
    => nil
    irb(#<...>):003:0>
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  155. How many objects are
    allocated in this method?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  156. module MineSweeper
    class Board
    attr_reader :mines, :cells, :status
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    cells.each(&:disable)
    'You win!'
    else
    count = mines - cells.select(&:guessed?).count
    count.to_s + ' mines left'
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  157. board = MineSweeper::Board.new(mines: 10)
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  158. Status = Struct.new(:text)
    board = MineSweeper::Board.new(mines: 10)
    board.instance_eval do
    @status = Status.new
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  159. Status = Struct.new(:text)
    board = MineSweeper::Board.new(mines: 10)
    board.instance_eval do
    @status = Status.new
    @cells = [
    MineSweeper::Cell.new(mine: true)
    ]
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  160. Status = Struct.new(:text)
    Button =
    Struct.new(:foobar) do
    def command(*) end
    def bind(*) end
    end
    board = MineSweeper::Board.new(mines: 10)
    board.instance_eval do
    @status = Status.new
    @cells = [
    MineSweeper::Cell.new(mine: true, button: Button.new)
    ]
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  161. Status = Struct.new(:text)
    Button =
    Struct.new(:foobar) do
    def command(*) end
    def bind(*) end
    end
    board = MineSweeper::Board.new(mines: 10)
    board.instance_eval do
    @status = Status.new
    @cells = [
    MineSweeper::Cell.new(mine: true, button: Button.new)
    ]
    end
    before = GC.stat[:total_allocated_objects]
    10_000.times { board.update_status }
    puts GC.stat[:total_allocated_objects] - before
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  162. Status = Struct.new(:text)
    Button =
    Struct.new(:foobar) do
    def command(*) end
    def bind(*) end
    end
    board = MineSweeper::Board.new(mines: 10)
    board.instance_eval do
    @status = Status.new
    @cells = [
    MineSweeper::Cell.new(mine: true, button: Button.new)
    ]
    end
    before = GC.stat[:total_allocated_objects]
    10_000.times { board.update_status }
    puts GC.stat[:total_allocated_objects] - before
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  163. Status = Struct.new(:text)
    Button =
    Struct.new(:foobar) do
    def command(*) end
    def bind(*) end
    end
    board = MineSweeper::Board.new(mines: 10)
    board.instance_eval do
    @status = Status.new
    @cells = [
    MineSweeper::Cell.new(mine: true, button: Button.new)
    ]
    end
    before = GC.stat[:total_allocated_objects]
    10_000.times { board.update_status }
    puts GC.stat[:total_allocated_objects] - before
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  164. Status = Struct.new(:text)
    Button =
    Struct.new(:foobar) do
    def command(*) end
    def bind(*) end
    end
    board = MineSweeper::Board.new(mines: 10)
    board.instance_eval do
    @status = Status.new
    @cells = [
    MineSweeper::Cell.new(mine: true, button: Button.new)
    ]
    end
    before = GC.stat[:total_allocated_objects]
    10_000.times { board.update_status }
    puts GC.stat[:total_allocated_objects] - before
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  165. Status = Struct.new(:text)
    Button =
    Struct.new(:foobar) do
    def command(*) end
    def bind(*) end
    end
    board = MineSweeper::Board.new(mines: 10)
    board.instance_eval do
    @status = Status.new
    @cells = [
    MineSweeper::Cell.new(mine: true, button: Button.new)
    ]
    end
    before = GC.stat[:total_allocated_objects]
    10_000.times { board.update_status }
    puts GC.stat[:total_allocated_objects] - before
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  166. Status = Struct.new(:text)
    Button =
    Struct.new(:foobar) do
    def command(*) end
    def bind(*) end
    end
    board = MineSweeper::Board.new(mines: 10)
    board.instance_eval do
    @status = Status.new
    @cells = [
    MineSweeper::Cell.new(mine: true, button: Button.new)
    ]
    end
    before = GC.stat[:total_allocated_objects]
    10_000.times { board.update_status }
    puts GC.stat[:total_allocated_objects] - before
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  167. ~/debugging $ ruby -Ilib -rminesweeper examples/gc.rb
    70001
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  168. module MineSweeper
    class Board
    attr_reader :mines, :cells, :status
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    cells.each(&:disable)
    'You win!'
    else
    count = mines - cells.select(&:guessed?).count
    count.to_s + ' mines left'
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  169. module MineSweeper
    class Board
    attr_reader :mines, :cells, :status
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    cells.each(&:disable)
    'You win!'
    else
    count = mines - cells.select(&:guessed?).count
    count.to_s + ' mines left'
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  170. module MineSweeper
    class Board
    attr_reader :mines, :cells, :status
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    cells.each(&:disable)
    'You win!'
    else
    count = mines - cells.count(&:guessed?)
    count.to_s + ' mines left'
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  171. module MineSweeper
    class Board
    attr_reader :mines, :cells, :status
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    cells.each(&:disable)
    'You win!'
    else
    count = mines - cells.count(&:guessed?)
    "#{count} mines left"
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  172. ~/debugging $ ruby -Ilib -rminesweeper examples/gc.rb
    50001
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  173. Lessons learned

    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  174. Take advantage of Ruby's
    quick feedback loops and
    flexibility.

    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  175. Your ability to see into your
    program’s state at any point is one
    of your strongest tools in debugging.

    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  176. Flow problems
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  177. Flow problems occur when you don’t
    know how the ruby interpreter got to
    or left a location in code and its
    associated state.
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  178. How did the interpreter get here?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  179. How did the interpreter get here?
    Where does the interpreter go from here?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  180. How did the interpreter get here?
    Where does the interpreter go from here?
    How did this object get here?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  181. How did the interpreter get here?
    Where does the interpreter go from here?
    How did this object get here?
    Where is this object being mutated?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  182. How did the interpreter get here?
    Where does the interpreter go from here?
    How did this object get here?
    Where is this object being mutated?
    When does this instance variable get set?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  183. How did the
    interpreter get here?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  184. module MineSweeper
    class Board
    ...
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    cells.each(&:disable)
    'You win!'
    else
    count = mines - cells.count(&:guessed?)
    "#{count} mines left"
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  185. module MineSweeper
    class Board
    ...
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    cells.each(&:disable)
    'You win!'
    else
    count = mines - cells.count(&:guessed?)
    "#{count} mines left"
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  186. module MineSweeper
    class Board
    ...
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    puts caller
    cells.each(&:disable)
    'You win!'
    else
    count = mines - cells.count(&:guessed?)
    "#{count} mines left"
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  187. module MineSweeper
    class Board
    ...
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    puts caller
    cells.each(&:disable)
    'You win!'
    else
    count = mines - cells.count(&:guessed?)
    "#{count} mines left"
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  188. ~/debugging $ bin/minesweeper
    ... cell.rb:54:in `update'
    ... cell.rb:39:in `toggle_mine'
    ... cell.rb:18:in `block in initialize'
    ... tk-0.1.2/lib/tk/event.rb:495:in `eval_cmd'
    ... tk-0.1.2/lib/tk/event.rb:495:in `block in
    install_bind_for_event_class'
    ... tk-0.1.2/lib/tk.rb:1456:in `eval_cmd'
    ... tk-0.1.2/lib/tk.rb:1456:in `cb_eval'
    ... tk-0.1.2/lib/tk.rb:1403:in `call'
    ... tk-0.1.2/lib/tk.rb:1607:in `block in callback'
    ... tk-0.1.2/lib/tk.rb:1606:in `catch'
    ... tk-0.1.2/lib/tk.rb:1606:in `callback'
    ... tk-0.1.2/lib/tk.rb:1871:in `mainloop'
    ... tk-0.1.2/lib/tk.rb:1871:in `mainloop'
    ... board.rb:19:in `start'
    ... minesweeper.rb:7:in `start'
    bin/minesweeper:7:in `'
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  189. ~/debugging $ bin/minesweeper
    ... cell.rb:54:in `update'
    ... cell.rb:39:in `toggle_mine'
    ... cell.rb:18:in `block in initialize'
    ... tk-0.1.2/lib/tk/event.rb:495:in `eval_cmd'
    ... tk-0.1.2/lib/tk/event.rb:495:in `block in
    install_bind_for_event_class'
    ... tk-0.1.2/lib/tk.rb:1456:in `eval_cmd'
    ... tk-0.1.2/lib/tk.rb:1456:in `cb_eval'
    ... tk-0.1.2/lib/tk.rb:1403:in `call'
    ... tk-0.1.2/lib/tk.rb:1607:in `block in callback'
    ... tk-0.1.2/lib/tk.rb:1606:in `catch'
    ... tk-0.1.2/lib/tk.rb:1606:in `callback'
    ... tk-0.1.2/lib/tk.rb:1871:in `mainloop'
    ... tk-0.1.2/lib/tk.rb:1871:in `mainloop'
    ... board.rb:19:in `start'
    ... minesweeper.rb:7:in `start'
    bin/minesweeper:7:in `'
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  190. module MineSweeper
    class Board
    ...
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    puts caller
    cells.each(&:disable)
    'You win!'
    else
    count = mines - cells.count(&:guessed?)
    "#{count} mines left"
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  191. module MineSweeper
    class Board
    ...
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    puts caller.grep(Regexp.new(Regexp.quote(__dir__)))
    cells.each(&:disable)
    'You win!'
    else
    count = mines - cells.count(&:guessed?)
    "#{count} mines left"
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  192. module MineSweeper
    class Board
    ...
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    puts caller.grep(Regexp.new(Regexp.quote(__dir__)))
    cells.each(&:disable)
    'You win!'
    else
    count = mines - cells.count(&:guessed?)
    "#{count} mines left"
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  193. module MineSweeper
    class Board
    ...
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    puts caller.grep(Regexp.new(Regexp.quote(__dir__)))
    cells.each(&:disable)
    'You win!'
    else
    count = mines - cells.count(&:guessed?)
    "#{count} mines left"
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  194. ~/debugging $ bin/minesweeper
    ... cell.rb:54:in `update'
    ... cell.rb:39:in `toggle_mine'
    ... cell.rb:18:in `block in initialize'
    ... board.rb:19:in `start'
    ... minesweeper.rb:7:in `start'
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  195. Where does the
    interpreter go from here?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  196. require 'tk'
    require 'minesweeper/board'
    require 'minesweeper/cell'
    module MineSweeper
    DEFAULTS = { width: 20, height: 10, mines: 10 }
    def self.start(args = {})
    Board.new(DEFAULTS.merge(args)).start
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  197. require 'tk'
    require 'minesweeper/board'
    require 'minesweeper/cell'
    module MineSweeper
    DEFAULTS = { width: 20, height: 10, mines: 10 }
    def self.start(args = {})
    TracePoint.new(:call) do |tp|
    puts "#{tp.path}:#{tp.lineno}"
    end.enable
    Board.new(DEFAULTS.merge(args)).start
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  198. require 'tk'
    require 'minesweeper/board'
    require 'minesweeper/cell'
    module MineSweeper
    DEFAULTS = { width: 20, height: 10, mines: 10 }
    def self.start(args = {})
    TracePoint.new(:call) do |tp|
    puts "#{tp.path}:#{tp.lineno}"
    end.enable
    Board.new(DEFAULTS.merge(args)).start
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  199. require 'tk'
    require 'minesweeper/board'
    require 'minesweeper/cell'
    module MineSweeper
    DEFAULTS = { width: 20, height: 10, mines: 10 }
    def self.start(args = {})
    TracePoint.new(:call) do |tp|
    puts "#{tp.path}:#{tp.lineno}"
    end.enable
    Board.new(DEFAULTS.merge(args)).start
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  200. require 'tk'
    require 'minesweeper/board'
    require 'minesweeper/cell'
    module MineSweeper
    DEFAULTS = { width: 20, height: 10, mines: 10 }
    def self.start(args = {})
    TracePoint.new(:call) do |tp|
    puts "#{tp.path}:#{tp.lineno}"
    end.enable
    Board.new(DEFAULTS.merge(args)).start
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  201. require 'tk'
    require 'minesweeper/board'
    require 'minesweeper/cell'
    module MineSweeper
    DEFAULTS = { width: 20, height: 10, mines: 10 }
    def self.start(args = {})
    TracePoint.new(:call) do |tp|
    puts "#{tp.path}:#{tp.lineno}"
    end.enable
    Board.new(DEFAULTS.merge(args)).start
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  202. ~/debugging $ bin/minesweeper
    ... board.rb:5
    ... board.rb:15
    ... ruby/2.4.0/rubygems/core_ext/kernel_require.rb:39
    ... ruby/2.4.0/monitor.rb:185
    ... ruby/2.4.0/rubygems.rb:1240
    ... ruby/2.4.0/rubygems.rb:1003
    ... ruby/2.4.0/rubygems/specification.rb:1299
    ... ruby/2.4.0/monitor.rb:197
    ... ruby/2.4.0/monitor.rb:247
    ... ruby/2.4.0/rubygems/core_ext/kernel_require.rb:39
    ... ruby/2.4.0/monitor.rb:185
    ... ruby/2.4.0/rubygems.rb:1240
    ... ruby/2.4.0/rubygems.rb:1003
    ... ruby/2.4.0/rubygems/specification.rb:1299
    ...
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  203. ~/debugging $ bin/minesweeper
    ... board.rb:5
    ... board.rb:15
    ... ruby/2.4.0/rubygems/core_ext/kernel_require.rb:39
    ... ruby/2.4.0/monitor.rb:185
    ... ruby/2.4.0/rubygems.rb:1240
    ... ruby/2.4.0/rubygems.rb:1003
    ... ruby/2.4.0/rubygems/specification.rb:1299
    ... ruby/2.4.0/monitor.rb:197
    ... ruby/2.4.0/monitor.rb:247
    ... ruby/2.4.0/rubygems/core_ext/kernel_require.rb:39
    ... ruby/2.4.0/monitor.rb:185
    ... ruby/2.4.0/rubygems.rb:1240
    ... ruby/2.4.0/rubygems.rb:1003
    ... ruby/2.4.0/rubygems/specification.rb:1299
    ...
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  204. require 'tk'
    require 'minesweeper/board'
    require 'minesweeper/cell'
    module MineSweeper
    DEFAULTS = { width: 20, height: 10, mines: 10 }
    def self.start(args = {})
    TracePoint.new(:call) do |tp|
    puts "#{tp.path}:#{tp.lineno}"
    end.enable
    Board.new(DEFAULTS.merge(args)).start
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  205. require 'tk'
    require 'minesweeper/board'
    require 'minesweeper/cell'
    module MineSweeper
    DEFAULTS = { width: 20, height: 10, mines: 10 }
    def self.start(args = {})
    TracePoint.new(:call) do |tp|
    puts "#{tp.path}:#{tp.lineno}" if tp.path.include?(__dir__)
    end.enable
    Board.new(DEFAULTS.merge(args)).start
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  206. ~/debugging $ bin/minesweeper
    ... board.rb:5
    ... board.rb:15
    ... board.rb:40
    ... board.rb:86
    ... board.rb:73
    ... board.rb:86
    ... board.rb:86
    ... board.rb:86
    ... board.rb:86
    ... board.rb:86
    ... board.rb:86
    ... board.rb:86
    ... board.rb:86
    ... cell.rb:10
    ... board.rb:86
    ... board.rb:73
    ...
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  207. How did this object get
    here?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  208. module MineSweeper
    class Cell
    ...
    def initialize(args = {})
    @button = args[:button]
    @mine_count = args[:mine_count]
    @mine = args[:mine]
    @neighbors = args[:neighbors]
    board = args[:board]
    @button.command(-> { click(board) })
    @button.bind('ButtonRelease-2', -> { toggle_mine(board) })
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  209. module MineSweeper
    class Cell
    ...
    def initialize(args = {})
    @button = args[:button]
    @mine_count = args[:mine_count]
    @mine = args[:mine]
    @neighbors = args[:neighbors]
    board = args[:board]
    @button.command(-> { click(board) })
    @button.bind('ButtonRelease-2', -> { toggle_mine(board) })
    sourcefile = ObjectSpace.allocation_sourcefile(@button)
    sourceline = ObjectSpace.allocation_sourceline(@button)
    puts "#{sourcefile}:#{sourceline}"
    end
    end
    end
    require 'objspace'
    ObjectSpace.trace_object_allocations_start
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  210. module MineSweeper
    class Cell
    ...
    def initialize(args = {})
    @button = args[:button]
    @mine_count = args[:mine_count]
    @mine = args[:mine]
    @neighbors = args[:neighbors]
    board = args[:board]
    @button.command(-> { click(board) })
    @button.bind('ButtonRelease-2', -> { toggle_mine(board) })
    sourcefile = ObjectSpace.allocation_sourcefile(@button)
    sourceline = ObjectSpace.allocation_sourceline(@button)
    puts "#{sourcefile}:#{sourceline}"
    end
    end
    end
    require 'objspace'
    ObjectSpace.trace_object_allocations_start
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  211. module MineSweeper
    class Cell
    ...
    def initialize(args = {})
    @button = args[:button]
    @mine_count = args[:mine_count]
    @mine = args[:mine]
    @neighbors = args[:neighbors]
    board = args[:board]
    @button.command(-> { click(board) })
    @button.bind('ButtonRelease-2', -> { toggle_mine(board) })
    sourcefile = ObjectSpace.allocation_sourcefile(@button)
    sourceline = ObjectSpace.allocation_sourceline(@button)
    puts "#{sourcefile}:#{sourceline}"
    end
    end
    end
    require 'objspace'
    ObjectSpace.trace_object_allocations_start
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  212. module MineSweeper
    class Cell
    ...
    def initialize(args = {})
    @button = args[:button]
    @mine_count = args[:mine_count]
    @mine = args[:mine]
    @neighbors = args[:neighbors]
    board = args[:board]
    @button.command(-> { click(board) })
    @button.bind('ButtonRelease-2', -> { toggle_mine(board) })
    sourcefile = ObjectSpace.allocation_sourcefile(@button)
    sourceline = ObjectSpace.allocation_sourceline(@button)
    puts "#{sourcefile}:#{sourceline}"
    end
    end
    end
    require 'objspace'
    ObjectSpace.trace_object_allocations_start
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  213. ~/debugging $ bin/minesweeper
    ... board.rb:48
    ... board.rb:48
    ... board.rb:48
    ... board.rb:48
    ... board.rb:48
    ... board.rb:48
    ...
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  214. module MineSweeper
    class Board
    def build_cells(tk_root)
    ...
    height.times.flat_map do |ycoord|
    width.times.map do |xcoord|
    index = index_for(ycoord, xcoord)
    neighbors = neighbors_for(ycoord, xcoord)
    button =
    TkButton.new(tk_root) do
    grid(column: xcoord, row: ycoord + 1)
    end
    ...
    end
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  215. module MineSweeper
    class Board
    def build_cells(tk_root)
    ...
    height.times.flat_map do |ycoord|
    width.times.map do |xcoord|
    index = index_for(ycoord, xcoord)
    neighbors = neighbors_for(ycoord, xcoord)
    button =
    TkButton.new(tk_root) do
    grid(column: xcoord, row: ycoord + 1)
    end
    ...
    end
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  216. Where is this object being
    mutated?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  217. Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  218. module MineSweeper
    class Board
    attr_reader :cells
    def click(index)
    cells[index].click(self)
    end
    def start
    tk_root = TkRoot.new { title 'MineSweeper' }
    @cells = build_cells(tk_root)
    @status = build_status_label(tk_root)
    Tk.mainloop
    end
    ...
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  219. module MineSweeper
    class Board
    attr_reader :cells
    def click(index)
    cells[index].click(self)
    end
    def start
    tk_root = TkRoot.new { title 'MineSweeper' }
    @cells = build_cells(tk_root)
    @status = build_status_label(tk_root)
    Tk.mainloop
    end
    ...
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  220. module MineSweeper
    class Board
    attr_reader :cells
    def click(index)
    cells[index].click(self)
    end
    def start
    tk_root = TkRoot.new { title 'MineSweeper' }
    @cells = build_cells(tk_root)
    @status = build_status_label(tk_root)
    Tk.mainloop
    end
    ...
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  221. module MineSweeper
    class Board
    attr_reader :cells
    def click(index)
    cells[index].click(self)
    end
    def start
    tk_root = TkRoot.new { title 'MineSweeper' }
    @cells = build_cells(tk_root)
    @cells.freeze
    @status = build_status_label(tk_root)
    Tk.mainloop
    end
    ...
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  222. Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  223. module MineSweeper
    class Board
    attr_reader :cells
    ...
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    cells.each(&:disable)
    'You win!'
    else
    cells.select!(&:guessed?)
    count = mines - cells.count
    "#{count} mines left"
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  224. module MineSweeper
    class Board
    attr_reader :cells
    ...
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    cells.each(&:disable)
    'You win!'
    else
    cells.select!(&:guessed?)
    count = mines - cells.count
    "#{count} mines left"
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  225. module MineSweeper
    class Board
    attr_reader :cells
    ...
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    cells.each(&:disable)
    'You win!'
    else
    count = mines - cells.count(&:guessed?)
    "#{count} mines left"
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  226. module MineSweeper
    class Board
    attr_reader :cells
    ...
    def update_status
    status.text =
    if cells.any? { |cell| cell.mine? && cell.clicked? }
    cells.each(&:disable)
    'You lose!'
    elsif cells.all? { |cell| !cell.mine? ||
    (cell.mine? && cell.guessed?) }
    cells.each(&:disable)
    'You win!'
    else
    count = mines - cells.count(&:guessed?)
    "#{count} mines left"
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  227. When does this instance
    variable get set?
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  228. ~/debugging $ bin/minesweeper
    ... board.rb:62:in `build_status_label': undefined
    method `count' for nil:NilClass (NoMethodError)
    from ... board.rb:17:in `start'
    from ... minesweeper.rb:7:in `start'
    from bin/minesweeper:7:in `'
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  229. module MineSweeper
    class Board
    attr_reader :cells
    def build_status_label(tk_root)
    require 'irb'; binding.irb
    initial_status = "#{cells.count(&:mines?)} mines left"
    column_span = width
    TkLabel.new(tk_root) do
    text initial_status
    grid(column: 0, row: 0, columnspan: column_span)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  230. module MineSweeper
    class Board
    attr_reader :cells
    def build_status_label(tk_root)
    initial_status = "#{cells.count(&:mines?)} mines left"
    column_span = width
    TkLabel.new(tk_root) do
    text initial_status
    grid(column: 0, row: 0, columnspan: column_span)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  231. module MineSweeper
    class Board
    attr_reader :cells
    def build_status_label(tk_root)
    @cells = []
    initial_status = "#{cells.count(&:mines?)} mines left"
    column_span = width
    TkLabel.new(tk_root) do
    text initial_status
    grid(column: 0, row: 0, columnspan: column_span)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  232. Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  233. module MineSweeper
    class Board
    attr_reader :cells
    def build_status_label(tk_root)
    @cells = []
    initial_status = "#{cells.count(&:mines?)} mines left"
    column_span = width
    TkLabel.new(tk_root) do
    text initial_status
    grid(column: 0, row: 0, columnspan: column_span)
    end
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  234. module MineSweeper
    class Board
    attr_reader :cells
    def build_status_label(tk_root)
    cells = @cells = []
    TracePoint.new(:line) do |tp|
    if (tp.path == __FILE__ &&
    tp.binding.eval('self.class') == self.class &&
    tp.binding.eval('@cells') != cells)
    puts "#{tp.path}:#{tp.lineno - 1}"
    exit
    end
    end.enable
    initial_status = "#{cells.count(&:mines?)} mines left”
    ...
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  235. module MineSweeper
    class Board
    attr_reader :cells
    def build_status_label(tk_root)
    cells = @cells = []
    TracePoint.new(:line) do |tp|
    if (tp.path == __FILE__ &&
    tp.binding.eval('self.class') == self.class &&
    tp.binding.eval('@cells') != cells)
    puts "#{tp.path}:#{tp.lineno - 1}"
    exit
    end
    end.enable
    initial_status = "#{cells.count(&:mines?)} mines left”
    ...
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  236. module MineSweeper
    class Board
    attr_reader :cells
    def build_status_label(tk_root)
    cells = @cells = []
    TracePoint.new(:line) do |tp|
    if (tp.path == __FILE__ &&
    tp.binding.eval('self.class') == self.class &&
    tp.binding.eval('@cells') != cells)
    puts "#{tp.path}:#{tp.lineno - 1}"
    exit
    end
    end.enable
    initial_status = "#{cells.count(&:mines?)} mines left”
    ...
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  237. module MineSweeper
    class Board
    attr_reader :cells
    def build_status_label(tk_root)
    cells = @cells = []
    TracePoint.new(:line) do |tp|
    if (tp.path == __FILE__ &&
    tp.binding.eval('self.class') == self.class &&
    tp.binding.eval('@cells') != cells)
    puts "#{tp.path}:#{tp.lineno - 1}"
    exit
    end
    end.enable
    initial_status = "#{cells.count(&:mines?)} mines left”
    ...
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  238. module MineSweeper
    class Board
    attr_reader :cells
    def build_status_label(tk_root)
    cells = @cells = []
    TracePoint.new(:line) do |tp|
    if (tp.path == __FILE__ &&
    tp.binding.eval('self.class') == self.class &&
    tp.binding.eval('@cells') != cells)
    puts "#{tp.path}:#{tp.lineno - 1}"
    exit
    end
    end.enable
    initial_status = "#{cells.count(&:mines?)} mines left”
    ...
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  239. module MineSweeper
    class Board
    attr_reader :cells
    def build_status_label(tk_root)
    cells = @cells = []
    TracePoint.new(:line) do |tp|
    if (tp.path == __FILE__ &&
    tp.binding.eval('self.class') == self.class &&
    tp.binding.eval('@cells') != cells)
    puts "#{tp.path}:#{tp.lineno - 1}"
    exit
    end
    end.enable
    initial_status = "#{cells.count(&:mines?)} mines left”
    ...
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  240. ~/debugging $ bin/minesweeper
    ... board.rb:18
    ~/debugging $
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  241. module MineSweeper
    class Board
    attr_reader :cells, :status
    ...
    def start
    tk_root = TkRoot.new { title 'MineSweeper' }
    @status = build_status_label(tk_root)
    @cells = build_cells(tk_root)
    Tk.mainloop
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  242. module MineSweeper
    class Board
    attr_reader :cells, :status
    ...
    def start
    tk_root = TkRoot.new { title 'MineSweeper' }
    @status = build_status_label(tk_root)
    @cells = build_cells(tk_root)
    Tk.mainloop
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  243. module MineSweeper
    class Board
    attr_reader :cells, :status
    ...
    def start
    tk_root = TkRoot.new { title 'MineSweeper' }
    @status = build_status_label(tk_root)
    @cells = build_cells(tk_root)
    Tk.mainloop
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  244. module MineSweeper
    class Board
    attr_reader :cells, :status
    ...
    def start
    tk_root = TkRoot.new { title 'MineSweeper' }
    @cells = build_cells(tk_root)
    @status = build_status_label(tk_root)
    Tk.mainloop
    end
    end
    end
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  245. Lessons learned

    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  246. For large projects, ruby
    requires effort to follow the
    flow.

    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  247. For new projects, stack
    traces are easy to generate
    and follow.

    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  248. Ruby's tradeoffs
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  249. Compiler niceties for introspection
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  250. Compiler niceties for introspection
    Out of the box speed for flexibility
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  251. Compiler niceties for introspection
    Out of the box speed for flexibility
    Maintenance cost for start-up cost
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  252. Interface
    Out of the box speed for flexibility
    Maintenance cost for start-up cost
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  253. Interface
    State
    Maintenance cost for start-up cost
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  254. Interface
    State
    Flow
    Intro • Interface • State • Flow • Tradeoffs

    View full-size slide

  255. Practical Debugging
    github.com/kddeisz/debugging
    Kevin Deisz
    @kddeisz

    View full-size slide

  256. A Comprehensive Guide to Debugging Rails

    https://www.jackkinsella.ie/articles/a-comprehensive-guide-to-debugging-rails
    Debugging memory leaks in Ruby

    https://samsaffron.com/archive/2015/03/31/debugging-memory-leaks-in-ruby
    Debugging Rails Applications

    http://guides.rubyonrails.org/debugging_rails_applications.html
    Debugging Rails Applications in Development

    http://nofail.de/2013/10/debugging-rails-applications-in-development/
    I am a puts debuggerer

    https://tenderlovemaking.com/2016/02/05/i-am-a-puts-debuggerer.html
    Ruby Debugging Magic Cheat Sheet

    http://www.schneems.com/2016/01/25/ruby-debugging-magic-cheat-sheet.html
    There’s More to Ruby Debugging Than puts()

    https://engineering.shopify.com/17489080-theres-more-to-ruby-debugging-
    than-puts
    References

    View full-size slide