Slide 1

Slide 1 text

May 2018 @sandimetz Sandi Metz Polly want a Message

Slide 2

Slide 2 text

@sandimetz May 2018 Path to Pain

Slide 3

Slide 3 text

@sandimetz May 2018 Design Stamina Hypothesis - Martin Fowler

Slide 4

Slide 4 text

@sandimetz May 2018 #1 Design Stamina Hypothesis - Martin Fowler

Slide 5

Slide 5 text

@sandimetz May 2018

Slide 6

Slide 6 text

@sandimetz May 2018

Slide 7

Slide 7 text

@sandimetz May 2018 Over Design

Slide 8

Slide 8 text

@sandimetz May 2018

Slide 9

Slide 9 text

@sandimetz May 2018

Slide 10

Slide 10 text

@sandimetz May 2018 Under Design

Slide 11

Slide 11 text

@sandimetz May 2018

Slide 12

Slide 12 text

@sandimetz May 2018

Slide 13

Slide 13 text

@sandimetz May 2018

Slide 14

Slide 14 text

@sandimetz May 2018

Slide 15

Slide 15 text

@sandimetz May 2018

Slide 16

Slide 16 text

@sandimetz May 2018 ???

Slide 17

Slide 17 text

@sandimetz May 2018

Slide 18

Slide 18 text

@sandimetz May 2018

Slide 19

Slide 19 text

@sandimetz May 2018 Ouch

Slide 20

Slide 20 text

@sandimetz May 2018 Procedures vs OO -Me

Slide 21

Slide 21 text

@sandimetz May 2018 #2 Procedures vs OO -Me

Slide 22

Slide 22 text

@sandimetz May 2018

Slide 23

Slide 23 text

@sandimetz May 2018

Slide 24

Slide 24 text

@sandimetz May 2018

Slide 25

Slide 25 text

@sandimetz May 2018

Slide 26

Slide 26 text

@sandimetz May 2018

Slide 27

Slide 27 text

@sandimetz May 2018

Slide 28

Slide 28 text

@sandimetz May 2018 Ouch

Slide 29

Slide 29 text

@sandimetz May 2018 Churn vs. Complexity - Michael Feathers

Slide 30

Slide 30 text

@sandimetz May 2018 #3 Churn vs. Complexity - Michael Feathers

Slide 31

Slide 31 text

@sandimetz May 2018

Slide 32

Slide 32 text

@sandimetz May 2018 Ouch

Slide 33

Slide 33 text

@sandimetz May 2018 What matters, suffers -Me

Slide 34

Slide 34 text

@sandimetz May 2018 #4 What matters, suffers -Me

Slide 35

Slide 35 text

@sandimetz May 2018

Slide 36

Slide 36 text

@sandimetz May 2018 GPA 1.8

Slide 37

Slide 37 text

@sandimetz May 2018 GPA 1.8

Slide 38

Slide 38 text

@sandimetz May 2018 GPA 1.8

Slide 39

Slide 39 text

@sandimetz May 2018

Slide 40

Slide 40 text

@sandimetz May 2018 GPA 2.58

Slide 41

Slide 41 text

@sandimetz May 2018 GPA 2.58

Slide 42

Slide 42 text

@sandimetz May 2018 GPA 2.58

Slide 43

Slide 43 text

@sandimetz May 2018

Slide 44

Slide 44 text

@sandimetz May 2018 GPA 2.97

Slide 45

Slide 45 text

@sandimetz May 2018 GPA 2.97

Slide 46

Slide 46 text

@sandimetz May 2018 GPA 2.97

Slide 47

Slide 47 text

@sandimetz May 2018

Slide 48

Slide 48 text

@sandimetz May 2018 #1 Design Stamina Hypothesis

Slide 49

Slide 49 text

@sandimetz May 2018 #1 Design Stamina Hypothesis #2 Procedures vs OO

Slide 50

Slide 50 text

@sandimetz May 2018 #1 Design Stamina Hypothesis #2 Procedures vs OO #3 Churn vs Complexity

Slide 51

Slide 51 text

@sandimetz May 2018 #1 Design Stamina Hypothesis #2 Procedures vs OO #3 Churn vs Complexity #4 What matters, suffers

Slide 52

Slide 52 text

@sandimetz May 2018 Interlude

Slide 53

Slide 53 text

@sandimetz May 2018 Wherein things go badly wrong Interlude

Slide 54

Slide 54 text

@sandimetz May 2018 File.read("/path/to/filename").split("\n")

Slide 55

Slide 55 text

@sandimetz May 2018 Change #1

Slide 56

Slide 56 text

@sandimetz May 2018 Change #1 Read from Git Tag

Slide 57

Slide 57 text

@sandimetz May 2018 class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end

Slide 58

Slide 58 text

@sandimetz May 2018 class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end

Slide 59

Slide 59 text

@sandimetz May 2018 class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end

Slide 60

Slide 60 text

@sandimetz May 2018 class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end

Slide 61

Slide 61 text

@sandimetz May 2018 if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end

Slide 62

Slide 62 text

@sandimetz May 2018 if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end

Slide 63

Slide 63 text

@sandimetz May 2018 if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end

Slide 64

Slide 64 text

@sandimetz May 2018 def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end

Slide 65

Slide 65 text

@sandimetz May 2018 class Listing attr_reader :filename, :repository, :tag, :git_cmd def initialize(filename:, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end end

Slide 66

Slide 66 text

@sandimetz May 2018 class Listing attr_reader :filename, :repository, :tag, :git_cmd def initialize(filename:, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end end

Slide 67

Slide 67 text

@sandimetz May 2018 class Listing attr_reader :filename, :repository, :tag, :git_cmd def initialize(filename:, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end end

Slide 68

Slide 68 text

@sandimetz May 2018 class Listing attr_reader :filename, :repository, :tag, :git_cmd def initialize(filename:, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end end

Slide 69

Slide 69 text

@sandimetz May 2018 class Listing attr_reader :filename, :repository, :tag, :git_cmd def initialize(filename:, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end end

Slide 70

Slide 70 text

@sandimetz May 2018 Change #2

Slide 71

Slide 71 text

@sandimetz May 2018 Change #2 Partial listings

Slide 72

Slide 72 text

@sandimetz May 2018 Change #2 Partial listings "1, 3-4, 15, 37-50"

Slide 73

Slide 73 text

@sandimetz May 2018 Easy is the enemy of simple

Slide 74

Slide 74 text

@sandimetz May 2018 class Listing attr_reader :filename, :repository, :tag, :git_cmd def initialize(filename:, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end # ... end

Slide 75

Slide 75 text

@sandimetz May 2018 class Listing attr_reader :filename, :repository, :tag, :git_cmd def initialize(filename:, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end # ... end

Slide 76

Slide 76 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end # ... end

Slide 77

Slide 77 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end # ... end

Slide 78

Slide 78 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end # ... end

Slide 79

Slide 79 text

@sandimetz May 2018 class Listing # ...

Slide 80

Slide 80 text

@sandimetz May 2018 class Listing # ... def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end

Slide 81

Slide 81 text

@sandimetz May 2018 class Listing # ... def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end New Conditional Here

Slide 82

Slide 82 text

@sandimetz May 2018 class Listing # ... def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end

Slide 83

Slide 83 text

@sandimetz May 2018 class Listing # ... def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end

Slide 84

Slide 84 text

@sandimetz May 2018 class Listing # ... def lines if git_cmd git_lines else File.read(filename).split("\n") end end def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end

Slide 85

Slide 85 text

@sandimetz May 2018 class Listing # ... def lines if git_cmd git_lines else File.read(filename).split("\n") end end

Slide 86

Slide 86 text

@sandimetz May 2018 class Listing # ... def lines if git_cmd git_lines else File.read(filename).split("\n") end end

Slide 87

Slide 87 text

@sandimetz May 2018 class Listing # ... def lines if git_cmd git_lines else File.read(filename).split("\n") end end

Slide 88

Slide 88 text

@sandimetz May 2018 class Listing # ... def lines if git_cmd git_lines else File.read(filename).split("\n") end end def file_lines File.read(filename).split("\n") end

Slide 89

Slide 89 text

@sandimetz May 2018 class Listing # ... def lines if git_cmd git_lines else file_lines end end def file_lines File.read(filename).split("\n") end

Slide 90

Slide 90 text

@sandimetz May 2018 class Listing # ... def lines if git_cmd git_lines else file_lines end end

Slide 91

Slide 91 text

@sandimetz May 2018 class Listing # ... def lines all_lines = if git_cmd git_lines else file_lines end end

Slide 92

Slide 92 text

@sandimetz May 2018 class Listing # ... def lines all_lines = if git_cmd git_lines else file_lines end end

Slide 93

Slide 93 text

@sandimetz May 2018 class Listing # ... def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end

Slide 94

Slide 94 text

@sandimetz May 2018 class Listing # ... def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end

Slide 95

Slide 95 text

@sandimetz May 2018 # "1, 3-4, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end

Slide 96

Slide 96 text

@sandimetz May 2018 # "1, 3-4, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end

Slide 97

Slide 97 text

@sandimetz May 2018 # "1, 3-4, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end

Slide 98

Slide 98 text

@sandimetz May 2018 # "1, 3-4, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end

Slide 99

Slide 99 text

@sandimetz May 2018 # "1, 3-4, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end

Slide 100

Slide 100 text

@sandimetz May 2018 # "1, 3-4, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end Ouch

Slide 101

Slide 101 text

@sandimetz May 2018 # "1, 3-4, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end

Slide 102

Slide 102 text

@sandimetz May 2018 Change #3

Slide 103

Slide 103 text

@sandimetz May 2018 Change #3 Dynamic Comments

Slide 104

Slide 104 text

@sandimetz May 2018 Change #3 Dynamic Comments "1, 3-4, 15, 37-50"

Slide 105

Slide 105 text

@sandimetz May 2018 Change #3 Dynamic Comments "1, 3-4, 15, 37-50"

Slide 106

Slide 106 text

@sandimetz May 2018 Change #3 Dynamic Comments "1, #4, 3-4, #6, 15, 37-50"

Slide 107

Slide 107 text

@sandimetz May 2018 # "1, 3-4, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end

Slide 108

Slide 108 text

@sandimetz May 2018 # "1, #4, 3-4, #6, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end

Slide 109

Slide 109 text

@sandimetz May 2018 # "1, #4, 3-4, #6, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end

Slide 110

Slide 110 text

@sandimetz May 2018 # "1, #4, 3-4, #6, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end New Conditional Here

Slide 111

Slide 111 text

@sandimetz May 2018 # "1, #4, 3-4, #6, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end New Conditional Here

Slide 112

Slide 112 text

@sandimetz May 2018 # "1, #4, 3-4, #6, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end

Slide 113

Slide 113 text

@sandimetz May 2018 # "1, #4, 3-4, #6, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end

Slide 114

Slide 114 text

@sandimetz May 2018 # "1, #4, 3-4, #6, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') else edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end

Slide 115

Slide 115 text

@sandimetz May 2018 # "1, #4, 3-4, #6, 15, 37-50" def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end

Slide 116

Slide 116 text

@sandimetz May 2018 Change #4

Slide 117

Slide 117 text

@sandimetz May 2018 Change #4 Left Justification

Slide 118

Slide 118 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end # ...

Slide 119

Slide 119 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end # ...

Slide 120

Slide 120 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end # ...

Slide 121

Slide 121 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end # ...

Slide 122

Slide 122 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end # ...

Slide 123

Slide 123 text

@sandimetz May 2018 class Listing # ...

Slide 124

Slide 124 text

@sandimetz May 2018 class Listing # ... def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end

Slide 125

Slide 125 text

@sandimetz May 2018 class Listing # ... def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end Yup

Slide 126

Slide 126 text

@sandimetz May 2018 def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end

Slide 127

Slide 127 text

@sandimetz May 2018 def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end

Slide 128

Slide 128 text

@sandimetz May 2018 def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end

Slide 129

Slide 129 text

@sandimetz May 2018 def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers lines_to_print(all_lines) else all_lines end end

Slide 130

Slide 130 text

@sandimetz May 2018 def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end end

Slide 131

Slide 131 text

@sandimetz May 2018 def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end end

Slide 132

Slide 132 text

@sandimetz May 2018 def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end

Slide 133

Slide 133 text

@sandimetz May 2018 def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end

Slide 134

Slide 134 text

@sandimetz May 2018 def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || ""} end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end

Slide 135

Slide 135 text

@sandimetz May 2018 def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || ""} end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end

Slide 136

Slide 136 text

@sandimetz May 2018 def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || ""} end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end

Slide 137

Slide 137 text

@sandimetz May 2018 def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || ""} end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end Ouch

Slide 138

Slide 138 text

@sandimetz May 2018 Progression

Slide 139

Slide 139 text

@sandimetz May 2018 1 File or Git Tag

Slide 140

Slide 140 text

@sandimetz May 2018 class Listing attr_reader :filename, :repository, :tag, :git_cmd def initialize(filename:, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 1 Lines: 29

Slide 141

Slide 141 text

@sandimetz May 2018 class Listing attr_reader :filename, :repository, :tag, :git_cmd def initialize(filename:, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 1

Slide 142

Slide 142 text

@sandimetz May 2018 class Listing attr_reader :filename, :repository, :tag, :git_cmd def initialize(filename:, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 1 Execution Paths: 2

Slide 143

Slide 143 text

@sandimetz May 2018 class Listing attr_reader :filename, :repository, :tag, :git_cmd def initialize(filename:, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 1

Slide 144

Slide 144 text

@sandimetz May 2018 2All or Some Lines

Slide 145

Slide 145 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end private def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 2 Lines: 50

Slide 146

Slide 146 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end private def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 2 Execution Paths:

Slide 147

Slide 147 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end private def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 2 Execution Paths: 4

Slide 148

Slide 148 text

@sandimetz May 2018 class Listing attr_reader :filename, :repository, :tag, :git_cmd def initialize(filename:, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end private def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 1 2

Slide 149

Slide 149 text

@sandimetz May 2018 3Code Line or Comment

Slide 150

Slide 150 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end private def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 3 Lines: 55

Slide 151

Slide 151 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end private def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 3 Execution Paths:

Slide 152

Slide 152 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end private def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 3 Execution Paths: 8

Slide 153

Slide 153 text

@sandimetz May 2018 class Listing attr_reader :filename, :repository, :tag, :git_cmd def initialize(filename:, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end private def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 1 2 class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end private def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 3

Slide 154

Slide 154 text

@sandimetz May 2018 4Raw or Left Just

Slide 155

Slide 155 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end private #################### # Reading #################### def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end #################### # Subsetting #################### def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end #################### # Justification #################### def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || "" } end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end # plus 90+ lines of error handling 4 Lines: 83

Slide 156

Slide 156 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end private #################### # Reading #################### def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end #################### # Subsetting #################### def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end #################### # Justification #################### def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || "" } end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end # plus 90+ lines of error handling 4 Execution Paths:

Slide 157

Slide 157 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end private #################### # Reading #################### def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end #################### # Subsetting #################### def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end #################### # Justification #################### def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || "" } end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end # plus 90+ lines of error handling 4 Execution Paths: 16

Slide 158

Slide 158 text

@sandimetz May 2018 class Listing attr_reader :filename, :repository, :tag, :git_cmd def initialize(filename:, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end private def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 1 2 class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end private def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 3 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end private #################### # Reading #################### def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end #################### # Subsetting #################### def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end #################### # Justification #################### def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || "" } end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end # plus 90+ lines of error handling 4

Slide 159

Slide 159 text

@sandimetz May 2018

Slide 160

Slide 160 text

@sandimetz May 2018 Ouch

Slide 161

Slide 161 text

@sandimetz May 2018

Slide 162

Slide 162 text

@sandimetz May 2018 Ouch

Slide 163

Slide 163 text

@sandimetz May 2018

Slide 164

Slide 164 text

@sandimetz May 2018 Ouch

Slide 165

Slide 165 text

@sandimetz May 2018

Slide 166

Slide 166 text

@sandimetz May 2018 What brought you success

Slide 167

Slide 167 text

@sandimetz May 2018 What brought you success will doom you to failure.

Slide 168

Slide 168 text

@sandimetz May 2018 Affordances

Slide 169

Slide 169 text

@sandimetz May 2018 "Doorknob" by photonooner CC BY-NC-ND

Slide 170

Slide 170 text

@sandimetz May 2018 "Doorknob" by photonooner CC BY-NC-ND "Door Handle" by www.trek.today CC BY-NC-ND

Slide 171

Slide 171 text

@sandimetz May 2018 "Doorknob" by photonooner CC BY-NC-ND "Door Handle" by www.trek.today CC BY-NC-ND "pull" by various brennemans CC BY-SA

Slide 172

Slide 172 text

@sandimetz May 2018 I'm a loser and lost the attribution. Sorry.

Slide 173

Slide 173 text

@sandimetz May 2018 "pull" by greenkozi CC BY-NC-ND I'm a loser and lost the attribution. Sorry.

Slide 174

Slide 174 text

@sandimetz May 2018 "pull" by greenkozi CC BY-NC-ND "door push plate" by stu_spivack CC BY-SA I'm a loser and lost the attribution. Sorry.

Slide 175

Slide 175 text

@sandimetz May 2018 OO Affords

Slide 176

Slide 176 text

@sandimetz May 2018 OO Affords Anthropomorphic

Slide 177

Slide 177 text

@sandimetz May 2018 OO Affords Anthropomorphic Polymorphic

Slide 178

Slide 178 text

@sandimetz May 2018 OO Affords Anthropomorphic Polymorphic Loosely-coupled

Slide 179

Slide 179 text

@sandimetz May 2018 OO Affords Anthropomorphic Polymorphic Loosely-coupled Role-playing

Slide 180

Slide 180 text

@sandimetz May 2018 OO Affords Anthropomorphic Polymorphic Loosely-coupled Role-playing Factory-created

Slide 181

Slide 181 text

@sandimetz May 2018 OO Affords Anthropomorphic Polymorphic Loosely-coupled Role-playing Factory-created Message-sending

Slide 182

Slide 182 text

@sandimetz May 2018 OO Affords Anthropomorphic Polymorphic Loosely-coupled Role-playing Factory-created Message-sending Objects

Slide 183

Slide 183 text

@sandimetz May 2018 Anthropomorphism

Slide 184

Slide 184 text

@sandimetz May 2018 Anthropomorphism -the attribution of human traits, emotions or intentions to non-human entities.

Slide 185

Slide 185 text

@sandimetz May 2018

Slide 186

Slide 186 text

@sandimetz May 2018 Polymorphism

Slide 187

Slide 187 text

@sandimetz May 2018 Polymorphism -the condition of occurring in several different forms

Slide 188

Slide 188 text

@sandimetz May 2018

Slide 189

Slide 189 text

@sandimetz May 2018

Slide 190

Slide 190 text

@sandimetz May 2018

Slide 191

Slide 191 text

@sandimetz May 2018 Loosely-Coupled

Slide 192

Slide 192 text

@sandimetz May 2018 Loosely-Coupled -objects strive for independence

Slide 193

Slide 193 text

@sandimetz May 2018

Slide 194

Slide 194 text

@sandimetz May 2018 Role-playing

Slide 195

Slide 195 text

@sandimetz May 2018 Role-playing -objects are more players of their roles than instances of their types

Slide 196

Slide 196 text

@sandimetz May 2018

Slide 197

Slide 197 text

@sandimetz May 2018 Factory-created

Slide 198

Slide 198 text

@sandimetz May 2018 Factory-created -factories hide the rules for picking the right player of a role

Slide 199

Slide 199 text

@sandimetz May 2018

Slide 200

Slide 200 text

@sandimetz May 2018

Slide 201

Slide 201 text

@sandimetz May 2018 Message-sending

Slide 202

Slide 202 text

@sandimetz May 2018 Message-sending -I know what I want, you know how to do it

Slide 203

Slide 203 text

@sandimetz May 2018

Slide 204

Slide 204 text

@sandimetz May 2018 OO Affords Anthropomorphic Polymorphic Loosely-coupled Role-playing Factory-created Message-sending Objects

Slide 205

Slide 205 text

@sandimetz May 2018 Resolution

Slide 206

Slide 206 text

@sandimetz May 2018 Where all ends well Resolution

Slide 207

Slide 207 text

@sandimetz May 2018 class Listing def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end

Slide 208

Slide 208 text

@sandimetz May 2018 class Listing def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end 2

Slide 209

Slide 209 text

@sandimetz May 2018 class Listing def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end 2 2x2

Slide 210

Slide 210 text

@sandimetz May 2018 class Listing def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end 2 2x2 2x2x2

Slide 211

Slide 211 text

@sandimetz May 2018 class Listing def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#"). (" " * num_spaces) + "# ..." else edges = spec.split('-').collec range_of_line_numbers = (edges range_of_line_numbers.collect end }.flatten.compact end 2 2x2 2x2x2

Slide 212

Slide 212 text

@sandimetz May 2018 class Listing def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#"). (" " * num_spaces) + "# ..." else edges = spec.split('-').collec range_of_line_numbers = (edges range_of_line_numbers.collect end }.flatten.compact end 2 2x2 2x2x2 2x2x2x2

Slide 213

Slide 213 text

@sandimetz May 2018 class Listing def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end

Slide 214

Slide 214 text

@sandimetz May 2018 class Listing def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end If you know enough to inject this

Slide 215

Slide 215 text

@sandimetz May 2018 class Listing def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end Inject a smarter thing

Slide 216

Slide 216 text

@sandimetz May 2018 class Listing def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end 1 File or Git Tag

Slide 217

Slide 217 text

@sandimetz May 2018 class Listing def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end 1 Role = Source

Slide 218

Slide 218 text

@sandimetz May 2018 Source

Slide 219

Slide 219 text

@sandimetz May 2018 Source #lines

Slide 220

Slide 220 text

@sandimetz May 2018 Source #lines File Source GitTag Source

Slide 221

Slide 221 text

@sandimetz May 2018 class FileSource end class GitTagSource end S O U R C E S

Slide 222

Slide 222 text

@sandimetz May 2018 class FileSource end class GitTagSource end S O U R C E S

Slide 223

Slide 223 text

@sandimetz May 2018 class FileSource def lines end # ... end class GitTagSource def lines end # ... end S O U R C E S

Slide 224

Slide 224 text

@sandimetz May 2018 class FileSource def lines end # ... end class GitTagSource def lines end # ... end S O U R C E S

Slide 225

Slide 225 text

@sandimetz May 2018 class FileSource def lines end # ... end class GitTagSource def lines end # ... end S O U R C E S Polymorphism

Slide 226

Slide 226 text

@sandimetz May 2018 class FileSource def lines File.read(filename).split("\n") end # ... end class GitTagSource def lines end # ... end S O U R C E S

Slide 227

Slide 227 text

@sandimetz May 2018 class FileSource def lines File.read(filename).split("\n") end # ... end class GitTagSource def lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end # ... end S O U R C E S

Slide 228

Slide 228 text

@sandimetz May 2018 Isolate the things you want to vary

Slide 229

Slide 229 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end subset = # ... def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end

Slide 230

Slide 230 text

@sandimetz May 2018 def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end v v v class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end subset = # ...

Slide 231

Slide 231 text

@sandimetz May 2018 class Listing attr_reader :line_numbers, :left_just def initialize( line_numbers: nil, left_justify: false) @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = if git_cmd git_lines else file_lines end subset = # ...

Slide 232

Slide 232 text

@sandimetz May 2018 class Listing attr_reader :line_numbers, :left_just def initialize( line_numbers: nil, left_justify: false) @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = if git_cmd git_lines else file_lines end subset = # ...

Slide 233

Slide 233 text

@sandimetz May 2018 class Listing attr_reader :line_numbers, :left_just def initialize( line_numbers: nil, left_justify: false) @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = if git_cmd git_lines else file_lines end subset = # ...

Slide 234

Slide 234 text

@sandimetz May 2018 class Listing attr_reader :line_numbers, :left_just def initialize( line_numbers: nil, left_justify: false) @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = if git_cmd git_lines else file_lines end subset = # ... Inject a Source

Slide 235

Slide 235 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = if git_cmd git_lines else file_lines end subset = # ...

Slide 236

Slide 236 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = if git_cmd git_lines else file_lines end subset = # ...

Slide 237

Slide 237 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = if git_cmd git_lines else file_lines end subset = # ...

Slide 238

Slide 238 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = if git_cmd git_lines else file_lines end subset = # ...

Slide 239

Slide 239 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = if git_cmd git_lines else file_lines end subset = # ...

Slide 240

Slide 240 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = if git_cmd git_lines else file_lines end subset = # ...

Slide 241

Slide 241 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = source.lines subset = # ...

Slide 242

Slide 242 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = source.lines subset = # ...

Slide 243

Slide 243 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = source.lines subset = # ... Execution Paths: 16?

Slide 244

Slide 244 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = source.lines subset = # ... Execution Paths: 8

Slide 245

Slide 245 text

@sandimetz May 2018 Push conditionals back on the stack

Slide 246

Slide 246 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = source.lines subset =

Slide 247

Slide 247 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = source.lines subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end

Slide 248

Slide 248 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = source.lines subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end

Slide 249

Slide 249 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = source.lines subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end 2 All or Some Lines

Slide 250

Slide 250 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = source.lines subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end 2 Subset

Slide 251

Slide 251 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = source.lines subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end Inject a smarter thing

Slide 252

Slide 252 text

@sandimetz May 2018 Subset

Slide 253

Slide 253 text

@sandimetz May 2018 Subset #lines

Slide 254

Slide 254 text

@sandimetz May 2018 Subset #lines Everything Line Number

Slide 255

Slide 255 text

@sandimetz May 2018 module Subset class Everything end class LineNumber end end S U B S E T

Slide 256

Slide 256 text

@sandimetz May 2018 module Subset class Everything end class LineNumber end S U B S E T

Slide 257

Slide 257 text

@sandimetz May 2018 module Subset class Everything def lines(everything) end end class LineNumber end S U B S E T

Slide 258

Slide 258 text

@sandimetz May 2018 module Subset class Everything def lines(everything) everything end end class LineNumber end S U B S E T

Slide 259

Slide 259 text

@sandimetz May 2018 module Subset class Everything end class LineNumber end S U B S E T

Slide 260

Slide 260 text

@sandimetz May 2018 module Subset class Everything; end class LineNumber end S U B S E T

Slide 261

Slide 261 text

@sandimetz May 2018 module Subset class Everything; end class LineNumber # ... def lines_to_print(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ / specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..ed range_of_line_numbers.collect {|i| all_line end }.flatten.compact end end S U B S E T

Slide 262

Slide 262 text

@sandimetz May 2018 module Subset class Everything; end class LineNumber # ... def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ / specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..ed range_of_line_numbers.collect {|i| all_line end }.flatten.compact end end S U B S E T

Slide 263

Slide 263 text

@sandimetz May 2018 module Subset class Everything; end class LineNumber # ... def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ / specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..ed range_of_line_numbers.collect {|i| all_line end }.flatten.compact end end S U B S E T ?

Slide 264

Slide 264 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = source.lines subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end def lines_to_print(all_lines) specs = line_numbers.gsub(/[ specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete (" " * num_spaces) + "# else edges = spec.split('-'). range_of_line_numbers = range_of_line_numbers.co end }.flatten.compact end

Slide 265

Slide 265 text

@sandimetz May 2018 class Listing attr_reader :source, :line_numbers, :left_just def initialize(source:, line_numbers: nil, left_justify: false) @source = source @line_numbers = line_numbers @left_just = left_justify end def lines all_lines = source.lines subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end def lines_to_print(all_lines) specs = line_numbers.gsub(/[ specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete (" " * num_spaces) + "# else edges = spec.split('-'). range_of_line_numbers = range_of_line_numbers.co end }.flatten.compact end

Slide 266

Slide 266 text

@sandimetz May 2018 class Listing attr_reader :source, :left_just def initialize(source:, left_justify: false) @source = source @left_just = left_justify end def lines all_lines = source.lines subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end

Slide 267

Slide 267 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @left_just = left_justify end def lines all_lines = source.lines subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end

Slide 268

Slide 268 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @left_just = left_justify end def lines all_lines = source.lines subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end

Slide 269

Slide 269 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @left_just = left_justify end def lines all_lines = source.lines subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end

Slide 270

Slide 270 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @subsetter = subsetter @left_just = left_justify end def lines all_lines = source.lines subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end

Slide 271

Slide 271 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @subsetter = subsetter @left_just = left_justify end def lines all_lines = source.lines subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end

Slide 272

Slide 272 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @subsetter = subsetter @left_just = left_justify end def lines all_lines = source.lines subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end

Slide 273

Slide 273 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @subsetter = subsetter @left_just = left_justify end def lines all_lines = source.lines subset = subsetter.lines(all_lines) if left_just return justify(subset) end subset end

Slide 274

Slide 274 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @subsetter = subsetter @left_just = left_justify end def lines all_lines = source.lines subset = subsetter.lines(all_lines) if left_just return justify(subset) end subset end

Slide 275

Slide 275 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @subsetter = subsetter @left_just = left_justify end def lines subset = subsetter.lines(source.lines) if left_just return justify(subset) end subset end

Slide 276

Slide 276 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @subsetter = subsetter @left_just = left_justify end def lines subset = subsetter.lines(source.lines) if left_just return justify(subset) end subset end Execution Paths: 4

Slide 277

Slide 277 text

@sandimetz May 2018 Dependency injection is your friend

Slide 278

Slide 278 text

@sandimetz May 2018 As long as you inject smart things

Slide 279

Slide 279 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @subsetter = subsetter @left_just = left_justify end def lines subset = subsetter.lines(source.lines) if left_just return justify(subset) end subset end

Slide 280

Slide 280 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @subsetter = subsetter @left_just = left_justify end def lines subset = subsetter.lines(source.lines) if left_just return justify(subset) end subset end

Slide 281

Slide 281 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @subsetter = subsetter @left_just = left_justify end def lines subset = subsetter.lines(source.lines) if left_just return justify(subset) end subset end 4 Raw or Left Just

Slide 282

Slide 282 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @subsetter = subsetter @left_just = left_justify end def lines subset = subsetter.lines(source.lines) if left_just return justify(subset) end subset end 4 Justifier

Slide 283

Slide 283 text

@sandimetz May 2018 Justifier

Slide 284

Slide 284 text

@sandimetz May 2018 Justifier #justify

Slide 285

Slide 285 text

@sandimetz May 2018 Justifier #justify None Block Left

Slide 286

Slide 286 text

@sandimetz May 2018 module Justification class None def justify(lines) lines end end class BlockLeft def justify(lines) lines.map {|line| line.slice(num_leading_spaces end def num_leading_spaces_to_remove; end def num_leading_spaces(line); end end end J U S T I F I E R

Slide 287

Slide 287 text

@sandimetz May 2018 module Justification class None def justify(lines) lines end end class BlockLeft def justify(lines) lines.map {|line| line.slice(num_leading_spaces end def num_leading_spaces_to_remove; end def num_leading_spaces(line); end end end J U S T I F I E R

Slide 288

Slide 288 text

@sandimetz May 2018 module Justification class None def justify(lines) lines end end class BlockLeft def justify(lines) lines.map {|line| line.slice(num_leading_spaces end def num_leading_spaces_to_remove; end def num_leading_spaces(line); end end end J U S T I F I E R

Slide 289

Slide 289 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @subsetter = subsetter @left_just = left_justify end def lines subset = subsetter.lines(source.lines) if left_just return justify(subset) end subset end end

Slide 290

Slide 290 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :left_just def initialize(source:, subsetter:, left_justify: false) @source = source @subsetter = subsetter @left_just = left_justify end def lines subset = subsetter.lines(source.lines) if left_just return justify(subset) end subset end end

Slide 291

Slide 291 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines subset = subsetter.lines(source.lines) if left_just return justify(subset) end subset end end

Slide 292

Slide 292 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines subset = subsetter.lines(source.lines) if left_just return justify(subset) end subset end end

Slide 293

Slide 293 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines subset = subsetter.lines(source.lines) justifier.justify(subset) end end

Slide 294

Slide 294 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines subset = subsetter.lines(source.lines) justifier.justify(subset) end end

Slide 295

Slide 295 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines subset = subsetter.lines(source.lines) justifier.justify(subsetter.lines(source.lines)) end end

Slide 296

Slide 296 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end

Slide 297

Slide 297 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end

Slide 298

Slide 298 text

@sandimetz May 2018 But, conditionals?

Slide 299

Slide 299 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end 3 Code Line or Comment

Slide 300

Slide 300 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, " specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to (" " * num_spaces) + "# ..." else edges = spec.split('-').collect( range_of_line_numbers = (edges.m range_of_line_numbers.collect {| end }.flatten.compact end 3 Code Line or Comment

Slide 301

Slide 301 text

@sandimetz May 2018 class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, " specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to (" " * num_spaces) + "# ..." else edges = spec.split('-').collect( range_of_line_numbers = (edges.m range_of_line_numbers.collect {| end }.flatten.compact end 3 Clump

Slide 302

Slide 302 text

@sandimetz May 2018 Clump

Slide 303

Slide 303 text

@sandimetz May 2018 Clump #lines

Slide 304

Slide 304 text

@sandimetz May 2018 Clump #lines Comment Line Number

Slide 305

Slide 305 text

@sandimetz May 2018 class Clump # initialize with spec and input class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.c end def expand_clump(spec) # ... end end end C L U M P

Slide 306

Slide 306 text

@sandimetz May 2018 class Clump # initialize with spec and input class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.c end def expand_clump(spec) # ... end end end C L U M P

Slide 307

Slide 307 text

@sandimetz May 2018 class Clump # initialize with spec and input class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.c end def expand_clump(spec) # ... end end end C L U M P

Slide 308

Slide 308 text

@sandimetz May 2018 class Clump # initialize with spec and input class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.c end def expand_clump(spec) # ... end end end C L U M P

Slide 309

Slide 309 text

@sandimetz May 2018 class Clump # initialize with spec and input class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.c end def expand_clump(spec) # ... end end end C L U M P

Slide 310

Slide 310 text

@sandimetz May 2018 class Clump # initialize with spec and input class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.c end def expand_clump(spec) # ... end end end C L U M P

Slide 311

Slide 311 text

@sandimetz May 2018 class Clump # initialize with spec and input class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.c end def expand_clump(spec) # ... end end end C L U M P

Slide 312

Slide 312 text

@sandimetz May 2018 class Clump # initialize with spec and input class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.c end def expand_clump(spec) # ... end end end C L U M P

Slide 313

Slide 313 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end S U B S E T

Slide 314

Slide 314 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end S U B S E T

Slide 315

Slide 315 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') else end }.flatten.compact end S U B S E T

Slide 316

Slide 316 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') else end }.flatten.compact end S U B S E T

Slide 317

Slide 317 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') Clump::Comment.new(...).lines else end }.flatten.compact end S U B S E T

Slide 318

Slide 318 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') Clump::Comment.new(...).lines else Clump::LineNumber.new(...).lines end }.flatten.compact end S U B S E T

Slide 319

Slide 319 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') Clump::Comment.new(...).lines else Clump::LineNumber.new(...).lines end }.flatten.compact end Polymorphic S U B S E T

Slide 320

Slide 320 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') Clump::Comment.new(...).lines else Clump::LineNumber.new(...).lines end }.flatten.compact end Role-playing S U B S E T

Slide 321

Slide 321 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') Clump::Comment.new(...).lines else Clump::LineNumber.new(...).lines end }.flatten.compact end Rule ?? S U B S E T

Slide 322

Slide 322 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') Clump::Comment.new(...).lines else Clump::LineNumber.new(...).lines end }.flatten.compact end Tightly-coupled S U B S E T

Slide 323

Slide 323 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') Clump::Comment.new(...).lines else Clump::LineNumber.new(...).lines end }.flatten.compact end S U B S E T

Slide 324

Slide 324 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') Clump::Comment.new(...).lines else Clump::LineNumber.new(...).lines end }.flatten.compact end Factory! S U B S E T

Slide 325

Slide 325 text

@sandimetz May 2018 class Clump def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end # ... end C L U M P

Slide 326

Slide 326 text

@sandimetz May 2018 class Clump def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end # ... end C L U M P

Slide 327

Slide 327 text

@sandimetz May 2018 class Clump def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end # ... end C L U M P

Slide 328

Slide 328 text

@sandimetz May 2018 class Clump def self.lines(spec:, possibilities: []) self.for(spec: spec, possibilities: possibilities).lines end def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end # ... end C L U M P

Slide 329

Slide 329 text

@sandimetz May 2018 class Clump def self.lines(spec:, possibilities: []) self.for(spec: spec, possibilities: possibilities).lines end def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end # ... end C L U M P Factory-created

Slide 330

Slide 330 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') Clump::Comment.new(...).lines else Clump::LineNumber.new(...).lines end }.flatten.compact end S U B S E T

Slide 331

Slide 331 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') Clump::Comment.new(...).lines else Clump::LineNumber.new(...).lines end }.flatten.compact end S U B S E T

Slide 332

Slide 332 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| }.flatten.compact end S U B S E T

Slide 333

Slide 333 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| Clump.lines(spec: spec, possibilities: possibilities) }.flatten.compact end S U B S E T

Slide 334

Slide 334 text

@sandimetz May 2018 module Subset class LineNumber def lines(possibilities) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| Clump.lines(spec: spec, possibilities: possibilities) }.flatten.compact end S U B S E T

Slide 335

Slide 335 text

@sandimetz May 2018 Reprise

Slide 336

Slide 336 text

@sandimetz May 2018 class Listing attr_reader :filename, :repository, :tag, :git_cmd def initialize(filename:, repository: nil, tag: nil, git_cmd: nil) @filename = filename @repository = repository @tag = tag @git_cmd = git_cmd end def lines if git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") else File.read(filename).split("\n") end end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end private def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| edges = spec.split('-').collect(&:to_i) individual_numbers = (edges.min.to_i..edges.max.to_i).to_a individual_numbers.collect {|i| all_lines[i - 1]}.compact }.flatten.compact end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 1 2 class Listing attr_reader :filename, :line_numbers, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end if line_numbers return lines_to_print(all_lines) end all_lines end private def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end 3 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end private #################### # Reading #################### def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end #################### # Subsetting #################### def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end #################### # Justification #################### def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || "" } end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end # plus 90+ lines of error handling 4

Slide 337

Slide 337 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end private #################### # Reading #################### def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end #################### # Subsetting #################### def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end #################### # Justification #################### def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || "" } end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end # plus 90+ lines of error handling Lines: 83 Execution Paths: 16 Procedure

Slide 338

Slide 338 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end private #################### # Reading #################### def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end #################### # Subsetting #################### def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end #################### # Justification #################### def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || "" } end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end # plus 90+ lines of error handling Lines: 83 Execution Paths: 16 Procedure OO

Slide 339

Slide 339 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end private #################### # Reading #################### def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end #################### # Subsetting #################### def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end #################### # Justification #################### def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || "" } end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end # plus 90+ lines of error handling Lines: 83 Execution Paths: 16 Procedure module Source class File attr_reader :filename def initialize(filename:) @filename = filename end def lines ::File.read(filename).split("\n") end end class GitTag def self.git_cmd GitCmd.new end attr_reader :filename, :tag, :repository, :git_cmd def initialize(filename:, repository:, tag:, git_cmd: self.class.git_cmd) @git_cmd = git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename end def lines git_cmd.show.split("\n") end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end def git_dir %Q[--git-dir="#{repository}"] end end end end Source: File GitTag OO

Slide 340

Slide 340 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end private #################### # Reading #################### def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end #################### # Subsetting #################### def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end #################### # Justification #################### def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || "" } end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end # plus 90+ lines of error handling Lines: 83 Execution Paths: 16 Procedure module Source class File attr_reader :filename def initialize(filename:) @filename = filename end def lines ::File.read(filename).split("\n") end end class GitTag def self.git_cmd GitCmd.new end attr_reader :filename, :tag, :repository, :git_cmd def initialize(filename:, repository:, tag:, git_cmd: self.class.git_cmd) @git_cmd = git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename end def lines git_cmd.show.split("\n") end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end def git_dir %Q[--git-dir="#{repository}"] end end end end OO module Subset class Everything def lines(everything) everything end end class LineNumber attr_reader :line_numbers def initialize(line_numbers:) @line_numbers = line_numbers end def lines(possibilities) clump_specs.collect {|spec| clump_for(spec, possibilities) }.flatten.compact end def clump_specs line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") end def clump_for(spec, possibilities) Clump.lines(spec: spec, possibilities: possibilities) end end end Subset: Everything LineNumber Source:

Slide 341

Slide 341 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end private #################### # Reading #################### def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end #################### # Subsetting #################### def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end #################### # Justification #################### def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || "" } end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end # plus 90+ lines of error handling Lines: 83 Execution Paths: 16 Procedure module Source class File attr_reader :filename def initialize(filename:) @filename = filename end def lines ::File.read(filename).split("\n") end end class GitTag def self.git_cmd GitCmd.new end attr_reader :filename, :tag, :repository, :git_cmd def initialize(filename:, repository:, tag:, git_cmd: self.class.git_cmd) @git_cmd = git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename end def lines git_cmd.show.split("\n") end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end def git_dir %Q[--git-dir="#{repository}"] end end end end OO module Subset class Everything def lines(everything) everything end end class LineNumber attr_reader :line_numbers def initialize(line_numbers:) @line_numbers = line_numbers end def lines(possibilities) clump_specs.collect {|spec| clump_for(spec, possibilities) }.flatten.compact end def clump_specs line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") end def clump_for(spec, possibilities) Clump.lines(spec: spec, possibilities: possibilities) end end end Subset: Source: class Clump def self.lines(spec:, possibilities: []) self.for(spec: spec, possibilities: possibilities).lines end def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end attr_reader :spec, :input def initialize(spec:, input: []) @spec = spec @input = input end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.compact end def expand_clump(spec) edges = spec.split('-').collect(&:to_i) (edges.min.to_i..edges.max.to_i).to_a end end class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end end Clump: Comment LineNumber

Slide 342

Slide 342 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end private #################### # Reading #################### def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end #################### # Subsetting #################### def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end #################### # Justification #################### def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || "" } end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end # plus 90+ lines of error handling Lines: 83 Execution Paths: 16 Procedure module Source class File attr_reader :filename def initialize(filename:) @filename = filename end def lines ::File.read(filename).split("\n") end end class GitTag def self.git_cmd GitCmd.new end attr_reader :filename, :tag, :repository, :git_cmd def initialize(filename:, repository:, tag:, git_cmd: self.class.git_cmd) @git_cmd = git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename end def lines git_cmd.show.split("\n") end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end def git_dir %Q[--git-dir="#{repository}"] end end end end OO module Subset class Everything def lines(everything) everything end end class LineNumber attr_reader :line_numbers def initialize(line_numbers:) @line_numbers = line_numbers end def lines(possibilities) clump_specs.collect {|spec| clump_for(spec, possibilities) }.flatten.compact end def clump_specs line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") end def clump_for(spec, possibilities) Clump.lines(spec: spec, possibilities: possibilities) end end end Subset: Source: class Clump def self.lines(spec:, possibilities: []) self.for(spec: spec, possibilities: possibilities).lines end def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end attr_reader :spec, :input def initialize(spec:, input: []) @spec = spec @input = input end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.compact end def expand_spec(spec) edges = spec.split('-').collect(&:to_i) (edges.min.to_i..edges.max.to_i).to_a end end class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end end Clump: module Justification class None def self.justify(lines) lines end end class BlockLeft def self.justify(lines) new(lines).justify end attr_reader :lines def initialize(lines) @lines = lines end def justify lines.map {|line| line.slice(num_leading_spaces_to_remove..-1) || "" } end private def num_leading_spaces_to_remove @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end end Justification: None BlockLeft

Slide 343

Slide 343 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end private #################### # Reading #################### def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end #################### # Subsetting #################### def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end #################### # Justification #################### def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || "" } end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end # plus 90+ lines of error handling Lines: 83 Execution Paths: 16 Procedure module Source class File attr_reader :filename def initialize(filename:) @filename = filename end def lines ::File.read(filename).split("\n") end end class GitTag def self.git_cmd GitCmd.new end attr_reader :filename, :tag, :repository, :git_cmd def initialize(filename:, repository:, tag:, git_cmd: self.class.git_cmd) @git_cmd = git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename end def lines git_cmd.show.split("\n") end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end def git_dir %Q[--git-dir="#{repository}"] end end end end OO module Subset class Everything def lines(everything) everything end end class LineNumber attr_reader :line_numbers def initialize(line_numbers:) @line_numbers = line_numbers end def lines(possibilities) clump_specs.collect {|spec| clump_for(spec, possibilities) }.flatten.compact end def clump_specs line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") end def clump_for(spec, possibilities) Clump.lines(spec: spec, possibilities: possibilities) end end end Subset: Source: class Clump def self.lines(spec:, possibilities: []) self.for(spec: spec, possibilities: possibilities).lines end def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end attr_reader :spec, :input def initialize(spec:, input: []) @spec = spec @input = input end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.compact end def expand_clump(spec) edges = spec.split('-').collect(&:to_i) (edges.min.to_i..edges.max.to_i).to_a end end class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end end Clump: module Justification class None def self.justify(lines) lines end end class BlockLeft def self.justify(lines) new(lines).justify end attr_reader :lines def initialize(lines) @lines = lines end def justify lines.map {|line| line.slice(num_leading_spaces_to_remove..-1) || "" } end private def num_leading_spaces_to_remove @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end end Justification: class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end

Slide 344

Slide 344 text

@sandimetz May 2018 class Listing attr_reader :filename, :line_numbers, :left_just, :repository, :tag, :git_cmd def initialize(filename:, line_numbers: nil, left_justify: false, repository: nil, tag: nil, git_cmd: nil) @filename = filename @line_numbers = line_numbers @left_just = left_justify @repository = repository @tag = tag @git_cmd = git_cmd end def lines all_lines = if git_cmd git_lines else file_lines end subset = if line_numbers lines_to_print(all_lines) else all_lines end if left_just return justify(subset) end subset end private #################### # Reading #################### def git_lines git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename git_cmd.show.split("\n") end def file_lines File.read(filename).split("\n") end #################### # Subsetting #################### def lines_to_print(all_lines) specs = line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") specs.collect {|spec| if spec.include?('#') num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." else edges = spec.split('-').collect(&:to_i) range_of_line_numbers = (edges.min.to_i..edges.max.to_i).to_a range_of_line_numbers.collect {|i| all_lines[i - 1]}.compact end }.flatten.compact end #################### # Justification #################### def justify(lines) lines.map {|line| line.slice(num_leading_spaces_to_remove(lines)..-1) || "" } end def num_leading_spaces_to_remove(lines) @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end private def git_dir %Q[--git-dir="#{repository}"] end end # plus 90+ lines of error handling Lines: 83 Execution Paths: 16 Procedure module Source class File attr_reader :filename def initialize(filename:) @filename = filename end def lines ::File.read(filename).split("\n") end end class GitTag def self.git_cmd GitCmd.new end attr_reader :filename, :tag, :repository, :git_cmd def initialize(filename:, repository:, tag:, git_cmd: self.class.git_cmd) @git_cmd = git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename end def lines git_cmd.show.split("\n") end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end def git_dir %Q[--git-dir="#{repository}"] end end end end OO module Subset class Everything def lines(everything) everything end end class LineNumber attr_reader :line_numbers def initialize(line_numbers:) @line_numbers = line_numbers end def lines(possibilities) clump_specs.collect {|spec| clump_for(spec, possibilities) }.flatten.compact end def clump_specs line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") end def clump_for(spec, possibilities) Clump.lines(spec: spec, possibilities: possibilities) end end end Subset: Source: class Clump def self.lines(spec:, possibilities: []) self.for(spec: spec, possibilities: possibilities).lines end def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end attr_reader :spec, :input def initialize(spec:, input: []) @spec = spec @input = input end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.compact end def expand_clump(spec) edges = spec.split('-').collect(&:to_i) (edges.min.to_i..edges.max.to_i).to_a end end class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end end Clump: module Justification class None def self.justify(lines) lines end end class BlockLeft def self.justify(lines) new(lines).justify end attr_reader :lines def initialize(lines) @lines = lines end def justify lines.map {|line| line.slice(num_leading_spaces_to_remove..-1) || "" } end private def num_leading_spaces_to_remove @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end end Justification: class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end Factories

Slide 345

Slide 345 text

@sandimetz May 2018 Lines: 83 Execution Paths: 16 Procedure module Source class File attr_reader :filename def initialize(filename:) @filename = filename end def lines ::File.read(filename).split("\n") end end class GitTag def self.git_cmd GitCmd.new end attr_reader :filename, :tag, :repository, :git_cmd def initialize(filename:, repository:, tag:, git_cmd: self.class.git_cmd) @git_cmd = git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename end def lines git_cmd.show.split("\n") end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end def git_dir %Q[--git-dir="#{repository}"] end end end end OO module Subset class Everything def lines(everything) everything end end class LineNumber attr_reader :line_numbers def initialize(line_numbers:) @line_numbers = line_numbers end def lines(possibilities) clump_specs.collect {|spec| clump_for(spec, possibilities) }.flatten.compact end def clump_specs line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") end def clump_for(spec, possibilities) Clump.lines(spec: spec, possibilities: possibilities) end end end Subset: Source: class Clump def self.lines(spec:, possibilities: []) self.for(spec: spec, possibilities: possibilities).lines end def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end attr_reader :spec, :input def initialize(spec:, input: []) @spec = spec @input = input end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.compact end def expand_clump(spec) edges = spec.split('-').collect(&:to_i) (edges.min.to_i..edges.max.to_i).to_a end end class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end end Clump: module Justification class None def self.justify(lines) lines end end class BlockLeft def self.justify(lines) new(lines).justify end attr_reader :lines def initialize(lines) @lines = lines end def justify lines.map {|line| line.slice(num_leading_spaces_to_remove..-1) || "" } end private def num_leading_spaces_to_remove @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end end Justification: class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end Factories

Slide 346

Slide 346 text

@sandimetz May 2018 Lines: 83 Execution Paths: 16 module Source class File attr_reader :filename def initialize(filename:) @filename = filename end def lines ::File.read(filename).split("\n") end end class GitTag def self.git_cmd GitCmd.new end attr_reader :filename, :tag, :repository, :git_cmd def initialize(filename:, repository:, tag:, git_cmd: self.class.git_cmd) @git_cmd = git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename end def lines git_cmd.show.split("\n") end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end def git_dir %Q[--git-dir="#{repository}"] end end end end OO module Subset class Everything def lines(everything) everything end end class LineNumber attr_reader :line_numbers def initialize(line_numbers:) @line_numbers = line_numbers end def lines(possibilities) clump_specs.collect {|spec| clump_for(spec, possibilities) }.flatten.compact end def clump_specs line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") end def clump_for(spec, possibilities) Clump.lines(spec: spec, possibilities: possibilities) end end end Subset: Source: class Clump def self.lines(spec:, possibilities: []) self.for(spec: spec, possibilities: possibilities).lines end def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end attr_reader :spec, :input def initialize(spec:, input: []) @spec = spec @input = input end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.compact end def expand_clump(spec) edges = spec.split('-').collect(&:to_i) (edges.min.to_i..edges.max.to_i).to_a end end class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end end Clump: module Justification class None def self.justify(lines) lines end end class BlockLeft def self.justify(lines) new(lines).justify end attr_reader :lines def initialize(lines) @lines = lines end def justify lines.map {|line| line.slice(num_leading_spaces_to_remove..-1) || "" } end private def num_leading_spaces_to_remove @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end end Justification: class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end Factories Procedure

Slide 347

Slide 347 text

@sandimetz May 2018 Lines: 83 Execution Paths: 16 module Source class File attr_reader :filename def initialize(filename:) @filename = filename end def lines ::File.read(filename).split("\n") end end class GitTag def self.git_cmd GitCmd.new end attr_reader :filename, :tag, :repository, :git_cmd def initialize(filename:, repository:, tag:, git_cmd: self.class.git_cmd) @git_cmd = git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename end def lines git_cmd.show.split("\n") end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end def git_dir %Q[--git-dir="#{repository}"] end end end end OO module Subset class Everything def lines(everything) everything end end class LineNumber attr_reader :line_numbers def initialize(line_numbers:) @line_numbers = line_numbers end def lines(possibilities) clump_specs.collect {|spec| clump_for(spec, possibilities) }.flatten.compact end def clump_specs line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") end def clump_for(spec, possibilities) Clump.lines(spec: spec, possibilities: possibilities) end end end Subset: Source: class Clump def self.lines(spec:, possibilities: []) self.for(spec: spec, possibilities: possibilities).lines end def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end attr_reader :spec, :input def initialize(spec:, input: []) @spec = spec @input = input end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.compact end def expand_clump(spec) edges = spec.split('-').collect(&:to_i) (edges.min.to_i..edges.max.to_i).to_a end end class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end end Clump: module Justification class None def self.justify(lines) lines end end class BlockLeft def self.justify(lines) new(lines).justify end attr_reader :lines def initialize(lines) @lines = lines end def justify lines.map {|line| line.slice(num_leading_spaces_to_remove..-1) || "" } end private def num_leading_spaces_to_remove @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end end Justification: class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end Factories Procedure Total Lines: 134

Slide 348

Slide 348 text

@sandimetz May 2018 Lines: 83 Execution Paths: 16 module Source class File attr_reader :filename def initialize(filename:) @filename = filename end def lines ::File.read(filename).split("\n") end end class GitTag def self.git_cmd GitCmd.new end attr_reader :filename, :tag, :repository, :git_cmd def initialize(filename:, repository:, tag:, git_cmd: self.class.git_cmd) @git_cmd = git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename end def lines git_cmd.show.split("\n") end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end def git_dir %Q[--git-dir="#{repository}"] end end end end OO module Subset class Everything def lines(everything) everything end end class LineNumber attr_reader :line_numbers def initialize(line_numbers:) @line_numbers = line_numbers end def lines(possibilities) clump_specs.collect {|spec| clump_for(spec, possibilities) }.flatten.compact end def clump_specs line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") end def clump_for(spec, possibilities) Clump.lines(spec: spec, possibilities: possibilities) end end end Subset: Source: class Clump def self.lines(spec:, possibilities: []) self.for(spec: spec, possibilities: possibilities).lines end def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end attr_reader :spec, :input def initialize(spec:, input: []) @spec = spec @input = input end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.compact end def expand_clump(spec) edges = spec.split('-').collect(&:to_i) (edges.min.to_i..edges.max.to_i).to_a end end class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end end Clump: module Justification class None def self.justify(lines) lines end end class BlockLeft def self.justify(lines) new(lines).justify end attr_reader :lines def initialize(lines) @lines = lines end def justify lines.map {|line| line.slice(num_leading_spaces_to_remove..-1) || "" } end private def num_leading_spaces_to_remove @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end end Justification: class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end Factories Procedure Total Lines: 134 Execution Paths: 1

Slide 349

Slide 349 text

@sandimetz May 2018 Lines: 83 Execution Paths: 16 module Source class File attr_reader :filename def initialize(filename:) @filename = filename end def lines ::File.read(filename).split("\n") end end class GitTag def self.git_cmd GitCmd.new end attr_reader :filename, :tag, :repository, :git_cmd def initialize(filename:, repository:, tag:, git_cmd: self.class.git_cmd) @git_cmd = git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename end def lines git_cmd.show.split("\n") end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end def git_dir %Q[--git-dir="#{repository}"] end end end end OO module Subset class Everything def lines(everything) everything end end class LineNumber attr_reader :line_numbers def initialize(line_numbers:) @line_numbers = line_numbers end def lines(possibilities) clump_specs.collect {|spec| clump_for(spec, possibilities) }.flatten.compact end def clump_specs line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") end def clump_for(spec, possibilities) Clump.lines(spec: spec, possibilities: possibilities) end end end Subset: Source: class Clump def self.lines(spec:, possibilities: []) self.for(spec: spec, possibilities: possibilities).lines end def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end attr_reader :spec, :input def initialize(spec:, input: []) @spec = spec @input = input end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.compact end def expand_clump(spec) edges = spec.split('-').collect(&:to_i) (edges.min.to_i..edges.max.to_i).to_a end end class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end end Clump: module Justification class None def self.justify(lines) lines end end class BlockLeft def self.justify(lines) new(lines).justify end attr_reader :lines def initialize(lines) @lines = lines end def justify lines.map {|line| line.slice(num_leading_spaces_to_remove..-1) || "" } end private def num_leading_spaces_to_remove @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end end Justification: class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end Factories Procedure Total Lines: 134 Execution Paths: 1

Slide 350

Slide 350 text

@sandimetz May 2018 Lines: 83 Execution Paths: 16 module Source class File attr_reader :filename def initialize(filename:) @filename = filename end def lines ::File.read(filename).split("\n") end end class GitTag def self.git_cmd GitCmd.new end attr_reader :filename, :tag, :repository, :git_cmd def initialize(filename:, repository:, tag:, git_cmd: self.class.git_cmd) @git_cmd = git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename end def lines git_cmd.show.split("\n") end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end def git_dir %Q[--git-dir="#{repository}"] end end end end OO module Subset class Everything def lines(everything) everything end end class LineNumber attr_reader :line_numbers def initialize(line_numbers:) @line_numbers = line_numbers end def lines(possibilities) clump_specs.collect {|spec| clump_for(spec, possibilities) }.flatten.compact end def clump_specs line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") end def clump_for(spec, possibilities) Clump.lines(spec: spec, possibilities: possibilities) end end end Subset: Source: class Clump def self.lines(spec:, possibilities: []) self.for(spec: spec, possibilities: possibilities).lines end def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end attr_reader :spec, :input def initialize(spec:, input: []) @spec = spec @input = input end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.compact end def expand_clump(spec) edges = spec.split('-').collect(&:to_i) (edges.min.to_i..edges.max.to_i).to_a end end class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end end Clump: module Justification class None def self.justify(lines) lines end end class BlockLeft def self.justify(lines) new(lines).justify end attr_reader :lines def initialize(lines) @lines = lines end def justify lines.map {|line| line.slice(num_leading_spaces_to_remove..-1) || "" } end private def num_leading_spaces_to_remove @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end end Justification: class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end Factories Procedure Classes: 9 Total Lines: 134 Execution Paths: 1

Slide 351

Slide 351 text

@sandimetz May 2018 Lines: 83 Execution Paths: 16 module Source class File attr_reader :filename def initialize(filename:) @filename = filename end def lines ::File.read(filename).split("\n") end end class GitTag def self.git_cmd GitCmd.new end attr_reader :filename, :tag, :repository, :git_cmd def initialize(filename:, repository:, tag:, git_cmd: self.class.git_cmd) @git_cmd = git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename end def lines git_cmd.show.split("\n") end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end def git_dir %Q[--git-dir="#{repository}"] end end end end OO module Subset class Everything def lines(everything) everything end end class LineNumber attr_reader :line_numbers def initialize(line_numbers:) @line_numbers = line_numbers end def lines(possibilities) clump_specs.collect {|spec| clump_for(spec, possibilities) }.flatten.compact end def clump_specs line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") end def clump_for(spec, possibilities) Clump.lines(spec: spec, possibilities: possibilities) end end end Subset: Source: class Clump def self.lines(spec:, possibilities: []) self.for(spec: spec, possibilities: possibilities).lines end def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end attr_reader :spec, :input def initialize(spec:, input: []) @spec = spec @input = input end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.compact end def expand_clump(spec) edges = spec.split('-').collect(&:to_i) (edges.min.to_i..edges.max.to_i).to_a end end class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end end Clump: module Justification class None def self.justify(lines) lines end end class BlockLeft def self.justify(lines) new(lines).justify end attr_reader :lines def initialize(lines) @lines = lines end def justify lines.map {|line| line.slice(num_leading_spaces_to_remove..-1) || "" } end private def num_leading_spaces_to_remove @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end end Justification: class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end Factories Procedure Classes: 9 Biggest: 18 loc Total Lines: 134 Execution Paths: 1

Slide 352

Slide 352 text

@sandimetz May 2018 Lines: 83 Execution Paths: 16 module Source class File attr_reader :filename def initialize(filename:) @filename = filename end def lines ::File.read(filename).split("\n") end end class GitTag def self.git_cmd GitCmd.new end attr_reader :filename, :tag, :repository, :git_cmd def initialize(filename:, repository:, tag:, git_cmd: self.class.git_cmd) @git_cmd = git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename end def lines git_cmd.show.split("\n") end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end def git_dir %Q[--git-dir="#{repository}"] end end end end OO module Subset class Everything def lines(everything) everything end end class LineNumber attr_reader :line_numbers def initialize(line_numbers:) @line_numbers = line_numbers end def lines(possibilities) clump_specs.collect {|spec| clump_for(spec, possibilities) }.flatten.compact end def clump_specs line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") end def clump_for(spec, possibilities) Clump.lines(spec: spec, possibilities: possibilities) end end end Subset: Source: class Clump def self.lines(spec:, possibilities: []) self.for(spec: spec, possibilities: possibilities).lines end def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end attr_reader :spec, :input def initialize(spec:, input: []) @spec = spec @input = input end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.compact end def expand_clump(spec) edges = spec.split('-').collect(&:to_i) (edges.min.to_i..edges.max.to_i).to_a end end class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end end Clump: module Justification class None def self.justify(lines) lines end end class BlockLeft def self.justify(lines) new(lines).justify end attr_reader :lines def initialize(lines) @lines = lines end def justify lines.map {|line| line.slice(num_leading_spaces_to_remove..-1) || "" } end private def num_leading_spaces_to_remove @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end end Justification: class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end Factories Procedure

Slide 353

Slide 353 text

@sandimetz May 2018 class Clump def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end # ... end

Slide 354

Slide 354 text

@sandimetz May 2018 class Clump def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end # ... end Factories are shared Factories are isolated Factories are simple Factories are ignorable

Slide 355

Slide 355 text

@sandimetz May 2018 module Source class File attr_reader :filename def initialize(filename:) @filename = filename end def lines ::File.read(filename).split("\n") end end class GitTag def self.git_cmd GitCmd.new end attr_reader :filename, :tag, :repository, :git_cmd def initialize(filename:, repository:, tag:, git_cmd: self.class.git_cmd) @git_cmd = git_cmd git_cmd.repository = repository git_cmd.tagname = tag git_cmd.filename = filename end def lines git_cmd.show.split("\n") end class GitCmd attr_accessor :repository, :tagname, :filename def show `git #{git_dir} show #{tagname}:#{filename}` end def git_dir %Q[--git-dir="#{repository}"] end end end end OO module Subset class Everything def lines(everything) everything end end class LineNumber attr_reader :line_numbers def initialize(line_numbers:) @line_numbers = line_numbers end def lines(possibilities) clump_specs.collect {|spec| clump_for(spec, possibilities) }.flatten.compact end def clump_specs line_numbers.gsub(/[‘|’]/, "").gsub(/ /,'').split(",") end def clump_for(spec, possibilities) Clump.lines(spec: spec, possibilities: possibilities) end end end Subset: Source: class Clump def self.lines(spec:, possibilities: []) self.for(spec: spec, possibilities: possibilities).lines end def self.for(spec:, possibilities: []) if spec.include?('#') Clump::Comment else Clump::LineNumber end.new(spec: spec, input: possibilities) end attr_reader :spec, :input def initialize(spec:, input: []) @spec = spec @input = input end class LineNumber < Clump def lines expand_clump(spec).collect {|i| input[i - 1]}.compact end def expand_clump(spec) edges = spec.split('-').collect(&:to_i) (edges.min.to_i..edges.max.to_i).to_a end end class Comment < Clump def lines num_spaces = spec.delete("#").to_i (" " * num_spaces) + "# ..." end end end Clump: module Justification class None def self.justify(lines) lines end end class BlockLeft def self.justify(lines) new(lines).justify end attr_reader :lines def initialize(lines) @lines = lines end def justify lines.map {|line| line.slice(num_leading_spaces_to_remove..-1) || "" } end private def num_leading_spaces_to_remove @num ||= lines.reduce(999999) {|current_min, line| line.empty? ? current_min : [current_min, num_leading_spaces(line)].min } end def num_leading_spaces(line) line[/\A */].size end end end Justification: class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end Factories

Slide 356

Slide 356 text

@sandimetz May 2018 OO class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end

Slide 357

Slide 357 text

@sandimetz May 2018 OO class Listing attr_reader :source, :subsetter, :justifier def initialize(source:, subsetter:, justifier:) @source = source @subsetter = subsetter @justifier = justifier end def lines justifier.justify(subsetter.lines(source.lines)) end end

Slide 358

Slide 358 text

@sandimetz May 2018 Anthropomorphic Polymorphic Loosely-coupled Role-playing Factory-created Message-sending

Slide 359

Slide 359 text

@sandimetz Thanks May 2018

Slide 360

Slide 360 text

@sandimetz http://poodr.com May 2018

Slide 361

Slide 361 text

@sandimetz http://99bottlesbook.com May 2018

Slide 362

Slide 362 text

@sandimetz Sandi Metz @sandimetz http://sandimetz.com May 2018