Polly Want A Message (DeconstructConf)

Polly Want A Message (DeconstructConf)

Object-oriented languages have opinions about how best to arrange code. These opinions lead OO to naturally offer certain _affordances_. Just as round doorknobs expect to be grasped and rotated, OO expects messages, polymorphism, loose-coupling and factories. The key to creating intuitive and maintainable OO applications is to understand and embrace these built-in affordances.

66ad01cf37c098bfdb76906a490e018f?s=128

Sandi Metz

May 21, 2018
Tweet

Transcript

  1. May 2018 @sandimetz Sandi Metz Polly want a Message

  2. @sandimetz May 2018 Path to Pain

  3. @sandimetz May 2018 Design Stamina Hypothesis - Martin Fowler

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

  5. @sandimetz May 2018

  6. @sandimetz May 2018

  7. @sandimetz May 2018 Over Design

  8. @sandimetz May 2018

  9. @sandimetz May 2018

  10. @sandimetz May 2018 Under Design

  11. @sandimetz May 2018

  12. @sandimetz May 2018

  13. @sandimetz May 2018

  14. @sandimetz May 2018

  15. @sandimetz May 2018

  16. @sandimetz May 2018 ???

  17. @sandimetz May 2018

  18. @sandimetz May 2018

  19. @sandimetz May 2018 Ouch

  20. @sandimetz May 2018 Procedures vs OO -Me

  21. @sandimetz May 2018 #2 Procedures vs OO -Me

  22. @sandimetz May 2018

  23. @sandimetz May 2018

  24. @sandimetz May 2018

  25. @sandimetz May 2018

  26. @sandimetz May 2018

  27. @sandimetz May 2018

  28. @sandimetz May 2018 Ouch

  29. @sandimetz May 2018 Churn vs. Complexity - Michael Feathers

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

  31. @sandimetz May 2018

  32. @sandimetz May 2018 Ouch

  33. @sandimetz May 2018 What matters, suffers -Me

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

  35. @sandimetz May 2018

  36. @sandimetz May 2018 GPA 1.8

  37. @sandimetz May 2018 GPA 1.8

  38. @sandimetz May 2018 GPA 1.8

  39. @sandimetz May 2018

  40. @sandimetz May 2018 GPA 2.58

  41. @sandimetz May 2018 GPA 2.58

  42. @sandimetz May 2018 GPA 2.58

  43. @sandimetz May 2018

  44. @sandimetz May 2018 GPA 2.97

  45. @sandimetz May 2018 GPA 2.97

  46. @sandimetz May 2018 GPA 2.97

  47. @sandimetz May 2018

  48. @sandimetz May 2018 #1 Design Stamina Hypothesis

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

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

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

    OO #3 Churn vs Complexity #4 What matters, suffers
  52. @sandimetz May 2018 Interlude

  53. @sandimetz May 2018 Wherein things go badly wrong Interlude

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

  55. @sandimetz May 2018 Change #1

  56. @sandimetz May 2018 Change #1 Read from Git Tag

  57. @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
  58. @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
  59. @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
  60. @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
  61. @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
  62. @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
  63. @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
  64. @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
  65. @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
  66. @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
  67. @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
  68. @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
  69. @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
  70. @sandimetz May 2018 Change #2

  71. @sandimetz May 2018 Change #2 Partial listings

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

    37-50"
  73. @sandimetz May 2018 Easy is the enemy of simple

  74. @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
  75. @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
  76. @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
  77. @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
  78. @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
  79. @sandimetz May 2018 class Listing # ...

  80. @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
  81. @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
  82. @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
  83. @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
  84. @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
  85. @sandimetz May 2018 class Listing # ... def lines if

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

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

    git_cmd git_lines else File.read(filename).split("\n") end end
  88. @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
  89. @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
  90. @sandimetz May 2018 class Listing # ... def lines if

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

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

    = if git_cmd git_lines else file_lines end end
  93. @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
  94. @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
  95. @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
  96. @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
  97. @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
  98. @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
  99. @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
  100. @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
  101. @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
  102. @sandimetz May 2018 Change #3

  103. @sandimetz May 2018 Change #3 Dynamic Comments

  104. @sandimetz May 2018 Change #3 Dynamic Comments "1, 3-4, 15,

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

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

    #6, 15, 37-50"
  107. @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
  108. @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
  109. @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
  110. @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
  111. @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
  112. @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
  113. @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
  114. @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
  115. @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
  116. @sandimetz May 2018 Change #4

  117. @sandimetz May 2018 Change #4 Left Justification

  118. @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 # ...
  119. @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 # ...
  120. @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 # ...
  121. @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 # ...
  122. @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 # ...
  123. @sandimetz May 2018 class Listing # ...

  124. @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
  125. @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
  126. @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
  127. @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
  128. @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
  129. @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
  130. @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
  131. @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
  132. @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
  133. @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
  134. @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
  135. @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
  136. @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
  137. @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
  138. @sandimetz May 2018 Progression

  139. @sandimetz May 2018 1 File or Git Tag

  140. @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
  141. @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
  142. @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
  143. @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
  144. @sandimetz May 2018 2All or Some Lines

  145. @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
  146. @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:
  147. @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
  148. @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
  149. @sandimetz May 2018 3Code Line or Comment

  150. @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
  151. @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:
  152. @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
  153. @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
  154. @sandimetz May 2018 4Raw or Left Just

  155. @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
  156. @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:
  157. @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
  158. @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
  159. @sandimetz May 2018

  160. @sandimetz May 2018 Ouch

  161. @sandimetz May 2018

  162. @sandimetz May 2018 Ouch

  163. @sandimetz May 2018

  164. @sandimetz May 2018 Ouch

  165. @sandimetz May 2018

  166. @sandimetz May 2018 What brought you success

  167. @sandimetz May 2018 What brought you success will doom you

    to failure.
  168. @sandimetz May 2018 Affordances

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

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

    by www.trek.today CC BY-NC-ND
  171. @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
  172. @sandimetz May 2018 I'm a loser and lost the attribution.

    Sorry.
  173. @sandimetz May 2018 "pull" by greenkozi CC BY-NC-ND I'm a

    loser and lost the attribution. Sorry.
  174. @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.
  175. @sandimetz May 2018 OO Affords

  176. @sandimetz May 2018 OO Affords Anthropomorphic

  177. @sandimetz May 2018 OO Affords Anthropomorphic Polymorphic

  178. @sandimetz May 2018 OO Affords Anthropomorphic Polymorphic Loosely-coupled

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

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

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

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

    Message-sending Objects
  183. @sandimetz May 2018 Anthropomorphism

  184. @sandimetz May 2018 Anthropomorphism -the attribution of human traits, emotions

    or intentions to non-human entities.
  185. @sandimetz May 2018

  186. @sandimetz May 2018 Polymorphism

  187. @sandimetz May 2018 Polymorphism -the condition of occurring in several

    different forms
  188. @sandimetz May 2018

  189. @sandimetz May 2018

  190. @sandimetz May 2018

  191. @sandimetz May 2018 Loosely-Coupled

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

  193. @sandimetz May 2018

  194. @sandimetz May 2018 Role-playing

  195. @sandimetz May 2018 Role-playing -objects are more players of their

    roles than instances of their types
  196. @sandimetz May 2018

  197. @sandimetz May 2018 Factory-created

  198. @sandimetz May 2018 Factory-created -factories hide the rules for picking

    the right player of a role
  199. @sandimetz May 2018

  200. @sandimetz May 2018

  201. @sandimetz May 2018 Message-sending

  202. @sandimetz May 2018 Message-sending -I know what I want, you

    know how to do it
  203. @sandimetz May 2018

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

    Message-sending Objects
  205. @sandimetz May 2018 Resolution

  206. @sandimetz May 2018 Where all ends well Resolution

  207. @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
  208. @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
  209. @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
  210. @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
  211. @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
  212. @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
  213. @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
  214. @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
  215. @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
  216. @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
  217. @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
  218. @sandimetz May 2018 Source

  219. @sandimetz May 2018 Source #lines

  220. @sandimetz May 2018 Source #lines File Source GitTag Source

  221. @sandimetz May 2018 class FileSource end class GitTagSource end S

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

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

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

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

    end class GitTagSource def lines end # ... end S O U R C E S Polymorphism
  226. @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
  227. @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
  228. @sandimetz May 2018 Isolate the things you want to vary

  229. @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
  230. @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 = # ...
  231. @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 = # ...
  232. @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 = # ...
  233. @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 = # ...
  234. @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
  235. @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 = # ...
  236. @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 = # ...
  237. @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 = # ...
  238. @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 = # ...
  239. @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 = # ...
  240. @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 = # ...
  241. @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 = # ...
  242. @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 = # ...
  243. @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?
  244. @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
  245. @sandimetz May 2018 Push conditionals back on the stack

  246. @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 =
  247. @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
  248. @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
  249. @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
  250. @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
  251. @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
  252. @sandimetz May 2018 Subset

  253. @sandimetz May 2018 Subset #lines

  254. @sandimetz May 2018 Subset #lines Everything Line Number

  255. @sandimetz May 2018 module Subset class Everything end class LineNumber

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

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

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

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

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

    end S U B S E T
  261. @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
  262. @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
  263. @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 ?
  264. @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
  265. @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
  266. @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
  267. @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
  268. @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
  269. @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
  270. @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
  271. @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
  272. @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
  273. @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
  274. @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
  275. @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
  276. @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
  277. @sandimetz May 2018 Dependency injection is your friend

  278. @sandimetz May 2018 As long as you inject smart things

  279. @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
  280. @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
  281. @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
  282. @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
  283. @sandimetz May 2018 Justifier

  284. @sandimetz May 2018 Justifier #justify

  285. @sandimetz May 2018 Justifier #justify None Block Left

  286. @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
  287. @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
  288. @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
  289. @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
  290. @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
  291. @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
  292. @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
  293. @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
  294. @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
  295. @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
  296. @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
  297. @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
  298. @sandimetz May 2018 But, conditionals?

  299. @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
  300. @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
  301. @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
  302. @sandimetz May 2018 Clump

  303. @sandimetz May 2018 Clump #lines

  304. @sandimetz May 2018 Clump #lines Comment Line Number

  305. @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
  306. @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
  307. @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
  308. @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
  309. @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
  310. @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
  311. @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
  312. @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
  313. @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
  314. @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
  315. @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
  316. @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
  317. @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
  318. @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
  319. @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
  320. @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
  321. @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
  322. @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
  323. @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
  324. @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
  325. @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
  326. @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
  327. @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
  328. @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
  329. @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
  330. @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
  331. @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
  332. @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
  333. @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
  334. @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
  335. @sandimetz May 2018 Reprise

  336. @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
  337. @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
  338. @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
  339. @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
  340. @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:
  341. @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
  342. @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
  343. @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
  344. @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
  345. @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
  346. @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
  347. @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
  348. @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
  349. @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
  350. @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
  351. @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
  352. @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
  353. @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
  354. @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
  355. @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
  356. @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
  357. @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
  358. @sandimetz May 2018 Anthropomorphic Polymorphic Loosely-coupled Role-playing Factory-created Message-sending

  359. @sandimetz Thanks May 2018

  360. @sandimetz http://poodr.com May 2018

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

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