SlideShare a Scribd company logo
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
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
Enable Labs @mark_menard
#smallcode
What do I mean by small?
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
It’s not
about total
line count.
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
It’s not about
method
count.
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
It’s not
about class
count.
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
It’s About
Small methods! Small classes
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
Why should we strive for small code?
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
We don’t know what the future will bring.
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
Raise the level of abstraction.
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
Create composable objects.
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
Enable Future Change
Refactoring to Small Code 16x9 - April 24, 2014
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
Enable Labs @mark_menard
#smallcode
Our Primary Tools
• Extract Method!
• Move Method!
• Extract Class
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
Let’s Look at Some Code
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
# some_ruby_program -v
Refactoring to Small Code 16x9 - April 24, 2014
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
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
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
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
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
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
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
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
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
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
Enable Labs @mark_menard
#smallcode
Done!
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
% some_ruby_program -v -sfoo
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
options = CommandLineOptions.new(ARGV) do
option :v
option :s, :string
end
Refactoring to Small Code 16x9 - April 24, 2014
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
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
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
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
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
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
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
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
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
Enable Labs @mark_menard
#smallcode
Methods
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
The first rule of methods:
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
Do one thing. Do it well. Do only that thing.
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
One level of abstraction per method.
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
Use Descriptive Names
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
The fewer arguments the better.
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
Separate Queries from Commands
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
Don’t RepeatYourself
Refactoring to Small Code 16x9 - April 24, 2014
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
Enable Labs @mark_menard
#smallcode
How do we write small classes?
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
What are the characteristics of a small class?
Refactoring to Small Code 16x9 - April 24, 2014
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
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
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
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
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
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
Enable Labs @mark_menard
#smallcode
Dependencies
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
Dependencies
Command
Line Options ?*
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
Depend on Abstractions
Refactoring to Small Code 16x9 - April 24, 2014
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
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
Enable Labs @mark_menard
#smallcode
How do we isolate abstractions?
Separate the “what”
from the “how”.
Refactoring to Small Code 16x9 - April 24, 2014
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
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
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
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
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
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
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
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
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
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
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
Enable Labs @mark_menard
#smallcode
Then
Refactoring to Small Code 16x9 - April 24, 2014
Enable Labs @mark_menard
#smallcode
some_ruby_program -v -efoo -i100 -afoo,bar,baz
Refactoring to Small Code 16x9 - April 24, 2014
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
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
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
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
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
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

More Related Content

What's hot (13)

PPTX
IOS Swift language 2nd tutorial
Hassan A-j
 
PPT
Cs30 New
DSK Chakravarthy
 
PPT
name name2 n2
callroom
 
PPT
ppt9
callroom
 
PPT
name name2 n
callroom
 
PPT
ppt18
callroom
 
PPT
name name2 n2.ppt
callroom
 
PPT
Ruby for Perl Programmers
amiable_indian
 
PPSX
Esoft Metro Campus - Programming with C++
Rasan Samarasinghe
 
PPT
Ruby For Java Programmers
Mike Bowler
 
PDF
Ruby for Java Developers
Robert Reiz
 
PDF
Moving away from legacy code with BDD
Konstantin Kudryashov
 
IOS Swift language 2nd tutorial
Hassan A-j
 
name name2 n2
callroom
 
ppt9
callroom
 
name name2 n
callroom
 
ppt18
callroom
 
name name2 n2.ppt
callroom
 
Ruby for Perl Programmers
amiable_indian
 
Esoft Metro Campus - Programming with C++
Rasan Samarasinghe
 
Ruby For Java Programmers
Mike Bowler
 
Ruby for Java Developers
Robert Reiz
 
Moving away from legacy code with BDD
Konstantin Kudryashov
 

Similar to Small Code - RailsConf 2014 (20)

PDF
Story for a Ruby on Rails Single Engineer
TylerJohnson988371
 
PDF
Refactoring Workshop (Rails Pacific 2014)
Bruce Li
 
KEY
Ruby
Kerry Buckley
 
PDF
Classboxes, nested methods, and real private methods
Shugo Maeda
 
PDF
Ruby_Coding_Convention
Jesse Cai
 
KEY
Refactor like a boss
gsterndale
 
PDF
Ruby and Rails by Example (GeekCamp edition)
bryanbibat
 
PDF
Ruby Intro {spection}
Christian KAKESA
 
PPTX
Ruby Style Guide
Bion Johnson
 
PDF
Refactoring
Herez Moise Kattan
 
PDF
Write your Ruby in Style
Bhavin Javia
 
PDF
Designing Ruby APIs
Wen-Tien Chang
 
PDF
Refactoring Long Methods
Marc Rendl Ignacio
 
KEY
Intro to Ruby - Twin Cities Code Camp 7
Brian Hogan
 
KEY
A tour on ruby and friends
旻琦 潘
 
PDF
Ruby and Rails by example
bryanbibat
 
KEY
Intro to Ruby
Brian Hogan
 
PPT
Refactoring Tips by Martin Fowler
Igor Crvenov
 
PDF
Ruby 程式語言入門導覽
Wen-Tien Chang
 
PDF
Ruby Metaprogramming
Nando Vieira
 
Story for a Ruby on Rails Single Engineer
TylerJohnson988371
 
Refactoring Workshop (Rails Pacific 2014)
Bruce Li
 
Classboxes, nested methods, and real private methods
Shugo Maeda
 
Ruby_Coding_Convention
Jesse Cai
 
Refactor like a boss
gsterndale
 
Ruby and Rails by Example (GeekCamp edition)
bryanbibat
 
Ruby Intro {spection}
Christian KAKESA
 
Ruby Style Guide
Bion Johnson
 
Refactoring
Herez Moise Kattan
 
Write your Ruby in Style
Bhavin Javia
 
Designing Ruby APIs
Wen-Tien Chang
 
Refactoring Long Methods
Marc Rendl Ignacio
 
Intro to Ruby - Twin Cities Code Camp 7
Brian Hogan
 
A tour on ruby and friends
旻琦 潘
 
Ruby and Rails by example
bryanbibat
 
Intro to Ruby
Brian Hogan
 
Refactoring Tips by Martin Fowler
Igor Crvenov
 
Ruby 程式語言入門導覽
Wen-Tien Chang
 
Ruby Metaprogramming
Nando Vieira
 
Ad

More from Mark Menard (12)

PDF
A Tour of Wyriki
Mark Menard
 
PDF
JRuby 6 Years in Production
Mark Menard
 
PDF
Conference of Grand Masters Tech Talk 2013
Mark Menard
 
KEY
Startup Lessons Learned
Mark Menard
 
PDF
Mobile Platforms and App Development
Mark Menard
 
KEY
Ruby on Rails Training - Module 2
Mark Menard
 
KEY
Ruby on Rails Training - Module 1
Mark Menard
 
KEY
Introduction to Ruby
Mark Menard
 
PPT
Intro to Rails ActiveRecord
Mark Menard
 
PPT
Behavior Driven Development with Rails
Mark Menard
 
PPT
Intro to Ruby on Rails
Mark Menard
 
PPT
JRuby in a Java World
Mark Menard
 
A Tour of Wyriki
Mark Menard
 
JRuby 6 Years in Production
Mark Menard
 
Conference of Grand Masters Tech Talk 2013
Mark Menard
 
Startup Lessons Learned
Mark Menard
 
Mobile Platforms and App Development
Mark Menard
 
Ruby on Rails Training - Module 2
Mark Menard
 
Ruby on Rails Training - Module 1
Mark Menard
 
Introduction to Ruby
Mark Menard
 
Intro to Rails ActiveRecord
Mark Menard
 
Behavior Driven Development with Rails
Mark Menard
 
Intro to Ruby on Rails
Mark Menard
 
JRuby in a Java World
Mark Menard
 
Ad

Recently uploaded (20)

PDF
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
DOCX
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
PDF
Biography of Daniel Podor.pdf
Daniel Podor
 
PDF
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
PDF
Staying Human in a Machine- Accelerated World
Catalin Jora
 
PDF
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
PDF
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
PPTX
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
PDF
Reverse Engineering of Security Products: Developing an Advanced Microsoft De...
nwbxhhcyjv
 
PDF
Advancing WebDriver BiDi support in WebKit
Igalia
 
PPTX
OpenID AuthZEN - Analyst Briefing July 2025
David Brossard
 
PDF
How Startups Are Growing Faster with App Developers in Australia.pdf
India App Developer
 
PDF
July Patch Tuesday
Ivanti
 
PDF
"AI Transformation: Directions and Challenges", Pavlo Shaternik
Fwdays
 
PDF
"Beyond English: Navigating the Challenges of Building a Ukrainian-language R...
Fwdays
 
PDF
Achieving Consistent and Reliable AI Code Generation - Medusa AI
medusaaico
 
PDF
DevBcn - Building 10x Organizations Using Modern Productivity Metrics
Justin Reock
 
DOCX
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
PPTX
COMPARISON OF RASTER ANALYSIS TOOLS OF QGIS AND ARCGIS
Sharanya Sarkar
 
PDF
LOOPS in C Programming Language - Technology
RishabhDwivedi43
 
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
Python coding for beginners !! Start now!#
Rajni Bhardwaj Grover
 
Biography of Daniel Podor.pdf
Daniel Podor
 
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
Staying Human in a Machine- Accelerated World
Catalin Jora
 
Mastering Financial Management in Direct Selling
Epixel MLM Software
 
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
Reverse Engineering of Security Products: Developing an Advanced Microsoft De...
nwbxhhcyjv
 
Advancing WebDriver BiDi support in WebKit
Igalia
 
OpenID AuthZEN - Analyst Briefing July 2025
David Brossard
 
How Startups Are Growing Faster with App Developers in Australia.pdf
India App Developer
 
July Patch Tuesday
Ivanti
 
"AI Transformation: Directions and Challenges", Pavlo Shaternik
Fwdays
 
"Beyond English: Navigating the Challenges of Building a Ukrainian-language R...
Fwdays
 
Achieving Consistent and Reliable AI Code Generation - Medusa AI
medusaaico
 
DevBcn - Building 10x Organizations Using Modern Productivity Metrics
Justin Reock
 
Cryptography Quiz: test your knowledge of this important security concept.
Rajni Bhardwaj Grover
 
COMPARISON OF RASTER ANALYSIS TOOLS OF QGIS AND ARCGIS
Sharanya Sarkar
 
LOOPS in C Programming Language - Technology
RishabhDwivedi43
 

Small Code - RailsConf 2014

  • 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. 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. Enable Labs @mark_menard #smallcode What do I mean by small? Refactoring to Small Code 16x9 - April 24, 2014
  • 4. Enable Labs @mark_menard #smallcode It’s not about total line count. Refactoring to Small Code 16x9 - April 24, 2014
  • 5. Enable Labs @mark_menard #smallcode It’s not about method count. Refactoring to Small Code 16x9 - April 24, 2014
  • 6. Enable Labs @mark_menard #smallcode It’s not about class count. Refactoring to Small Code 16x9 - April 24, 2014
  • 7. Enable Labs @mark_menard #smallcode It’s About Small methods! Small classes Refactoring to Small Code 16x9 - April 24, 2014
  • 8. Enable Labs @mark_menard #smallcode Why should we strive for small code? Refactoring to Small Code 16x9 - April 24, 2014
  • 9. Enable Labs @mark_menard #smallcode We don’t know what the future will bring. Refactoring to Small Code 16x9 - April 24, 2014
  • 10. Enable Labs @mark_menard #smallcode Raise the level of abstraction. Refactoring to Small Code 16x9 - April 24, 2014
  • 11. Enable Labs @mark_menard #smallcode Create composable objects. Refactoring to Small Code 16x9 - April 24, 2014
  • 12. Enable Labs @mark_menard #smallcode Enable Future Change 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
  • 14. Enable Labs @mark_menard #smallcode Our Primary Tools • Extract Method! • Move Method! • Extract Class Refactoring to Small Code 16x9 - April 24, 2014
  • 15. Enable Labs @mark_menard #smallcode Let’s Look at Some Code Refactoring to Small Code 16x9 - April 24, 2014
  • 16. Enable Labs @mark_menard #smallcode # some_ruby_program -v Refactoring to Small Code 16x9 - April 24, 2014
  • 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. 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
  • 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. 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. 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. 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. 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. 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. Enable Labs @mark_menard #smallcode Done! Refactoring to Small Code 16x9 - April 24, 2014
  • 28. Enable Labs @mark_menard #smallcode % some_ruby_program -v -sfoo Refactoring to Small Code 16x9 - April 24, 2014
  • 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. 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
  • 39. Enable Labs @mark_menard #smallcode Methods Refactoring to Small Code 16x9 - April 24, 2014
  • 40. Enable Labs @mark_menard #smallcode The first rule of methods: Refactoring to Small Code 16x9 - April 24, 2014
  • 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. Enable Labs @mark_menard #smallcode One level of abstraction per method. Refactoring to Small Code 16x9 - April 24, 2014
  • 43. Enable Labs @mark_menard #smallcode Use Descriptive Names Refactoring to Small Code 16x9 - April 24, 2014
  • 44. Enable Labs @mark_menard #smallcode The fewer arguments the better. Refactoring to Small Code 16x9 - April 24, 2014
  • 45. Enable Labs @mark_menard #smallcode Separate Queries from Commands Refactoring to Small Code 16x9 - April 24, 2014
  • 46. Enable Labs @mark_menard #smallcode Don’t RepeatYourself 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
  • 77. Enable Labs @mark_menard #smallcode How do we write small classes? Refactoring to Small Code 16x9 - April 24, 2014
  • 78. Enable Labs @mark_menard #smallcode What are the characteristics of a small class? 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
  • 86. Enable Labs @mark_menard #smallcode Dependencies Command Line Options ?* Refactoring to Small Code 16x9 - April 24, 2014
  • 87. Enable Labs @mark_menard #smallcode Depend on Abstractions 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
  • 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. 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
  • 119. Enable Labs @mark_menard #smallcode Then Refactoring to Small Code 16x9 - April 24, 2014
  • 120. Enable Labs @mark_menard #smallcode some_ruby_program -v -efoo -i100 -afoo,bar,baz 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
  • 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