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.
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
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
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. 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. 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
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. 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. 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. 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. 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. 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. 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. 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. 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
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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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
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. 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. 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. 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. 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. 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
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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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
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. 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. 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. 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. 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. 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. 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. 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. 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. 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. 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
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. 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. 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. 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