Small Code - RailsConf 2014

644 views

Published on

Writing small classes is hard. You know you should, but how? It's so much easier to write a large class. In this talk we'll build up a set of small classes starting from nothing using a set of directed refactorings applied as we build, all while keeping our tests green. We'll identify abstractions yearning to be free of their big class cages. In the process we'll also see how basic patterns such as composition, delegation and dependency inversion emerge from using small objects.

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
644
On SlideShare
0
From Embeds
0
Number of Embeds
30
Actions
Shares
0
Downloads
5
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Small Code - RailsConf 2014

  1. 1. Enable Labs @mark_menard #smallcode Small Code Mark Menard RailsConf 2014! April 24, 2014 @mark_menard ! Enable Labs Refactoring to Small Code 16x9 - April 24, 2014
  2. 2. Enable Labs @mark_menard #smallcode ‘The great thing about writing shitty code that “just works,” is that it is too risky and too expensive to change, so it lives forever.’! ! ! -Reginald Braithwaite @raganwald Refactoring to Small Code 16x9 - April 24, 2014
  3. 3. Enable Labs @mark_menard #smallcode What do I mean by small? Refactoring to Small Code 16x9 - April 24, 2014
  4. 4. Enable Labs @mark_menard #smallcode It’s not about total line count. Refactoring to Small Code 16x9 - April 24, 2014
  5. 5. Enable Labs @mark_menard #smallcode It’s not about method count. Refactoring to Small Code 16x9 - April 24, 2014
  6. 6. Enable Labs @mark_menard #smallcode It’s not about class count. Refactoring to Small Code 16x9 - April 24, 2014
  7. 7. Enable Labs @mark_menard #smallcode It’s About Small methods! Small classes Refactoring to Small Code 16x9 - April 24, 2014
  8. 8. Enable Labs @mark_menard #smallcode Why should we strive for small code? Refactoring to Small Code 16x9 - April 24, 2014
  9. 9. Enable Labs @mark_menard #smallcode We don’t know what the future will bring. Refactoring to Small Code 16x9 - April 24, 2014
  10. 10. Enable Labs @mark_menard #smallcode Raise the level of abstraction. Refactoring to Small Code 16x9 - April 24, 2014
  11. 11. Enable Labs @mark_menard #smallcode Create composable objects. Refactoring to Small Code 16x9 - April 24, 2014
  12. 12. Enable Labs @mark_menard #smallcode Enable Future Change Refactoring to Small Code 16x9 - April 24, 2014
  13. 13. Enable Labs @mark_menard #smallcode The goal: Small units of understandable code that are amenable to change. Refactoring to Small Code 16x9 - April 24, 2014
  14. 14. Enable Labs @mark_menard #smallcode Our Primary Tools • Extract Method! • Move Method! • Extract Class Refactoring to Small Code 16x9 - April 24, 2014
  15. 15. Enable Labs @mark_menard #smallcode Let’s Look at Some Code Refactoring to Small Code 16x9 - April 24, 2014
  16. 16. Enable Labs @mark_menard #smallcode # some_ruby_program -v Refactoring to Small Code 16x9 - April 24, 2014
  17. 17. Enable Labs @mark_menard #smallcode options = CommandLineOptions.new(ARGV) do option :c option :v option :e end Refactoring to Small Code 16x9 - April 24, 2014
  18. 18. Enable Labs @mark_menard #smallcode if options.has(:c) # Do something end ! if options.has(:v) # Do something else end ! if options.has(:e) # Do the other thing end Refactoring to Small Code 16x9 - April 24, 2014
  19. 19. Enable Labs @mark_menard #smallcode# some_ruby_program -v ! options = CommandLineOptions.new(ARGV) do option :c option :v option :e end ! if options.has(:c) # Do something end ! if options.has(:v) # Do something else end ! if options.has(:e) # Do the other thing end Refactoring to Small Code 16x9 - April 24, 2014
  20. 20. Enable Labs @mark_menard #smallcode describe CommandLineOptions do ! describe 'options' do ! let(:parser) { CommandLineOptions.new { option :c } } ! it "are true if present" do … ! it "are false if absent" do … end end Refactoring to Small Code 16x9 - April 24, 2014
  21. 21. Enable Labs @mark_menard #smallcode CommandLineOptions options are true if present are false if absent ! Finished in 0.00206 seconds 2 examples, 2 failures Refactoring to Small Code 16x9 - April 24, 2014
  22. 22. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! attr_accessor :argv attr_reader :options ! def initialize (argv = [], &block) @options = [] @argv = argv instance_eval &block end ! def has (option_flag) options.include?(option_flag) && argv.include?("-#{option_flag}") end ! def option (option_flag) options << option_flag end ! end Refactoring to Small Code 16x9 - April 24, 2014
  23. 23. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! attr_accessor :argv attr_reader :options ! def initialize (argv = [], &block) @options = [] @argv = argv instance_eval &block end ! def has (option_flag) options.include?(option_flag) && argv.include?("-#{option_flag}") end ! def option (option_flag) options << option_flag end ! end Refactoring to Small Code 16x9 - April 24, 2014
  24. 24. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! attr_accessor :argv attr_reader :options ! def initialize (argv = [], &block) @options = [] @argv = argv instance_eval &block end ! def has (option_flag) options.include?(option_flag) && argv.include?("-#{option_flag}") end ! def option (option_flag) options << option_flag end ! end Refactoring to Small Code 16x9 - April 24, 2014
  25. 25. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! attr_accessor :argv attr_reader :options ! def initialize (argv = [], &block) @options = [] @argv = argv instance_eval &block end ! def has (option_flag) options.include?(option_flag) && argv.include?("-#{option_flag}") end ! def option (option_flag) options << option_flag end ! end options = CommandLineOptions.new(ARGV) do option :c option :v option :e end Refactoring to Small Code 16x9 - April 24, 2014
  26. 26. Enable Labs @mark_menard #smallcode CommandLineOptions options are true if present are false if absent ! Finished in 0.00206 seconds 2 examples, 0 failures Refactoring to Small Code 16x9 - April 24, 2014
  27. 27. Enable Labs @mark_menard #smallcode Done! Refactoring to Small Code 16x9 - April 24, 2014
  28. 28. Enable Labs @mark_menard #smallcode % some_ruby_program -v -sfoo Refactoring to Small Code 16x9 - April 24, 2014
  29. 29. Enable Labs @mark_menard #smallcode options = CommandLineOptions.new(ARGV) do option :v option :s, :string end Refactoring to Small Code 16x9 - April 24, 2014
  30. 30. Enable Labs @mark_menard #smallcode if options.valid? if options.value(:v) # Do something end ! if (s_option = options.value(:s)) # Do something end end Refactoring to Small Code 16x9 - April 24, 2014
  31. 31. Enable Labs @mark_menard #smallcode if options.valid? if options.value(:v) # Do something end ! if (s_option = options.value(:s)) # Do something end end Refactoring to Small Code 16x9 - April 24, 2014
  32. 32. Enable Labs @mark_menard #smallcode if options.valid? if options.value(:v) # Do something end ! if (s_option = options.value(:s)) # Do something end end Refactoring to Small Code 16x9 - April 24, 2014
  33. 33. Enable Labs @mark_menard #smallcode # some_ruby_program -v -sfoo ! options = CommandLineOptions.new(ARGV) do option :v option :s, :string end ! if options.valid? if options.value(:v) # Do something end ! if (s_option = options.value(:s)) # Do something end end Refactoring to Small Code 16x9 - April 24, 2014
  34. 34. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! attr_accessor :argv attr_reader :options ! def initialize (argv = [], &block) @options = {} @argv = argv instance_eval &block end ! def option (option_flag, option_type = :boolean) options[option_flag] = option_type end ! def valid? options.each do |option_flag, option_type| raw_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return false if option_type == :string && raw_value && raw_value.length < 3 end end ! def value (option_flag) raw_option_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string end ! end Refactoring to Small Code 16x9 - April 24, 2014
  35. 35. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! … def initialize (argv = [], &block) @options = {} @argv = argv instance_eval &block end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  36. 36. Enable Labs @mark_menard #smallcode class CommandLineOptions ! … ! def valid? options.each do |option_flag, option_type| raw_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return false if option_type == :string && raw_value && raw_value.length < 3 end end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  37. 37. Enable Labs @mark_menard #smallcode class CommandLineOptions ! … ! def value (option_flag) raw_option_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  38. 38. Enable Labs @mark_menard #smallcode CommandLineOptions boolean options are true if present are false if absent string options must have content are valid if there is content are valid if not in argv can return the value return nil if not in argv ! Finished in 0.00401 seconds 7 examples, 0 failures Refactoring to Small Code 16x9 - April 24, 2014
  39. 39. Enable Labs @mark_menard #smallcode Methods Refactoring to Small Code 16x9 - April 24, 2014
  40. 40. Enable Labs @mark_menard #smallcode The first rule of methods: Refactoring to Small Code 16x9 - April 24, 2014
  41. 41. Enable Labs @mark_menard #smallcode Do one thing. Do it well. Do only that thing. Refactoring to Small Code 16x9 - April 24, 2014
  42. 42. Enable Labs @mark_menard #smallcode One level of abstraction per method. Refactoring to Small Code 16x9 - April 24, 2014
  43. 43. Enable Labs @mark_menard #smallcode Use Descriptive Names Refactoring to Small Code 16x9 - April 24, 2014
  44. 44. Enable Labs @mark_menard #smallcode The fewer arguments the better. Refactoring to Small Code 16x9 - April 24, 2014
  45. 45. Enable Labs @mark_menard #smallcode Separate Queries from Commands Refactoring to Small Code 16x9 - April 24, 2014
  46. 46. Enable Labs @mark_menard #smallcode Don’t RepeatYourself Refactoring to Small Code 16x9 - April 24, 2014
  47. 47. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! … ! def valid? options.each do |option_flag, option_type| raw_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return false if option_type == :string && raw_value && raw_value.length < 3 end end ! def value (option_flag) raw_option_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  48. 48. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! … ! def valid? options.each do |option_flag, option_type| raw_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return false if option_type == :string && raw_value && raw_value.length < 3 end end ! def value (option_flag) raw_option_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  49. 49. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! … ! def valid? options.each do |option_flag, option_type| raw_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return false if option_type == :string && raw_value && raw_value.length < 3 end end ! def value (option_flag) raw_option_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  50. 50. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! … ! def valid? options.each do |option_flag, option_type| raw_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return false if option_type == :string && raw_value && raw_value.length < 3 end end ! def value (option_flag) raw_option_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  51. 51. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! … ! def valid? options.each do |option_flag, option_type| raw_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return false if option_type == :string && raw_value && raw_value.length < 3 end end ! def value (option_flag) raw_option_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  52. 52. Enable Labs @mark_menard #smallcode Extract Method Refactoring def print_invoice_for_amount (amount) print_header puts "Name: #{@name}" puts "Amount: #{amount}" end Refactoring to Small Code 16x9 - April 24, 2014
  53. 53. Enable Labs @mark_menard #smallcode Extract Method Refactoring def print_invoice_for_amount (amount) print_header puts "Name: #{@name}" puts "Amount: #{amount}" end High level of abstraction Refactoring to Small Code 16x9 - April 24, 2014
  54. 54. Enable Labs @mark_menard #smallcode Extract Method Refactoring def print_invoice_for_amount (amount) print_header puts "Name: #{@name}" puts "Amount: #{amount}" end Low level of abstraction Refactoring to Small Code 16x9 - April 24, 2014
  55. 55. Enable Labs @mark_menard #smallcode Extract Method Refactoring def print_invoice_for_amount (amount) print_header puts "Name: #{@name}" puts "Amount: #{amount}" end def print_invoice_for_amount (amount) print_header print_details (amount) end ! def print_details (amount) puts "Name: #{@name}" puts "Amount: #{amount}" end Move this to here Refactoring to Small Code 16x9 - April 24, 2014
  56. 56. Enable Labs @mark_menard #smallcode Extract Method Refactoring def print_invoice_for_amount (amount) print_header puts "Name: #{@name}" puts "Amount: #{amount}" end def print_invoice_for_amount (amount) print_header print_details (amount) end ! def print_details (amount) puts "Name: #{@name}" puts "Amount: #{amount}" end Same level of abstraction Move this to here Refactoring to Small Code 16x9 - April 24, 2014
  57. 57. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! … ! def valid? options.each do |option_flag, option_type| raw_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return false if option_type == :string && raw_value && raw_value.length < 3 end end ! def value (option_flag) raw_option_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  58. 58. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! … ! def valid? options.each do |option_flag, option_type| raw_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return false if option_type == :string && raw_value && raw_value.length < 3 end end ! def value (option_flag) raw_option_value = argv.find { |arg| arg =~ /^-#{option_flag}/ } return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  59. 59. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! … def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) return false if option_type == :string && raw_option_value && raw_option_value.length < 3 end end ! def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string end ! def raw_value_for_option (option_flag) argv.find { |arg| arg =~ /^-#{option_flag}/ } end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  60. 60. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! … def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) return false if option_type == :string && raw_option_value && raw_option_value.length < 3 end end ! def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string end ! def raw_value_for_option (option_flag) argv.find { |arg| arg =~ /^-#{option_flag}/ } end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  61. 61. Enable Labs @mark_menard #smallcode class CommandLineOptions ! … def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) return false if option_type == :string && !string_option_valid?(raw_option_value) end end ! def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return extract_content(raw_option_value) if option_type == :string end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  62. 62. Enable Labs @mark_menard #smallcode class CommandLineOptions ! … def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) return false if option_type == :string && !string_option_valid?(raw_option_value) end end ! def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return extract_content(raw_option_value) if option_type == :string end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  63. 63. Enable Labs @mark_menard #smallcode class CommandLineOptions ! … def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) return false if option_type == :string && !string_option_valid?(raw_option_value) end end ! def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return extract_content(raw_option_value) if option_type == :string end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  64. 64. Enable Labs @mark_menard #smallcode class CommandLineOptions ! … def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) return false if option_type == :string && !string_option_valid?(raw_option_value) end end ! def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return extract_content(raw_option_value) if option_type == :string end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  65. 65. Enable Labs @mark_menard #smallcode class CommandLineOptions ! … def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) return false if option_type == :string && !string_option_valid?(raw_option_value) end end ! def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return extract_content(raw_option_value) if option_type == :string end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  66. 66. Enable Labs @mark_menard #smallcode # some_ruby_program -v -efoo -i100 ! options = CommandLineOptions.new(ARGV) do option :v option :e, :string option :i, :integer end ! verbose = options.value(:v) expression = options.value(:e) iteration_count = options.value(:i) || 1 Refactoring to Small Code 16x9 - April 24, 2014
  67. 67. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! attr_accessor :argv attr_reader :options ! def initialize (argv = [], &block) @options = {} @argv = argv instance_eval &block end ! def option (option_flag, option_type = :boolean) options[option_flag] = option_type end ! def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) return false if (option_type == :string || option_type == :integer) && raw_option_value && raw_option_value.length < 3 return false if option_type == :integer && raw_option_value && !(Integer(raw_option_value[2..-1]) rescue false) end end ! def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string return (Integer(raw_option_value[2..-1])) if option_type == :integer end ! private def raw_value_for_option (option_flag) argv.find { |arg| arg =~ /^-#{option_flag}/ } end ! end Refactoring to Small Code 16x9 - April 24, 2014
  68. 68. Enable Labs @mark_menard #smallcodeCommandLineOptions boolean options are true if present are false if absent string options must have content are valid if there is content are valid if not in argv can return the value return nil if not in argv integer options must have content are valid if there is content and it's an integer are invalid if the content is not an integer are valid if not in argv can return the value return nil if not in argv ! Finished in 0.00338 seconds 13 examples, 0 failures Refactoring to Small Code 16x9 - April 24, 2014
  69. 69. Enable Labs @mark_menard #smallcode class CommandLineOptions ! … ! def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) return false if (option_type == :string || option_type == :integer) && raw_option_value && raw_option_value.length < 3 return false if option_type == :integer && raw_option_value && !(Integer(raw_option_value[2..-1]) rescue false) end end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  70. 70. Enable Labs @mark_menard #smallcode class CommandLineOptions ! … ! def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) return false if (option_type == :string || option_type == :integer) && raw_option_value && raw_option_value.length < 3 return false if option_type == :integer && raw_option_value && !(Integer(raw_option_value[2..-1]) rescue false) end end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  71. 71. Enable Labs @mark_menard #smallcode class CommandLineOptions ! … ! def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) return false if (option_type == :string || option_type == :integer) && raw_option_value && raw_option_value.length < 3 return false if option_type == :integer && raw_option_value && !(Integer(raw_option_value[2..-1]) rescue false) end end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  72. 72. Enable Labs @mark_menard #smallcode class CommandLineOptions ! … ! def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string return (Integer(raw_option_value[2..-1])) if option_type == :integer end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  73. 73. Enable Labs @mark_menard #smallcode class CommandLineOptions ! … ! def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string return (Integer(raw_option_value[2..-1])) if option_type == :integer end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  74. 74. Enable Labs @mark_menard #smallcode class CommandLineOptions ! … ! def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string return (Integer(raw_option_value[2..-1])) if option_type == :integer end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  75. 75. Enable Labs @mark_menard #smallcodeclass CommandLineOptions … ! def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) ! case(option_type) when :string return false if raw_option_value && raw_option_value.length < 3 when :integer return false if raw_option_value && raw_option_value.length < 3 return false if raw_option_value && !(Integer(raw_option_value[2..-1]) rescue false) end end end ! … end Refactoring to Small Code 16x9 - April 24, 2014
  76. 76. Enable Labs @mark_menard #smallcodeclass CommandLineOptions … ! def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] ! return case(option_type) when :string raw_option_value[2..-1] when :integer (Integer(raw_option_value[2..-1])) when :boolean return true if option_type == :boolean && raw_option_value end end ! … end Refactoring to Small Code 16x9 - April 24, 2014
  77. 77. Enable Labs @mark_menard #smallcode How do we write small classes? Refactoring to Small Code 16x9 - April 24, 2014
  78. 78. Enable Labs @mark_menard #smallcode What are the characteristics of a small class? Refactoring to Small Code 16x9 - April 24, 2014
  79. 79. Enable Labs @mark_menard #smallcode class CommandLineOptions ! attr_accessor :argv attr_reader :options ! def initialize (argv = [], &block) @options = {} @argv = argv instance_eval &block end ! def valid? options.all?(&:valid) end ! def value (option_flag) options[option_flag].value end ! private def option (option_flag, option_type = :boolean) options[option_flag] = build_option(option_flag, option_type) end ! def build_option # Need to write this. end ! end Refactoring to Small Code 16x9 - April 24, 2014
  80. 80. Enable Labs @mark_menard #smallcode class CommandLineOptions ! attr_accessor :argv attr_reader :options ! def initialize (argv = [], &block) @options = {} @argv = argv instance_eval &block end ! def valid? options.all?(&:valid) end ! def value (option_flag) options[option_flag].value end ! private def option (option_flag, option_type = :boolean) options[option_flag] = build_option(option_flag, option_type) end ! def build_option # Need to write this. end ! end Refactoring to Small Code 16x9 - April 24, 2014
  81. 81. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! … ! def valid? options.all?(&:valid?) end ! def value (option_flag) options[option_flag].value end ! def build_option # Need to write this. end ! end Refactoring to Small Code 16x9 - April 24, 2014
  82. 82. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! … ! def valid? options.all?(&:valid?) end ! def value (option_flag) options[option_flag].value end ! def build_option # Need to write this. end ! end Refactoring to Small Code 16x9 - April 24, 2014
  83. 83. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! … ! def valid? options.all?(&:valid?) end ! def value (option_flag) options[option_flag].value end ! def build_option # Need to write this. end ! end Refactoring to Small Code 16x9 - April 24, 2014
  84. 84. Enable Labs @mark_menard #smallcodeclass CommandLineOptions ! … ! def valid? options.all?(&:valid?) end ! def value (option_flag) options[option_flag].value end ! def build_option # Need to write this. end ! end Refactoring to Small Code 16x9 - April 24, 2014
  85. 85. Enable Labs @mark_menard #smallcode Dependencies Refactoring to Small Code 16x9 - April 24, 2014
  86. 86. Enable Labs @mark_menard #smallcode Dependencies Command Line Options ?* Refactoring to Small Code 16x9 - April 24, 2014
  87. 87. Enable Labs @mark_menard #smallcode Depend on Abstractions Refactoring to Small Code 16x9 - April 24, 2014
  88. 88. Enable Labs @mark_menard #smallcode Command Line Options String Option Integer Option Boolean Option valid? value Option* Depend on Abstractions Refactoring to Small Code 16x9 - April 24, 2014
  89. 89. Enable Labs @mark_menard #smallcode Command Line Options String Option Integer Option Boolean Option valid? value Option* This is my duck type! Depend on Abstractions Refactoring to Small Code 16x9 - April 24, 2014
  90. 90. Enable Labs @mark_menard #smallcode Command Line Options String Option Integer Option Boolean Option valid? value Option* This is my duck type! These are my concrete types. Depend on Abstractions Refactoring to Small Code 16x9 - April 24, 2014
  91. 91. Enable Labs @mark_menard #smallcode Command Line Options String Option Integer Option Boolean Option valid? value Option* These are my concrete types. Depend on this! Depend on Abstractions Refactoring to Small Code 16x9 - April 24, 2014
  92. 92. Enable Labs @mark_menard #smallcode Command Line Options String Option Integer Option Boolean Option valid? value Option* Depend on this! Don’t depend on these! Depend on Abstractions Refactoring to Small Code 16x9 - April 24, 2014
  93. 93. Enable Labs @mark_menard #smallcode private def option (option_flag, option_type = :boolean) options[option_flag] = case (option_type) when :boolean return BooleanOption.new(option_flag, nil) when :string return StringOption.new(option_flag, nil) when :integer return IntegerOption.new(option_flag, nil) end end Refactoring to Small Code 16x9 - April 24, 2014
  94. 94. Enable Labs @mark_menard #smallcode class CommandLineOptions ! … ! def build_option (option_flag, option_type) “#{option_type}_option".camelize.constantize.new( option_flag, raw_value_for_option(option_flag) ) end ! … ! end Refactoring to Small Code 16x9 - April 24, 2014
  95. 95. Enable Labs @mark_menard #smallcodeclass Option ! attr_reader :flag, :raw_value ! def initialize (flag, raw_value) @flag = flag @raw_value = raw_value end ! def valid? end ! def value end ! end Refactoring to Small Code 16x9 - April 24, 2014
  96. 96. Enable Labs @mark_menard #smallcodeclass BooleanOption < Option ! def valid? true end ! def value raw_value end end class Option ! attr_reader :flag, :raw_value ! def initialize (flag, raw_value) @flag = flag @raw_value = raw_value end ! def valid? end ! def value end ! end Refactoring to Small Code 16x9 - April 24, 2014
  97. 97. Enable Labs @mark_menard #smallcodeclass StringOption < Option ! def valid? ! ! end ! def value ! ! end end class IntegerOption < Option ! def valid? ! ! end ! def value ! ! end end Refactoring to Small Code 16x9 - April 24, 2014
  98. 98. Enable Labs @mark_menard #smallcodedef valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) ! case(option_type) when :string ! ! when :integer ! ! ! end end end def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) ! case(option_type) when :string return false if raw_option_value && raw_option_value.length < 3 when :integer return false if raw_option_value && raw_option_value.length < 3 return false if raw_option_value && !(Integer(raw_option_value[2..-1]) rescue false) end end end def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] ! return case(option_type) when :string ! when :integer ! when :boolean ! end end def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] ! return case(option_type) when :string raw_option_value[2..-1] when :integer (Integer(raw_option_value[2..-1])) when :boolean return true if option_type == :boolean && raw_option_value end end class StringOption < Option ! def valid? ! ! end ! def value ! ! end end ! class IntegerOption < Option ! def valid? ! ! ! end ! def value ! ! end end Refactoring to Small Code 16x9 - April 24, 2014
  99. 99. Enable Labs @mark_menard #smallcodedef valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) ! case(option_type) when :string ! ! when :integer ! ! ! end end end def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] ! return case(option_type) when :string ! when :integer ! when :boolean ! end end def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] ! return case(option_type) when :string raw_option_value[2..-1] when :integer (Integer(raw_option_value[2..-1])) when :boolean return true if option_type == :boolean && raw_option_value end end class StringOption < Option ! def valid? ! ! end ! def value ! ! end end ! class IntegerOption < Option ! def valid? ! ! ! end ! def value ! ! end end class StringOption < Option ! def valid? return true unless raw_value raw_value && raw_value.length > 2 end ! def value ! ! end end ! class IntegerOption < Option ! def valid? return true unless raw_value (raw_value && raw_value.length > 2) && (Integer(raw_value[2..-1]) rescue false) end ! def value ! ! end end Refactoring to Small Code 16x9 - April 24, 2014
  100. 100. Enable Labs @mark_menard #smallcodedef valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) ! case(option_type) when :string ! ! when :integer ! ! ! end end end def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] ! return case(option_type) when :string ! when :integer ! when :boolean ! end end class StringOption < Option ! def valid? ! ! end ! def value ! ! end end ! class IntegerOption < Option ! def valid? ! ! ! end ! def value ! ! end end class StringOption < Option ! def valid? return true unless raw_value raw_value && raw_value.length > 2 end ! def value ! ! end end ! class IntegerOption < Option ! def valid? return true unless raw_value (raw_value && raw_value.length > 2) && (Integer(raw_value[2..-1]) rescue false) end ! def value ! ! end end class StringOption < Option ! def valid? return true unless raw_value raw_value && raw_value.length > 2 end ! def value return nil unless raw_value raw_value[2..-1] end end ! class IntegerOption < Option ! def valid? return true unless raw_value (raw_value && raw_value.length > 2) && (Integer(raw_value[2..-1]) rescue false) end ! def value return nil unless raw_value Integer(raw_value[2..-1]) end end Refactoring to Small Code 16x9 - April 24, 2014
  101. 101. Enable Labs @mark_menard #smallcodedef valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) ! case(option_type) when :string ! ! when :integer ! ! ! end end end def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] ! return case(option_type) when :string ! when :integer ! when :boolean ! end end class StringOption < Option ! def valid? ! ! end ! def value ! ! end end ! class IntegerOption < Option ! def valid? ! ! ! end ! def value ! ! end end class StringOption < Option ! def valid? return true unless raw_value raw_value && raw_value.length > 2 end ! def value ! ! end end ! class IntegerOption < Option ! def valid? return true unless raw_value (raw_value && raw_value.length > 2) && (Integer(raw_value[2..-1]) rescue false) end ! def value ! ! end end class StringOption < Option ! def valid? return true unless raw_value raw_value && raw_value.length > 2 end ! def value return nil unless raw_value raw_value[2..-1] end end ! class IntegerOption < Option ! def valid? return true unless raw_value (raw_value && raw_value.length > 2) && (Integer(raw_value[2..-1]) rescue false) end ! def value return nil unless raw_value Integer(raw_value[2..-1]) end end Refactoring to Small Code 16x9 - April 24, 2014
  102. 102. Enable Labs @mark_menard #smallcodedef valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) ! case(option_type) when :string ! ! when :integer ! ! ! end end end def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] ! return case(option_type) when :string ! when :integer ! when :boolean ! end end class StringOption < Option ! def valid? ! ! end ! def value ! ! end end ! class IntegerOption < Option ! def valid? ! ! ! end ! def value ! ! end end class StringOption < Option ! def valid? return true unless raw_value raw_value && raw_value.length > 2 end ! def value ! ! end end ! class IntegerOption < Option ! def valid? return true unless raw_value (raw_value && raw_value.length > 2) && (Integer(raw_value[2..-1]) rescue false) end ! def value ! ! end end class StringOption < Option ! def valid? return true unless raw_value raw_value && raw_value.length > 2 end ! def value return nil unless raw_value raw_value[2..-1] end end ! class IntegerOption < Option ! def valid? return true unless raw_value (raw_value && raw_value.length > 2) && (Integer(raw_value[2..-1]) rescue false) end ! def value return nil unless raw_value Integer(raw_value[2..-1]) end end Refactoring to Small Code 16x9 - April 24, 2014
  103. 103. Enable Labs @mark_menard #smallcode def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] ! return case(option_type) when :string ! when :integer ! when :boolean ! end end class StringOption < Option ! def valid? ! ! end ! def value ! ! end end ! class IntegerOption < Option ! def valid? ! ! ! end ! def value ! ! end end def valid? options.all?(&:valid?) end class StringOption < Option ! def valid? return true unless raw_value raw_value && raw_value.length > 2 end ! def value ! ! end end ! class IntegerOption < Option ! def valid? return true unless raw_value (raw_value && raw_value.length > 2) && (Integer(raw_value[2..-1]) rescue false) end ! def value ! ! end end class StringOption < Option ! def valid? return true unless raw_value raw_value && raw_value.length > 2 end ! def value return nil unless raw_value raw_value[2..-1] end end ! class IntegerOption < Option ! def valid? return true unless raw_value (raw_value && raw_value.length > 2) && (Integer(raw_value[2..-1]) rescue false) end ! def value return nil unless raw_value Integer(raw_value[2..-1]) end end Refactoring to Small Code 16x9 - April 24, 2014
  104. 104. Enable Labs @mark_menard #smallcode def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] ! return case(option_type) when :string ! when :integer ! when :boolean ! end end class StringOption < Option ! def valid? ! ! end ! def value ! ! end end ! class IntegerOption < Option ! def valid? ! ! ! end ! def value ! ! end end def valid? options.all?(&:valid?) end class StringOption < Option ! def valid? return true unless raw_value raw_value && raw_value.length > 2 end ! def value ! ! end end ! class IntegerOption < Option ! def valid? return true unless raw_value (raw_value && raw_value.length > 2) && (Integer(raw_value[2..-1]) rescue false) end ! def value ! ! end end class StringOption < Option ! def valid? return true unless raw_value raw_value && raw_value.length > 2 end ! def value return nil unless raw_value raw_value[2..-1] end end ! class IntegerOption < Option ! def valid? return true unless raw_value (raw_value && raw_value.length > 2) && (Integer(raw_value[2..-1]) rescue false) end ! def value return nil unless raw_value Integer(raw_value[2..-1]) end end Refactoring to Small Code 16x9 - April 24, 2014
  105. 105. Enable Labs @mark_menard #smallcode def value (option_flag) options[option_flag].value end class StringOption < Option ! def valid? ! ! end ! def value ! ! end end ! class IntegerOption < Option ! def valid? ! ! ! end ! def value ! ! end end def valid? options.all?(&:valid?) end class StringOption < Option ! def valid? return true unless raw_value raw_value && raw_value.length > 2 end ! def value ! ! end end ! class IntegerOption < Option ! def valid? return true unless raw_value (raw_value && raw_value.length > 2) && (Integer(raw_value[2..-1]) rescue false) end ! def value ! ! end end class StringOption < Option ! def valid? return true unless raw_value raw_value && raw_value.length > 2 end ! def value return nil unless raw_value raw_value[2..-1] end end ! class IntegerOption < Option ! def valid? return true unless raw_value (raw_value && raw_value.length > 2) && (Integer(raw_value[2..-1]) rescue false) end ! def value return nil unless raw_value Integer(raw_value[2..-1]) end end Refactoring to Small Code 16x9 - April 24, 2014
  106. 106. Enable Labs @mark_menard #smallcode Option classes Option stores it's flag stores it's raw value BooleanOption is true if the raw value is present is false if the raw value is nil is valid StringOption invalid when there is no content is valid if there is content is valid if raw value is nil can return the value value is nil if raw value is nil IntegerOption is invalid without content is invalid if the content is not an integer is valid if there is content and it's an integer is valid if raw value is nil can return the value returns nil if raw value is nil ! Finished in 0.00495 seconds 22 examples, 0 failures, 6 pending Refactoring to Small Code 16x9 - April 24, 2014
  107. 107. Enable Labs @mark_menard #smallcode How do we isolate abstractions? Separate the “what” from the “how”. Refactoring to Small Code 16x9 - April 24, 2014
  108. 108. Enable Labs @mark_menard #smallcode def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) ! case(option_type) when :string return false if raw_option_value && raw_option_value.length < 3 when :integer return false if raw_option_value && raw_option_value.length < 3 return false if raw_option_value && !(Integer(raw_option_value[2..-1]) rescue false) end end end Refactoring to Small Code 16x9 - April 24, 2014
  109. 109. Enable Labs @mark_menard #smallcode def valid? options.values.all?(&:valid?) end def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) ! case(option_type) when :string return false if raw_option_value && raw_option_value.length < 3 when :integer return false if raw_option_value && raw_option_value.length < 3 return false if raw_option_value && !(Integer(raw_option_value[2..-1]) rescue false) end end end Refactoring to Small Code 16x9 - April 24, 2014
  110. 110. Enable Labs @mark_menard #smallcode def valid? options.values.all?(&:valid?) end def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) ! case(option_type) when :string return false if raw_option_value && raw_option_value.length < 3 when :integer return false if raw_option_value && raw_option_value.length < 3 return false if raw_option_value && !(Integer(raw_option_value[2..-1]) rescue false) end end end This all about HOW! Refactoring to Small Code 16x9 - April 24, 2014
  111. 111. Enable Labs @mark_menard #smallcode def valid? options.values.all?(&:valid?) end def valid? options.each do |option_flag, option_type| raw_option_value = raw_value_for_option(option_flag) ! case(option_type) when :string return false if raw_option_value && raw_option_value.length < 3 when :integer return false if raw_option_value && raw_option_value.length < 3 return false if raw_option_value && !(Integer(raw_option_value[2..-1]) rescue false) end end end This all about HOW! This is WHAT I want done. Refactoring to Small Code 16x9 - April 24, 2014
  112. 112. Enable Labs @mark_menard #smallcode def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string return (Integer(raw_option_value[2..-1])) if option_type == :integer end Refactoring to Small Code 16x9 - April 24, 2014
  113. 113. Enable Labs @mark_menard #smallcode def value (option_flag) options[option_flag].value end def value (option_flag) raw_option_value = raw_value_for_option(option_flag) return nil unless raw_option_value option_type = options[option_flag] return true if option_type == :boolean && raw_option_value return raw_option_value[2..-1] if option_type == :string return (Integer(raw_option_value[2..-1])) if option_type == :integer end Refactoring to Small Code 16x9 - April 24, 2014
  114. 114. Enable Labs @mark_menard #smallcode class CommandLineOptions ! attr_accessor :argv attr_reader :options ! def initialize (argv = [], &block) @options = {} @argv = argv instance_eval &block end ! def valid? options.values.all?(&:valid?) end ! def value (option_flag) options[option_flag].value end ! private ! def option (option_flag, option_type = :boolean) options[option_flag] = build_option(option_flag, option_type) end ! def build_option (option_flag, option_type) "#{option_type}_option".camelize.constantize.new(option_flag, raw_value_for_option(option_flag)) end ! def raw_value_for_option (option_flag) argv.find { |arg| arg =~ /^-#{option_flag}/ } end ! end Refactoring to Small Code 16x9 - April 24, 2014
  115. 115. Enable Labs @mark_menard #smallcode class CommandLineOptions ! attr_accessor :argv attr_reader :options ! def initialize (argv = [], &block) @options = {} @argv = argv instance_eval &block end ! def valid? options.values.all?(&:valid?) end ! def value (option_flag) options[option_flag].value end ! private ! def option (option_flag, option_type = :boolean) options[option_flag] = build_option(option_flag, option_type) end ! def build_option (option_flag, option_type) "#{option_type}_option".camelize.constantize.new(option_flag, raw_value_for_option(option_flag)) end ! def raw_value_for_option (option_flag) argv.find { |arg| arg =~ /^-#{option_flag}/ } end ! end Refactoring to Small Code 16x9 - April 24, 2014
  116. 116. Enable Labs @mark_menard #smallcode class CommandLineOptions ! attr_accessor :argv attr_reader :options ! def initialize (argv = [], &block) @options = {} @argv = argv instance_eval &block end ! def valid? options.values.all?(&:valid?) end ! def value (option_flag) options[option_flag].value end ! private ! def option (option_flag, option_type = :boolean) options[option_flag] = build_option(option_flag, option_type) end ! def build_option (option_flag, option_type) "#{option_type}_option".camelize.constantize.new(option_flag, raw_value_for_option(option_flag)) end ! def raw_value_for_option (option_flag) argv.find { |arg| arg =~ /^-#{option_flag}/ } end ! end Refactoring to Small Code 16x9 - April 24, 2014
  117. 117. Enable Labs @mark_menard #smallcode ! def valid? options.values.all?(&:valid?) end ! def value (option_flag) options[option_flag].value end Refactoring to Small Code 16x9 - April 24, 2014
  118. 118. Enable Labs @mark_menard #smallcode CommandLineOptions builds an option object for each defined option is valid if all options are valid is invalid if any option is invalid option object conventions uses a StringOption for string options uses a BooleanOption for boolean options uses an IntegerOption for integer options Refactoring to Small Code 16x9 - April 24, 2014
  119. 119. Enable Labs @mark_menard #smallcode Then Refactoring to Small Code 16x9 - April 24, 2014
  120. 120. Enable Labs @mark_menard #smallcode some_ruby_program -v -efoo -i100 -afoo,bar,baz Refactoring to Small Code 16x9 - April 24, 2014
  121. 121. Enable Labs @mark_menard #smallcode describe "array options" do it "can return the value as an array" do options = CommandLineOptions.new([ "-afoo,bar,baz" ]) { option :a, :array } expect(options.value(:a)).to eq(["foo", "bar", "baz"]) end end Refactoring to Small Code 16x9 - April 24, 2014
  122. 122. Enable Labs @mark_menard #smallcode describe "array options" do it "can return the value as an array" do options = CommandLineOptions.new([ "-afoo,bar,baz" ]) { option :a, :array } expect(options.value(:a)).to eq(["foo", "bar", "baz"]) end end class ArrayOption < OptionWithContent def value return nil if option_unset? extract_value_from_raw_value.split(",") end end Refactoring to Small Code 16x9 - April 24, 2014
  123. 123. Enable Labs @mark_menard #smallcodeCommandLineOptions boolean options are true if present are false if absent string options must have content is valid when there is content can return the value return nil if not in argv integer options must have content must be an integer can return the value as an integer returns nil if not in argv array options can return the value as an array ! OptionWithContent has a flag is valid when it has no raw value is valid when it has a value can return it's value when present returns nil if the flag has no raw value Refactoring to Small Code 16x9 - April 24, 2014
  124. 124. Enable Labs @mark_menard #smallcode Now We’re Done!! Let them implement their own option classes. It’s easy. Refactoring to Small Code 16x9 - April 24, 2014
  125. 125. Enable Labs @mark_menard #smallcode http://www.enablelabs.com/ info@enablelabs.com 866-895-8189 @mark_menard Mark Menard Enable Labs GitHub: MarkMenard Refactoring to Small Code 16x9 - April 24, 2014
  126. 126. Enable Labs @mark_menard #smallcode Credits • Syntax highlighting: pbpaste | highlight --syntax=rb --style=edit-xcode --out-format=rtf | pbcopy! • Command line option example inspiration Uncle Bob. Refactoring to Small Code 16x9 - April 24, 2014

×