Slide 1

Slide 1 text

Practical Debugging

Slide 2

Slide 2 text

~/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

Slide 3

Slide 3 text

Intro • Interface • State • Flow • Tradeoffs

Slide 4

Slide 4 text

Remain calm Intro • Interface • State • Flow • Tradeoffs

Slide 5

Slide 5 text

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

Slide 6

Slide 6 text

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

Slide 7

Slide 7 text

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

Slide 8

Slide 8 text

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

Slide 9

Slide 9 text

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

Slide 10

Slide 10 text

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

Slide 11

Slide 11 text

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

Slide 12

Slide 12 text

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

Slide 13

Slide 13 text

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

Slide 14

Slide 14 text

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

Slide 15

Slide 15 text

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

Slide 16

Slide 16 text

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

Slide 17

Slide 17 text

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

Slide 18

Slide 18 text

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

Slide 19

Slide 19 text

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

Slide 20

Slide 20 text

Debugging Intro • Interface • State • Flow • Tradeoffs

Slide 21

Slide 21 text

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

Slide 22

Slide 22 text

Interface problem Intro • Interface • State • Flow • Tradeoffs

Slide 23

Slide 23 text

Interface problem State problem Intro • Interface • State • Flow • Tradeoffs

Slide 24

Slide 24 text

Interface problem State problem Flow problem Intro • Interface • State • Flow • Tradeoffs

Slide 25

Slide 25 text

Interface problems Intro • Interface • State • Flow • Tradeoffs

Slide 26

Slide 26 text

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

Slide 27

Slide 27 text

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

Slide 28

Slide 28 text

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

Slide 29

Slide 29 text

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

Slide 30

Slide 30 text

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

Slide 31

Slide 31 text

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

Slide 32

Slide 32 text

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

Slide 33

Slide 33 text

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

Slide 34

Slide 34 text

~/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

Slide 35

Slide 35 text

~/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

Slide 36

Slide 36 text

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

Slide 37

Slide 37 text

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

Slide 38

Slide 38 text

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

Slide 39

Slide 39 text

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

Slide 40

Slide 40 text

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

Slide 41

Slide 41 text

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

Slide 42

Slide 42 text

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

Slide 43

Slide 43 text

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

Slide 44

Slide 44 text

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

Slide 45

Slide 45 text

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

Slide 46

Slide 46 text

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

Slide 47

Slide 47 text

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

Slide 48

Slide 48 text

~/debugging $ bin/minesweeper ... tk-0.1.2/lib/tk.rb:4984:in `rescue in method_missing': unknown option 'mines' for # (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

Slide 49

Slide 49 text

~/debugging $ bin/minesweeper ... tk-0.1.2/lib/tk.rb:4984:in `rescue in method_missing': unknown option 'mines' for # (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

Slide 50

Slide 50 text

~/debugging $ bin/minesweeper ... tk-0.1.2/lib/tk.rb:4984:in `rescue in method_missing': unknown option 'mines' for # (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

Slide 51

Slide 51 text

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

Slide 52

Slide 52 text

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

Slide 53

Slide 53 text

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

Slide 54

Slide 54 text

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

Slide 55

Slide 55 text

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

Slide 56

Slide 56 text

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

Slide 57

Slide 57 text

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

Slide 58

Slide 58 text

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

Slide 59

Slide 59 text

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

Slide 60

Slide 60 text

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

Slide 61

Slide 61 text

~/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

Slide 62

Slide 62 text

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

Slide 63

Slide 63 text

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

Slide 64

Slide 64 text

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

Slide 65

Slide 65 text

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

Slide 66

Slide 66 text

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

Slide 67

Slide 67 text

~/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

Slide 68

Slide 68 text

~/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

Slide 69

Slide 69 text

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

Slide 70

Slide 70 text

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

Slide 71

Slide 71 text

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

Slide 72

Slide 72 text

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

Slide 73

Slide 73 text

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

Slide 74

Slide 74 text

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

Slide 75

Slide 75 text

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

Slide 76

Slide 76 text

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

Slide 77

Slide 77 text

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

Slide 78

Slide 78 text

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

Slide 79

Slide 79 text

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

Slide 80

Slide 80 text

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

Slide 81

Slide 81 text

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

Slide 82

Slide 82 text

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

Slide 83

Slide 83 text

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

Slide 84

Slide 84 text

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

Slide 85

Slide 85 text

~/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

Slide 86

Slide 86 text

~/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

Slide 87

Slide 87 text

~/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

Slide 88

Slide 88 text

~/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

Slide 89

Slide 89 text

~/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

Slide 90

Slide 90 text

~/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

Slide 91

Slide 91 text

~/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

Slide 92

Slide 92 text

~/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

Slide 93

Slide 93 text

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

Slide 94

Slide 94 text

~/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

Slide 95

Slide 95 text

~/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

Slide 96

Slide 96 text

~/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

Slide 97

Slide 97 text

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

Slide 98

Slide 98 text

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

Slide 99

Slide 99 text

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

Slide 100

Slide 100 text

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

Slide 101

Slide 101 text

~/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

Slide 102

Slide 102 text

~/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

Slide 103

Slide 103 text

~/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

Slide 104

Slide 104 text

~/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

Slide 105

Slide 105 text

class TkWindow

Slide 106

Slide 106 text

class TkWindow

Slide 107

Slide 107 text

~/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

Slide 108

Slide 108 text

~/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

Slide 109

Slide 109 text

~/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

Slide 110

Slide 110 text

~/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

Slide 111

Slide 111 text

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

Slide 112

Slide 112 text

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

Slide 113

Slide 113 text

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

Slide 114

Slide 114 text

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

Slide 115

Slide 115 text

~/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

Slide 116

Slide 116 text

~/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

Slide 117

Slide 117 text

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

Slide 118

Slide 118 text

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

Slide 119

Slide 119 text

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

Slide 120

Slide 120 text

Lessons learned Intro • Interface • State • Flow • Tradeoffs

Slide 121

Slide 121 text

Account for every return type Intro • Interface • State • Flow • Tradeoffs

Slide 122

Slide 122 text

Account for constant and method lookup Intro • Interface • State • Flow • Tradeoffs

Slide 123

Slide 123 text

Take advantage of constant and method introspection Intro • Interface • State • Flow • Tradeoffs

Slide 124

Slide 124 text

Take advantage of the fact that gems aren't bytecode Intro • Interface • State • Flow • Tradeoffs

Slide 125

Slide 125 text

State problems Intro • Interface • State • Flow • Tradeoffs

Slide 126

Slide 126 text

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

Slide 127

Slide 127 text

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

Slide 128

Slide 128 text

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

Slide 129

Slide 129 text

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

Slide 130

Slide 130 text

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

Slide 131

Slide 131 text

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

Slide 132

Slide 132 text

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

Slide 133

Slide 133 text

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

Slide 134

Slide 134 text

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

Slide 135

Slide 135 text

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

Slide 136

Slide 136 text

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

Slide 137

Slide 137 text

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

Slide 138

Slide 138 text

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

Slide 139

Slide 139 text

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

Slide 140

Slide 140 text

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

Slide 141

Slide 141 text

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

Slide 142

Slide 142 text

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

Slide 143

Slide 143 text

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

Slide 144

Slide 144 text

~/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

Slide 145

Slide 145 text

~/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

Slide 146

Slide 146 text

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

Slide 147

Slide 147 text

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

Slide 148

Slide 148 text

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

Slide 149

Slide 149 text

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

Slide 150

Slide 150 text

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

Slide 151

Slide 151 text

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

Slide 152

Slide 152 text

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

Slide 153

Slide 153 text

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

Slide 154

Slide 154 text

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

Slide 155

Slide 155 text

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

Slide 156

Slide 156 text

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

Slide 157

Slide 157 text

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

Slide 158

Slide 158 text

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

Slide 159

Slide 159 text

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

Slide 160

Slide 160 text

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

Slide 161

Slide 161 text

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

Slide 162

Slide 162 text

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

Slide 163

Slide 163 text

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

Slide 164

Slide 164 text

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

Slide 165

Slide 165 text

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

Slide 166

Slide 166 text

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

Slide 167

Slide 167 text

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

Slide 168

Slide 168 text

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

Slide 169

Slide 169 text

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

Slide 170

Slide 170 text

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

Slide 171

Slide 171 text

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

Slide 172

Slide 172 text

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

Slide 173

Slide 173 text

Lessons learned Intro • Interface • State • Flow • Tradeoffs

Slide 174

Slide 174 text

Take advantage of Ruby's quick feedback loops and flexibility. Intro • Interface • State • Flow • Tradeoffs

Slide 175

Slide 175 text

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

Slide 176

Slide 176 text

Flow problems Intro • Interface • State • Flow • Tradeoffs

Slide 177

Slide 177 text

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

Slide 178

Slide 178 text

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

Slide 179

Slide 179 text

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

Slide 180

Slide 180 text

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

Slide 181

Slide 181 text

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

Slide 182

Slide 182 text

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

Slide 183

Slide 183 text

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

Slide 184

Slide 184 text

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

Slide 185

Slide 185 text

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

Slide 186

Slide 186 text

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

Slide 187

Slide 187 text

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

Slide 188

Slide 188 text

~/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

Slide 189

Slide 189 text

~/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

Slide 190

Slide 190 text

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

Slide 191

Slide 191 text

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

Slide 192

Slide 192 text

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

Slide 193

Slide 193 text

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

Slide 194

Slide 194 text

~/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

Slide 195

Slide 195 text

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

Slide 196

Slide 196 text

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

Slide 197

Slide 197 text

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

Slide 198

Slide 198 text

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

Slide 199

Slide 199 text

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

Slide 200

Slide 200 text

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

Slide 201

Slide 201 text

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

Slide 202

Slide 202 text

~/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

Slide 203

Slide 203 text

~/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

Slide 204

Slide 204 text

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

Slide 205

Slide 205 text

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

Slide 206

Slide 206 text

~/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

Slide 207

Slide 207 text

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

Slide 208

Slide 208 text

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

Slide 209

Slide 209 text

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

Slide 210

Slide 210 text

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

Slide 211

Slide 211 text

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

Slide 212

Slide 212 text

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

Slide 213

Slide 213 text

~/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

Slide 214

Slide 214 text

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

Slide 215

Slide 215 text

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

Slide 216

Slide 216 text

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

Slide 217

Slide 217 text

Intro • Interface • State • Flow • Tradeoffs

Slide 218

Slide 218 text

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

Slide 219

Slide 219 text

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

Slide 220

Slide 220 text

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

Slide 221

Slide 221 text

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

Slide 222

Slide 222 text

Intro • Interface • State • Flow • Tradeoffs

Slide 223

Slide 223 text

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

Slide 224

Slide 224 text

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

Slide 225

Slide 225 text

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

Slide 226

Slide 226 text

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

Slide 227

Slide 227 text

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

Slide 228

Slide 228 text

~/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

Slide 229

Slide 229 text

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

Slide 230

Slide 230 text

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

Slide 231

Slide 231 text

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

Slide 232

Slide 232 text

Intro • Interface • State • Flow • Tradeoffs

Slide 233

Slide 233 text

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

Slide 234

Slide 234 text

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

Slide 235

Slide 235 text

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

Slide 236

Slide 236 text

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

Slide 237

Slide 237 text

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

Slide 238

Slide 238 text

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

Slide 239

Slide 239 text

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

Slide 240

Slide 240 text

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

Slide 241

Slide 241 text

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

Slide 242

Slide 242 text

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

Slide 243

Slide 243 text

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

Slide 244

Slide 244 text

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

Slide 245

Slide 245 text

Lessons learned Intro • Interface • State • Flow • Tradeoffs

Slide 246

Slide 246 text

For large projects, ruby requires effort to follow the flow. Intro • Interface • State • Flow • Tradeoffs

Slide 247

Slide 247 text

For new projects, stack traces are easy to generate and follow. Intro • Interface • State • Flow • Tradeoffs

Slide 248

Slide 248 text

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

Slide 249

Slide 249 text

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

Slide 250

Slide 250 text

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

Slide 251

Slide 251 text

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

Slide 252

Slide 252 text

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

Slide 253

Slide 253 text

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

Slide 254

Slide 254 text

Interface State Flow Intro • Interface • State • Flow • Tradeoffs

Slide 255

Slide 255 text

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

Slide 256

Slide 256 text

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