126
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

Small Code - RailsConf 2014

Embed Size (px)

DESCRIPTION

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

Citation preview

Page 1: Small Code - RailsConf 2014

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

Page 2: Small Code - RailsConf 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

Page 3: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

What do I mean by small?

Refactoring to Small Code 16x9 - April 24, 2014

Page 4: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

It’s not about total line count.

Refactoring to Small Code 16x9 - April 24, 2014

Page 5: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

It’s not about method count.

Refactoring to Small Code 16x9 - April 24, 2014

Page 6: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

It’s not about class

count.

Refactoring to Small Code 16x9 - April 24, 2014

Page 7: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

It’s About

Small methods! Small classes

Refactoring to Small Code 16x9 - April 24, 2014

Page 8: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Why should we strive for small code?

Refactoring to Small Code 16x9 - April 24, 2014

Page 9: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

We don’t know what the future will bring.

Refactoring to Small Code 16x9 - April 24, 2014

Page 10: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Raise the level of abstraction.

Refactoring to Small Code 16x9 - April 24, 2014

Page 11: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Create composable objects.

Refactoring to Small Code 16x9 - April 24, 2014

Page 12: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Enable Future Change

Refactoring to Small Code 16x9 - April 24, 2014

Page 13: Small Code - RailsConf 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

Page 14: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Our Primary Tools

• Extract Method!

• Move Method!

• Extract Class

Refactoring to Small Code 16x9 - April 24, 2014

Page 15: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Let’s Look at Some Code

Refactoring to Small Code 16x9 - April 24, 2014

Page 16: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

# some_ruby_program -v

Refactoring to Small Code 16x9 - April 24, 2014

Page 17: Small Code - RailsConf 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

Page 18: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcodeif 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

Page 19: Small Code - RailsConf 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

Page 20: Small Code - RailsConf 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

Page 21: Small Code - RailsConf 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

Page 22: Small Code - RailsConf 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

Page 23: Small Code - RailsConf 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

Page 24: Small Code - RailsConf 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

Page 25: Small Code - RailsConf 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

Page 26: Small Code - RailsConf 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

Page 27: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Done!

Refactoring to Small Code 16x9 - April 24, 2014

Page 28: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

% some_ruby_program -v -sfoo

Refactoring to Small Code 16x9 - April 24, 2014

Page 29: Small Code - RailsConf 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

Page 30: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcodeif 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

Page 31: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcodeif 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

Page 32: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcodeif 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

Page 33: Small Code - RailsConf 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

Page 34: Small Code - RailsConf 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

Page 35: Small Code - RailsConf 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

Page 36: Small Code - RailsConf 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 ! … !end

Refactoring to Small Code 16x9 - April 24, 2014

Page 37: Small Code - RailsConf 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

Page 38: Small Code - RailsConf 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 seconds7 examples, 0 failures

Refactoring to Small Code 16x9 - April 24, 2014

Page 39: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Methods

Refactoring to Small Code 16x9 - April 24, 2014

Page 40: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

The first rule of methods:

Refactoring to Small Code 16x9 - April 24, 2014

Page 41: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Do one thing. Do it well. Do only that thing.

Refactoring to Small Code 16x9 - April 24, 2014

Page 42: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

One level of abstraction per method.

Refactoring to Small Code 16x9 - April 24, 2014

Page 43: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Use Descriptive Names

Refactoring to Small Code 16x9 - April 24, 2014

Page 44: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

The fewer arguments the better.

Refactoring to Small Code 16x9 - April 24, 2014

Page 45: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Separate Queries from Commands

Refactoring to Small Code 16x9 - April 24, 2014

Page 46: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Don’t Repeat Yourself

Refactoring to Small Code 16x9 - April 24, 2014

Page 47: Small Code - RailsConf 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

Page 48: Small Code - RailsConf 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

Page 49: Small Code - RailsConf 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

Page 50: Small Code - RailsConf 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

Page 51: Small Code - RailsConf 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

Page 52: Small Code - RailsConf 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

Page 53: Small Code - RailsConf 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

Page 54: Small Code - RailsConf 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

Page 55: Small Code - RailsConf 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

Page 56: Small Code - RailsConf 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

Page 57: Small Code - RailsConf 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

Page 58: Small Code - RailsConf 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

Page 59: Small Code - RailsConf 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

Page 60: Small Code - RailsConf 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

Page 61: Small Code - RailsConf 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 && !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

Page 62: Small Code - RailsConf 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 && !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

Page 63: Small Code - RailsConf 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 && !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

Page 64: Small Code - RailsConf 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

Page 65: Small Code - RailsConf 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

Page 66: Small Code - RailsConf 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

Page 67: Small Code - RailsConf 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

Page 68: Small Code - RailsConf 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 seconds13 examples, 0 failures

Refactoring to Small Code 16x9 - April 24, 2014

Page 69: Small Code - RailsConf 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

Page 70: Small Code - RailsConf 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

Page 71: Small Code - RailsConf 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

Page 72: Small Code - RailsConf 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

Page 73: Small Code - RailsConf 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

Page 74: Small Code - RailsConf 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

Page 75: Small Code - RailsConf 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

Page 76: Small Code - RailsConf 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

Page 77: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

How do we write small classes?

Refactoring to Small Code 16x9 - April 24, 2014

Page 78: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

What are the characteristics of a small class?

Refactoring to Small Code 16x9 - April 24, 2014

Page 79: Small Code - RailsConf 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 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

Page 80: Small Code - RailsConf 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 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

Page 81: Small Code - RailsConf 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

Page 82: Small Code - RailsConf 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

Page 83: Small Code - RailsConf 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

Page 84: Small Code - RailsConf 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

Page 85: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Dependencies

Refactoring to Small Code 16x9 - April 24, 2014

Page 86: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Dependencies

Command Line Options ?*

Refactoring to Small Code 16x9 - April 24, 2014

Page 87: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Depend on Abstractions

Refactoring to Small Code 16x9 - April 24, 2014

Page 88: Small Code - RailsConf 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

Page 89: Small Code - RailsConf 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

Page 90: Small Code - RailsConf 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

Page 91: Small Code - RailsConf 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

Page 92: Small Code - RailsConf 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

Page 93: Small Code - RailsConf 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

Page 94: Small Code - RailsConf 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

Page 95: Small Code - RailsConf 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

Page 96: Small Code - RailsConf 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

Page 97: Small Code - RailsConf 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

Page 98: Small Code - RailsConf 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 !! 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

Page 99: Small Code - RailsConf 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 !! 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

Page 100: Small Code - RailsConf 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 !! 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

Page 101: Small Code - RailsConf 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 !! 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

Page 102: Small Code - RailsConf 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 !! 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

Page 103: Small Code - RailsConf 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

Page 104: Small Code - RailsConf 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

Page 105: Small Code - RailsConf 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

Page 106: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcodeOption 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 seconds22 examples, 0 failures, 6 pending

Refactoring to Small Code 16x9 - April 24, 2014

Page 107: Small Code - RailsConf 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

Page 108: Small Code - RailsConf 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

Page 109: Small Code - RailsConf 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

Page 110: Small Code - RailsConf 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

Page 111: Small Code - RailsConf 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

Page 112: Small Code - RailsConf 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

Page 113: Small Code - RailsConf 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

Page 114: Small Code - RailsConf 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 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

Page 115: Small Code - RailsConf 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 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

Page 116: Small Code - RailsConf 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 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

Page 117: Small Code - RailsConf 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

Page 118: Small Code - RailsConf 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

Page 119: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

Then

Refactoring to Small Code 16x9 - April 24, 2014

Page 120: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

some_ruby_program -v -efoo -i100 -afoo,bar,baz

Refactoring to Small Code 16x9 - April 24, 2014

Page 121: Small Code - RailsConf 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

Page 122: Small Code - RailsConf 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

Page 123: Small Code - RailsConf 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

Page 124: Small Code - RailsConf 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

Page 125: Small Code - RailsConf 2014

Enable Labs @mark_menard

#smallcode

http://www.enablelabs.com/

[email protected]

866-895-8189

@mark_menard

Mark Menard Enable Labs

GitHub: MarkMenard

Refactoring to Small Code 16x9 - April 24, 2014

Page 126: Small Code - RailsConf 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