SlideShare a Scribd company logo
1 of 91
Download to read offline
Small Code
Ruby on Ales 2014

Mark Menard
@mark_menard
Enable Labs

Enable Labs

!

@mark_menard
‘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

Enable Labs

@mark_menard
Introduction

Enable Labs

@mark_menard
What do I mean by small?

Enable Labs

@mark_menard
It’s not
about total
line count.
Enable Labs

@mark_menard
It’s not about
method
count.
Enable Labs

@mark_menard
It’s not
about class
count.

Enable Labs

@mark_menard
So, what do I mean by small?
Small methods!

Enable Labs

Small classes

@mark_menard
Why should we strive for small code?
•
•
•
•

We don’t know what the future will bring!
Raise the level of abstraction!
Create composable components!
Prefer delegation over inheritance

Enable Labs

@mark_menard
Why should we strive for small code?
•
•
•
•

We don’t know what the future will bring!
Raise the level of abstraction!
Create composable components!
Prefer delegation over inheritance

Enable Future Change

Enable Labs

@mark_menard
What are the challenges of small code?

• Dependency Management!
• Context Management

Enable Labs

@mark_menard
The goal: Small units of
understandable code that
are amenable to change.

Enable Labs

@mark_menard
Our Primary Tools
•
•
•

Extract Method!
Move Method!
Extract Class

Enable Labs

@mark_menard
Let’s Look at Some Code

Enable Labs

@mark_menard
% some_ruby_program -v -sfoo

Enable Labs

!14

@mark_menard
options = CommandLineOptions.new(ARGV) do
option :v
option :s, :string
end

Enable Labs

!15

@mark_menard
if options.valid?
if options.value(:v)
# Do something
end
!

if (s_option = options.value(:s))
# Do something
end
end

Enable Labs

!16

@mark_menard
if options.valid?
if options.value(:v)
# Do something
end
!

if (s_option = options.value(:s))
# Do something
end
end

Enable Labs

!16

@mark_menard
if options.valid?
if options.value(:v)
# Do something
end
!

if (s_option = options.value(:s))
# Do something
end
end

Enable Labs

!16

@mark_menard
# 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

Enable Labs

!17

@mark_menard
class 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

Enable Labs

!18

@mark_menard
class CommandLineOptions
!

…
def initialize (argv = [], &block)
@options = {}
@argv = argv
instance_eval &block
end
!

…
!

end

Enable Labs

# In some_ruby_program
options = CommandLineOptions.new(ARGV) do
option :v
option :s, :string
end
!19

@mark_menard
class CommandLineOptions
!

…
def option (option_flag, option_type = :boolean)
options[option_flag] = option_type
end
!

…
!

end

Enable Labs

# In some_ruby_program
options = CommandLineOptions.new(ARGV) do
option :v
option :s, :string
end

!20

@mark_menard
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

Enable Labs

!21

@mark_menard
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

Enable Labs

!22

@mark_menard
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

Enable Labs

!23

@mark_menard
Methods

Enable Labs

@mark_menard
“The object programs that live
best and longest are those with
short methods.”!
! ! ! ! ! ! -Refactoring by Fields, Harvey, Fowler, Black

Enable Labs

@mark_menard
The first rule of methods:

Enable Labs

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

Enable Labs

@mark_menard
One level of abstraction per
method.

Enable Labs

@mark_menard
Use Descriptive Names

Enable Labs

@mark_menard
The fewer arguments the better.

Enable Labs

@mark_menard
Separate Queries from Commands

Enable Labs

@mark_menard
Don’t Repeat Yourself

Enable Labs

@mark_menard
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
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

Enable Labs

!33

@mark_menard
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
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

Enable Labs

!33

@mark_menard
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
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

Enable Labs

!33

@mark_menard
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
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

Enable Labs

!33

@mark_menard
Extract Method Refactoring
def print_invoice_for_amount (amount)
print_header
puts "Name: #{@name}"
puts "Amount: #{amount}"
end

Enable Labs

@mark_menard
Extract Method Refactoring
def print_invoice_for_amount (amount)
print_header
puts "Name: #{@name}"
puts "Amount: #{amount}"
end

High level of abstraction

Enable Labs

@mark_menard
Extract Method Refactoring
def print_invoice_for_amount (amount)
print_header
puts "Name: #{@name}"
puts "Amount: #{amount}"
end

Low level of abstraction

Enable Labs

@mark_menard
Extract Method Refactoring
def print_invoice_for_amount (amount)
print_header
puts "Name: #{@name}"
puts "Amount: #{amount}"
end

Move this to here

Enable Labs

def print_invoice_for_amount (amount)
print_header
print_details (amount)
end

!

def print_details (amount)
puts "Name: #{@name}"
puts "Amount: #{amount}"
end

@mark_menard
Extract Method Refactoring
def print_invoice_for_amount (amount)
print_header
puts "Name: #{@name}"
puts "Amount: #{amount}"
end

Move this to here

Enable Labs

Same level of abstraction

def print_invoice_for_amount (amount)
print_header
print_details (amount)
end

!

def print_details (amount)
puts "Name: #{@name}"
puts "Amount: #{amount}"
end

@mark_menard
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

Enable Labs

@mark_menard
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 && 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

Enable Labs

!35

@mark_menard
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 && 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

Enable Labs

!35

@mark_menard
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 && 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

Enable Labs

!35

@mark_menard
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 && 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

end

Enable Labs

!36

@mark_menard
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

Enable Labs

!37

@mark_menard
# 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

Enable Labs

!38

@mark_menard
class 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

Enable Labs

!39

@mark_menard
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	
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	

Enable Labs

!40

@mark_menard
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

Enable Labs

!41

@mark_menard
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

Enable Labs

!41

@mark_menard
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

Enable Labs

!41

@mark_menard
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

Enable Labs

!42

@mark_menard
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

Enable Labs

!42

@mark_menard
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

Enable Labs

!42

@mark_menard
class 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

Enable Labs

!43

@mark_menard
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 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

Enable Labs

!44

@mark_menard
How do we write small classes?
•
•
•
•
•
•
•

Write small methods!
Talk to the class!
Find a good name!
Isolate Responsibilities!
Find cohesive sets of variables/properties!
Extract Class!
Move method

Enable Labs

@mark_menard
What are the characteristics of a
well designed small class?

•
•
•
•
•

Single responsibility!
Cohesive properties!
Small public interface (preferably a handful of
methods at the most)!
Implements a single Use Case if possible!
Primary logic is expressed in a composed method!

Enable Labs

@mark_menard
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

Enable Labs

!47

@mark_menard
class 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

Enable Labs

!48

@mark_menard
Dependencies

Enable Labs

@mark_menard
How do we deal with
Dependencies?
• Dependency Inversion!
• Depend on Stable Abstractions

Enable Labs

@mark_menard
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

Enable Labs

!51

@mark_menard
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

Enable Labs

!51

@mark_menard
class Option

!
!

!

def initialize (flag, raw_value)
@flag = flag
@raw_value = raw_value
end

!

class StringOption < Option

!

!

attr_reader :flag, :raw_value

end

!

class IntegerOption < 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

Enable Labs

!

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

!

class BooleanOption < Option

!
!

def valid?
true
end

def value
!!raw_value
end
end

!52

@mark_menard
!

!

class StringOption < Option

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
return nil unless raw_value
raw_value[2..-1]
end
end

!
class
!

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

Enable Labs

def valid?
return true unless raw_value
raw_value && raw_value.length > 2
end

!

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

!53

@mark_menard
class StringOption < Option

!
class CommandLineOptions
!
…
!
def valid?
options.all?(&:valid?)
end
!
def value (option_flag)
options[option_flag].value
end
!
end

Enable Labs

!

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

!53

@mark_menard
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	

Enable Labs

!54

@mark_menard
How do we isolate abstractions?
Separate the “what”
from the “how”.

Enable Labs

@mark_menard
!

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 valid?
options.values.all?(&:valid?)
end

Enable Labs

!56

@mark_menard
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

def value (option_flag)
options[option_flag].value
end

Enable Labs

!57

@mark_menard
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

Enable Labs

!58

@mark_menard
!

def valid?
options.values.all?(&:valid?)
end
!

def value (option_flag)
options[option_flag].value
end

Enable Labs

!59

@mark_menard
CommandLineOptions	
builds an option object for each defined option (PENDING: Not yet implemented)	
is valid if all options are valid (PENDING: Not yet implemented)	
is invalid if any option is invalid (PENDING: Not yet implemented)	
option object conventions	
uses a StringOption for string options (PENDING: Not yet implemented)	
uses a BooleanOption for boolean options (PENDING: Not yet implemented)	
uses an IntegerOption for integer options (PENDING: Not yet implemented)

Enable Labs

!60

@mark_menard
describe CommandLineOptions do
it "builds an option object for each defined option" do
options = CommandLineOptions.new([ "-v" ]) { option :v }
expect(options.options.values.size).to eq(1)
end

!

!

!
!

!

!

it "is valid if all options are valid" do
options = CommandLineOptions.new([ "-sfoo" ]) { option :s, :string }
expect(options.valid?).to be_true
end
it "is invalid if any option is invalid" do
options = CommandLineOptions.new([ "-s" ]) { option :s, :string }
expect(options.valid?).to be_false
end
describe "option object conventions" do
it "uses a StringOption for string options" do
options = CommandLineOptions.new([ "-s" ]) { option :s, :string }
expect(options.options[:s].class).to eq(StringOption)
end
it "uses a BooleanOption for boolean options" do
options = CommandLineOptions.new([ "-s" ]) { option :v }
expect(options.options[:v].class).to eq(BooleanOption)
end

it "uses an IntegerOption for integer options" do
options = CommandLineOptions.new([ "-s" ]) { option :i, :integer }
expect(options.options[:i].class).to eq(IntegerOption)
end
end
end

Enable Labs

!61

@mark_menard
describe CommandLineOptions do
it "builds an option object for each defined option" do
options = CommandLineOptions.new([ "-v" ]) { option :v }
expect(options.options.values.size).to eq(1)
end

!

!

!
!

!

!

it "is valid if all options are valid" do
options = CommandLineOptions.new([ "-sfoo" ]) { option :s, :string }
expect(options.valid?).to be_true
end
it "is invalid if any option is invalid" do
options = CommandLineOptions.new([ "-s" ]) { option :s, :string }
expect(options.valid?).to be_false
end
describe "option object conventions" do
it "uses a StringOption for string options" do
options = CommandLineOptions.new([ "-s" ]) { option :s, :string }
expect(options.options[:s].class).to eq(StringOption)
end
it "uses a BooleanOption for boolean options" do
options = CommandLineOptions.new([ "-s" ]) { option :v }
expect(options.options[:v].class).to eq(BooleanOption)
end

it "uses an IntegerOption for integer options" do
options = CommandLineOptions.new([ "-s" ]) { option :i, :integer }
expect(options.options[:i].class).to eq(IntegerOption)
end
end
end

Enable Labs

!61

@mark_menard
describe CommandLineOptions do
it "builds an option object for each defined option" do
options = CommandLineOptions.new([ "-v" ]) { option :v }
expect(options.options.values.size).to eq(1)
end

!

!

!
!

!

!

it "is valid if all options are valid" do
options = CommandLineOptions.new([ "-sfoo" ]) { option :s, :string }
expect(options.valid?).to be_true
end
it "is invalid if any option is invalid" do
options = CommandLineOptions.new([ "-s" ]) { option :s, :string }
expect(options.valid?).to be_false
end
describe "option object conventions" do
it "uses a StringOption for string options" do
options = CommandLineOptions.new([ "-s" ]) { option :s, :string }
expect(options.options[:s].class).to eq(StringOption)
end
it "uses a BooleanOption for boolean options" do
options = CommandLineOptions.new([ "-s" ]) { option :v }
expect(options.options[:v].class).to eq(BooleanOption)
end

it "uses an IntegerOption for integer options" do
options = CommandLineOptions.new([ "-s" ]) { option :i, :integer }
expect(options.options[:i].class).to eq(IntegerOption)
end
end
end

Enable Labs

!61

@mark_menard
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	

Enable Labs

!62

@mark_menard
Then

Enable Labs

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

Enable Labs

!64

@mark_menard
describe "array options" do
it "can return the value as an array" do
expect(CommandLineOptions.new([ "-afoo,bar,baz" ]) { option :a, :array }.value(:a)).to eq(["foo", "bar", "baz"])
end
end

Enable Labs

!65

@mark_menard
describe "array options" do
it "can return the value as an array" do
expect(CommandLineOptions.new([ "-afoo,bar,baz" ]) { option :a, :array }.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

Enable Labs

!65

@mark_menard
CommandLineOptions	
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	

Enable Labs

!66

@mark_menard
Now We’re Done!!
Let them implement their own
option classes. It’s easy.

Enable Labs

@mark_menard
Credits
•

Syntax highlighting: pbpaste | highlight --syntax=rb --style=edit-xcode -out-format=rtf | pbcopy!

•

Command line option example inspiration Uncle Bob.

Enable Labs

@mark_menard
Enable Labs

Start Today
@mark_menard

http://www.enablelabs.com/
info@enablelabs.com
866-895-8189
Enable Labs

@mark_menard

More Related Content

What's hot

IOS Swift language 2nd tutorial
IOS Swift language 2nd tutorialIOS Swift language 2nd tutorial
IOS Swift language 2nd tutorialHassan A-j
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDDKonstantin Kudryashov
 
More Little Wonders of C#/.NET
More Little Wonders of C#/.NETMore Little Wonders of C#/.NET
More Little Wonders of C#/.NETBlackRabbitCoder
 
Workin ontherailsroad
Workin ontherailsroadWorkin ontherailsroad
Workin ontherailsroadJim Jones
 
WorkinOnTheRailsRoad
WorkinOnTheRailsRoadWorkinOnTheRailsRoad
WorkinOnTheRailsRoadwebuploader
 
Functional Programming - Worth the Effort
Functional Programming - Worth the EffortFunctional Programming - Worth the Effort
Functional Programming - Worth the EffortBoldRadius Solutions
 
Boogie 2011 Hi-Lite
Boogie 2011 Hi-LiteBoogie 2011 Hi-Lite
Boogie 2011 Hi-LiteAdaCore
 
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009Yasuko Ohba
 
Puppet future parser
Puppet future parserPuppet future parser
Puppet future parserMartin Alfke
 
The Little Wonders of C# 6
The Little Wonders of C# 6The Little Wonders of C# 6
The Little Wonders of C# 6BlackRabbitCoder
 
Introduction to Python decorators
Introduction to Python decoratorsIntroduction to Python decorators
Introduction to Python decoratorsrikbyte
 
NLP using JavaScript Natural Library
NLP using JavaScript Natural LibraryNLP using JavaScript Natural Library
NLP using JavaScript Natural LibraryAniruddha Chakrabarti
 
Software development best practices & coding guidelines
Software development best practices & coding guidelinesSoftware development best practices & coding guidelines
Software development best practices & coding guidelinesAnkur Goyal
 

What's hot (17)

Cs30 New
Cs30 NewCs30 New
Cs30 New
 
IOS Swift language 2nd tutorial
IOS Swift language 2nd tutorialIOS Swift language 2nd tutorial
IOS Swift language 2nd tutorial
 
Moving away from legacy code with BDD
Moving away from legacy code with BDDMoving away from legacy code with BDD
Moving away from legacy code with BDD
 
More Little Wonders of C#/.NET
More Little Wonders of C#/.NETMore Little Wonders of C#/.NET
More Little Wonders of C#/.NET
 
Workin ontherailsroad
Workin ontherailsroadWorkin ontherailsroad
Workin ontherailsroad
 
WorkinOnTheRailsRoad
WorkinOnTheRailsRoadWorkinOnTheRailsRoad
WorkinOnTheRailsRoad
 
Of Lambdas and LINQ
Of Lambdas and LINQOf Lambdas and LINQ
Of Lambdas and LINQ
 
Functional Programming - Worth the Effort
Functional Programming - Worth the EffortFunctional Programming - Worth the Effort
Functional Programming - Worth the Effort
 
Boogie 2011 Hi-Lite
Boogie 2011 Hi-LiteBoogie 2011 Hi-Lite
Boogie 2011 Hi-Lite
 
Refactoring
RefactoringRefactoring
Refactoring
 
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
Pragmatic Patterns of Ruby on Rails - Ruby Kaigi2009
 
Puppet future parser
Puppet future parserPuppet future parser
Puppet future parser
 
The Little Wonders of C# 6
The Little Wonders of C# 6The Little Wonders of C# 6
The Little Wonders of C# 6
 
Introduction to Python decorators
Introduction to Python decoratorsIntroduction to Python decorators
Introduction to Python decorators
 
NLP using JavaScript Natural Library
NLP using JavaScript Natural LibraryNLP using JavaScript Natural Library
NLP using JavaScript Natural Library
 
Software development best practices & coding guidelines
Software development best practices & coding guidelinesSoftware development best practices & coding guidelines
Software development best practices & coding guidelines
 
Refactoring
RefactoringRefactoring
Refactoring
 

Similar to Small Code - Ruby on Ales 2014

The Java Script Programming Language
The  Java Script  Programming  LanguageThe  Java Script  Programming  Language
The Java Script Programming Languagezone
 
The JavaScript Programming Language
The JavaScript Programming LanguageThe JavaScript Programming Language
The JavaScript Programming LanguageRaghavan Mohan
 
Les origines de Javascript
Les origines de JavascriptLes origines de Javascript
Les origines de JavascriptBernard Loire
 
Javascript by Yahoo
Javascript by YahooJavascript by Yahoo
Javascript by Yahoobirbal
 
Ruby introduction part1
Ruby introduction part1Ruby introduction part1
Ruby introduction part1Brady Cheng
 
Scala Language Intro - Inspired by the Love Game
Scala Language Intro - Inspired by the Love GameScala Language Intro - Inspired by the Love Game
Scala Language Intro - Inspired by the Love GameAntony Stubbs
 
Paulo Freire Pedagpogia 1
Paulo Freire Pedagpogia 1Paulo Freire Pedagpogia 1
Paulo Freire Pedagpogia 1Alejandra Perez
 
Washington Practitioners Significant Changes To Rpc 1.5
Washington Practitioners Significant Changes To Rpc 1.5Washington Practitioners Significant Changes To Rpc 1.5
Washington Practitioners Significant Changes To Rpc 1.5Oregon Law Practice Management
 
Jerry Shea Resume And Addendum 5 2 09
Jerry  Shea Resume And Addendum 5 2 09Jerry  Shea Resume And Addendum 5 2 09
Jerry Shea Resume And Addendum 5 2 09gshea11
 
Majlis Persaraan Pn.Hjh.Normah bersama guru-guru Sesi Petang
Majlis Persaraan Pn.Hjh.Normah bersama guru-guru Sesi PetangMajlis Persaraan Pn.Hjh.Normah bersama guru-guru Sesi Petang
Majlis Persaraan Pn.Hjh.Normah bersama guru-guru Sesi PetangImsamad
 
Agapornis Mansos - www.criadourosudica.blogspot.com
Agapornis Mansos - www.criadourosudica.blogspot.comAgapornis Mansos - www.criadourosudica.blogspot.com
Agapornis Mansos - www.criadourosudica.blogspot.comAntonio Silva
 
Perl Tidy Perl Critic
Perl Tidy Perl CriticPerl Tidy Perl Critic
Perl Tidy Perl Criticolegmmiller
 

Similar to Small Code - Ruby on Ales 2014 (20)

The Java Script Programming Language
The  Java Script  Programming  LanguageThe  Java Script  Programming  Language
The Java Script Programming Language
 
The JavaScript Programming Language
The JavaScript Programming LanguageThe JavaScript Programming Language
The JavaScript Programming Language
 
Les origines de Javascript
Les origines de JavascriptLes origines de Javascript
Les origines de Javascript
 
Javascript by Yahoo
Javascript by YahooJavascript by Yahoo
Javascript by Yahoo
 
Javascript
JavascriptJavascript
Javascript
 
Javascript
JavascriptJavascript
Javascript
 
Ruby introduction part1
Ruby introduction part1Ruby introduction part1
Ruby introduction part1
 
Dsl
DslDsl
Dsl
 
Scala Language Intro - Inspired by the Love Game
Scala Language Intro - Inspired by the Love GameScala Language Intro - Inspired by the Love Game
Scala Language Intro - Inspired by the Love Game
 
Intro to ruby
Intro to rubyIntro to ruby
Intro to ruby
 
MMBJ Shanzhai Culture
MMBJ Shanzhai CultureMMBJ Shanzhai Culture
MMBJ Shanzhai Culture
 
LoteríA Correcta
LoteríA CorrectaLoteríA Correcta
LoteríA Correcta
 
Paulo Freire Pedagpogia 1
Paulo Freire Pedagpogia 1Paulo Freire Pedagpogia 1
Paulo Freire Pedagpogia 1
 
Washington Practitioners Significant Changes To Rpc 1.5
Washington Practitioners Significant Changes To Rpc 1.5Washington Practitioners Significant Changes To Rpc 1.5
Washington Practitioners Significant Changes To Rpc 1.5
 
Jerry Shea Resume And Addendum 5 2 09
Jerry  Shea Resume And Addendum 5 2 09Jerry  Shea Resume And Addendum 5 2 09
Jerry Shea Resume And Addendum 5 2 09
 
Majlis Persaraan Pn.Hjh.Normah bersama guru-guru Sesi Petang
Majlis Persaraan Pn.Hjh.Normah bersama guru-guru Sesi PetangMajlis Persaraan Pn.Hjh.Normah bersama guru-guru Sesi Petang
Majlis Persaraan Pn.Hjh.Normah bersama guru-guru Sesi Petang
 
Agapornis Mansos - www.criadourosudica.blogspot.com
Agapornis Mansos - www.criadourosudica.blogspot.comAgapornis Mansos - www.criadourosudica.blogspot.com
Agapornis Mansos - www.criadourosudica.blogspot.com
 
Perl Tidy Perl Critic
Perl Tidy Perl CriticPerl Tidy Perl Critic
Perl Tidy Perl Critic
 
Perl Introduction
Perl IntroductionPerl Introduction
Perl Introduction
 
Perl Presentation
Perl PresentationPerl Presentation
Perl Presentation
 

More from Mark Menard

A Tour of Wyriki
A Tour of WyrikiA Tour of Wyriki
A Tour of WyrikiMark Menard
 
JRuby 6 Years in Production
JRuby 6 Years in ProductionJRuby 6 Years in Production
JRuby 6 Years in ProductionMark Menard
 
Conference of Grand Masters Tech Talk 2013
Conference of Grand Masters Tech Talk 2013Conference of Grand Masters Tech Talk 2013
Conference of Grand Masters Tech Talk 2013Mark Menard
 
Startup Lessons Learned
Startup Lessons LearnedStartup Lessons Learned
Startup Lessons LearnedMark Menard
 
Mobile Platforms and App Development
Mobile Platforms and App DevelopmentMobile Platforms and App Development
Mobile Platforms and App DevelopmentMark Menard
 
Ruby on Rails Training - Module 2
Ruby on Rails Training - Module 2Ruby on Rails Training - Module 2
Ruby on Rails Training - Module 2Mark Menard
 
Ruby on Rails Training - Module 1
Ruby on Rails Training - Module 1Ruby on Rails Training - Module 1
Ruby on Rails Training - Module 1Mark Menard
 
Introduction to Ruby
Introduction to RubyIntroduction to Ruby
Introduction to RubyMark Menard
 
Intro to Rails ActiveRecord
Intro to Rails ActiveRecordIntro to Rails ActiveRecord
Intro to Rails ActiveRecordMark Menard
 
Behavior Driven Development with Rails
Behavior Driven Development with RailsBehavior Driven Development with Rails
Behavior Driven Development with RailsMark Menard
 
Intro to Ruby on Rails
Intro to Ruby on RailsIntro to Ruby on Rails
Intro to Ruby on RailsMark Menard
 
JRuby in a Java World
JRuby in a Java WorldJRuby in a Java World
JRuby in a Java WorldMark Menard
 

More from Mark Menard (12)

A Tour of Wyriki
A Tour of WyrikiA Tour of Wyriki
A Tour of Wyriki
 
JRuby 6 Years in Production
JRuby 6 Years in ProductionJRuby 6 Years in Production
JRuby 6 Years in Production
 
Conference of Grand Masters Tech Talk 2013
Conference of Grand Masters Tech Talk 2013Conference of Grand Masters Tech Talk 2013
Conference of Grand Masters Tech Talk 2013
 
Startup Lessons Learned
Startup Lessons LearnedStartup Lessons Learned
Startup Lessons Learned
 
Mobile Platforms and App Development
Mobile Platforms and App DevelopmentMobile Platforms and App Development
Mobile Platforms and App Development
 
Ruby on Rails Training - Module 2
Ruby on Rails Training - Module 2Ruby on Rails Training - Module 2
Ruby on Rails Training - Module 2
 
Ruby on Rails Training - Module 1
Ruby on Rails Training - Module 1Ruby on Rails Training - Module 1
Ruby on Rails Training - Module 1
 
Introduction to Ruby
Introduction to RubyIntroduction to Ruby
Introduction to Ruby
 
Intro to Rails ActiveRecord
Intro to Rails ActiveRecordIntro to Rails ActiveRecord
Intro to Rails ActiveRecord
 
Behavior Driven Development with Rails
Behavior Driven Development with RailsBehavior Driven Development with Rails
Behavior Driven Development with Rails
 
Intro to Ruby on Rails
Intro to Ruby on RailsIntro to Ruby on Rails
Intro to Ruby on Rails
 
JRuby in a Java World
JRuby in a Java WorldJRuby in a Java World
JRuby in a Java World
 

Recently uploaded

How to release an Open Source Dataweave Library
How to release an Open Source Dataweave LibraryHow to release an Open Source Dataweave Library
How to release an Open Source Dataweave Libraryshyamraj55
 
UiPath Studio Web workshop Series - Day 3
UiPath Studio Web workshop Series - Day 3UiPath Studio Web workshop Series - Day 3
UiPath Studio Web workshop Series - Day 3DianaGray10
 
Explore the UiPath Community and ways you can benefit on your journey to auto...
Explore the UiPath Community and ways you can benefit on your journey to auto...Explore the UiPath Community and ways you can benefit on your journey to auto...
Explore the UiPath Community and ways you can benefit on your journey to auto...DianaGray10
 
TrustArc Webinar - How to Live in a Post Third-Party Cookie World
TrustArc Webinar - How to Live in a Post Third-Party Cookie WorldTrustArc Webinar - How to Live in a Post Third-Party Cookie World
TrustArc Webinar - How to Live in a Post Third-Party Cookie WorldTrustArc
 
Scenario Library et REX Discover industry- and role- based scenarios
Scenario Library et REX Discover industry- and role- based scenariosScenario Library et REX Discover industry- and role- based scenarios
Scenario Library et REX Discover industry- and role- based scenariosErol GIRAUDY
 
Extra-120324-Visite-Entreprise-icare.pdf
Extra-120324-Visite-Entreprise-icare.pdfExtra-120324-Visite-Entreprise-icare.pdf
Extra-120324-Visite-Entreprise-icare.pdfInfopole1
 
AI Workshops at Computers In Libraries 2024
AI Workshops at Computers In Libraries 2024AI Workshops at Computers In Libraries 2024
AI Workshops at Computers In Libraries 2024Brian Pichman
 
Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024
Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024
Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024Alkin Tezuysal
 
The Zero-ETL Approach: Enhancing Data Agility and Insight
The Zero-ETL Approach: Enhancing Data Agility and InsightThe Zero-ETL Approach: Enhancing Data Agility and Insight
The Zero-ETL Approach: Enhancing Data Agility and InsightSafe Software
 
Webinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - Tech
Webinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - TechWebinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - Tech
Webinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - TechProduct School
 
Novo Nordisk's journey in developing an open-source application on Neo4j
Novo Nordisk's journey in developing an open-source application on Neo4jNovo Nordisk's journey in developing an open-source application on Neo4j
Novo Nordisk's journey in developing an open-source application on Neo4jNeo4j
 
SIM INFORMATION SYSTEM: REVOLUTIONIZING DATA MANAGEMENT
SIM INFORMATION SYSTEM: REVOLUTIONIZING DATA MANAGEMENTSIM INFORMATION SYSTEM: REVOLUTIONIZING DATA MANAGEMENT
SIM INFORMATION SYSTEM: REVOLUTIONIZING DATA MANAGEMENTxtailishbaloch
 
Automation Ops Series: Session 2 - Governance for UiPath projects
Automation Ops Series: Session 2 - Governance for UiPath projectsAutomation Ops Series: Session 2 - Governance for UiPath projects
Automation Ops Series: Session 2 - Governance for UiPath projectsDianaGray10
 
Trailblazer Community - Flows Workshop (Session 2)
Trailblazer Community - Flows Workshop (Session 2)Trailblazer Community - Flows Workshop (Session 2)
Trailblazer Community - Flows Workshop (Session 2)Muhammad Tiham Siddiqui
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfCheryl Hung
 
UiPath Studio Web workshop series - Day 1
UiPath Studio Web workshop series  - Day 1UiPath Studio Web workshop series  - Day 1
UiPath Studio Web workshop series - Day 1DianaGray10
 
Introduction to RAG (Retrieval Augmented Generation) and its application
Introduction to RAG (Retrieval Augmented Generation) and its applicationIntroduction to RAG (Retrieval Augmented Generation) and its application
Introduction to RAG (Retrieval Augmented Generation) and its applicationKnoldus Inc.
 
UiPath Studio Web workshop series - Day 4
UiPath Studio Web workshop series - Day 4UiPath Studio Web workshop series - Day 4
UiPath Studio Web workshop series - Day 4DianaGray10
 
Where developers are challenged, what developers want and where DevEx is going
Where developers are challenged, what developers want and where DevEx is goingWhere developers are challenged, what developers want and where DevEx is going
Where developers are challenged, what developers want and where DevEx is goingFrancesco Corti
 
Stobox 4: Revolutionizing Investment in Real-World Assets Through Tokenization
Stobox 4: Revolutionizing Investment in Real-World Assets Through TokenizationStobox 4: Revolutionizing Investment in Real-World Assets Through Tokenization
Stobox 4: Revolutionizing Investment in Real-World Assets Through TokenizationStobox
 

Recently uploaded (20)

How to release an Open Source Dataweave Library
How to release an Open Source Dataweave LibraryHow to release an Open Source Dataweave Library
How to release an Open Source Dataweave Library
 
UiPath Studio Web workshop Series - Day 3
UiPath Studio Web workshop Series - Day 3UiPath Studio Web workshop Series - Day 3
UiPath Studio Web workshop Series - Day 3
 
Explore the UiPath Community and ways you can benefit on your journey to auto...
Explore the UiPath Community and ways you can benefit on your journey to auto...Explore the UiPath Community and ways you can benefit on your journey to auto...
Explore the UiPath Community and ways you can benefit on your journey to auto...
 
TrustArc Webinar - How to Live in a Post Third-Party Cookie World
TrustArc Webinar - How to Live in a Post Third-Party Cookie WorldTrustArc Webinar - How to Live in a Post Third-Party Cookie World
TrustArc Webinar - How to Live in a Post Third-Party Cookie World
 
Scenario Library et REX Discover industry- and role- based scenarios
Scenario Library et REX Discover industry- and role- based scenariosScenario Library et REX Discover industry- and role- based scenarios
Scenario Library et REX Discover industry- and role- based scenarios
 
Extra-120324-Visite-Entreprise-icare.pdf
Extra-120324-Visite-Entreprise-icare.pdfExtra-120324-Visite-Entreprise-icare.pdf
Extra-120324-Visite-Entreprise-icare.pdf
 
AI Workshops at Computers In Libraries 2024
AI Workshops at Computers In Libraries 2024AI Workshops at Computers In Libraries 2024
AI Workshops at Computers In Libraries 2024
 
Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024
Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024
Design and Modeling for MySQL SCALE 21X Pasadena, CA Mar 2024
 
The Zero-ETL Approach: Enhancing Data Agility and Insight
The Zero-ETL Approach: Enhancing Data Agility and InsightThe Zero-ETL Approach: Enhancing Data Agility and Insight
The Zero-ETL Approach: Enhancing Data Agility and Insight
 
Webinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - Tech
Webinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - TechWebinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - Tech
Webinar: The Art of Prioritizing Your Product Roadmap by AWS Sr PM - Tech
 
Novo Nordisk's journey in developing an open-source application on Neo4j
Novo Nordisk's journey in developing an open-source application on Neo4jNovo Nordisk's journey in developing an open-source application on Neo4j
Novo Nordisk's journey in developing an open-source application on Neo4j
 
SIM INFORMATION SYSTEM: REVOLUTIONIZING DATA MANAGEMENT
SIM INFORMATION SYSTEM: REVOLUTIONIZING DATA MANAGEMENTSIM INFORMATION SYSTEM: REVOLUTIONIZING DATA MANAGEMENT
SIM INFORMATION SYSTEM: REVOLUTIONIZING DATA MANAGEMENT
 
Automation Ops Series: Session 2 - Governance for UiPath projects
Automation Ops Series: Session 2 - Governance for UiPath projectsAutomation Ops Series: Session 2 - Governance for UiPath projects
Automation Ops Series: Session 2 - Governance for UiPath projects
 
Trailblazer Community - Flows Workshop (Session 2)
Trailblazer Community - Flows Workshop (Session 2)Trailblazer Community - Flows Workshop (Session 2)
Trailblazer Community - Flows Workshop (Session 2)
 
Key Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdfKey Trends Shaping the Future of Infrastructure.pdf
Key Trends Shaping the Future of Infrastructure.pdf
 
UiPath Studio Web workshop series - Day 1
UiPath Studio Web workshop series  - Day 1UiPath Studio Web workshop series  - Day 1
UiPath Studio Web workshop series - Day 1
 
Introduction to RAG (Retrieval Augmented Generation) and its application
Introduction to RAG (Retrieval Augmented Generation) and its applicationIntroduction to RAG (Retrieval Augmented Generation) and its application
Introduction to RAG (Retrieval Augmented Generation) and its application
 
UiPath Studio Web workshop series - Day 4
UiPath Studio Web workshop series - Day 4UiPath Studio Web workshop series - Day 4
UiPath Studio Web workshop series - Day 4
 
Where developers are challenged, what developers want and where DevEx is going
Where developers are challenged, what developers want and where DevEx is goingWhere developers are challenged, what developers want and where DevEx is going
Where developers are challenged, what developers want and where DevEx is going
 
Stobox 4: Revolutionizing Investment in Real-World Assets Through Tokenization
Stobox 4: Revolutionizing Investment in Real-World Assets Through TokenizationStobox 4: Revolutionizing Investment in Real-World Assets Through Tokenization
Stobox 4: Revolutionizing Investment in Real-World Assets Through Tokenization
 

Small Code - Ruby on Ales 2014

  • 1. Small Code Ruby on Ales 2014 Mark Menard @mark_menard Enable Labs Enable Labs ! @mark_menard
  • 2. ‘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 Enable Labs @mark_menard
  • 4. What do I mean by small? Enable Labs @mark_menard
  • 5. It’s not about total line count. Enable Labs @mark_menard
  • 8. So, what do I mean by small? Small methods! Enable Labs Small classes @mark_menard
  • 9. Why should we strive for small code? • • • • We don’t know what the future will bring! Raise the level of abstraction! Create composable components! Prefer delegation over inheritance Enable Labs @mark_menard
  • 10. Why should we strive for small code? • • • • We don’t know what the future will bring! Raise the level of abstraction! Create composable components! Prefer delegation over inheritance Enable Future Change Enable Labs @mark_menard
  • 11. What are the challenges of small code? • Dependency Management! • Context Management Enable Labs @mark_menard
  • 12. The goal: Small units of understandable code that are amenable to change. Enable Labs @mark_menard
  • 13. Our Primary Tools • • • Extract Method! Move Method! Extract Class Enable Labs @mark_menard
  • 14. Let’s Look at Some Code Enable Labs @mark_menard
  • 15. % some_ruby_program -v -sfoo Enable Labs !14 @mark_menard
  • 16. options = CommandLineOptions.new(ARGV) do option :v option :s, :string end Enable Labs !15 @mark_menard
  • 17. if options.valid? if options.value(:v) # Do something end ! if (s_option = options.value(:s)) # Do something end end Enable Labs !16 @mark_menard
  • 18. if options.valid? if options.value(:v) # Do something end ! if (s_option = options.value(:s)) # Do something end end Enable Labs !16 @mark_menard
  • 19. if options.valid? if options.value(:v) # Do something end ! if (s_option = options.value(:s)) # Do something end end Enable Labs !16 @mark_menard
  • 20. # 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 Enable Labs !17 @mark_menard
  • 21. class 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 Enable Labs !18 @mark_menard
  • 22. class CommandLineOptions ! … def initialize (argv = [], &block) @options = {} @argv = argv instance_eval &block end ! … ! end Enable Labs # In some_ruby_program options = CommandLineOptions.new(ARGV) do option :v option :s, :string end !19 @mark_menard
  • 23. class CommandLineOptions ! … def option (option_flag, option_type = :boolean) options[option_flag] = option_type end ! … ! end Enable Labs # In some_ruby_program options = CommandLineOptions.new(ARGV) do option :v option :s, :string end !20 @mark_menard
  • 24. 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 Enable Labs !21 @mark_menard
  • 25. 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 Enable Labs !22 @mark_menard
  • 26. 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 Enable Labs !23 @mark_menard
  • 28. “The object programs that live best and longest are those with short methods.”! ! ! ! ! ! ! -Refactoring by Fields, Harvey, Fowler, Black Enable Labs @mark_menard
  • 29. The first rule of methods: Enable Labs @mark_menard
  • 30. Do one thing. Do it well. Do only that thing. Enable Labs @mark_menard
  • 31. One level of abstraction per method. Enable Labs @mark_menard
  • 32. Use Descriptive Names Enable Labs @mark_menard
  • 33. The fewer arguments the better. Enable Labs @mark_menard
  • 34. Separate Queries from Commands Enable Labs @mark_menard
  • 35. Don’t Repeat Yourself Enable Labs @mark_menard
  • 36. 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 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 Enable Labs !33 @mark_menard
  • 37. 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 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 Enable Labs !33 @mark_menard
  • 38. 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 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 Enable Labs !33 @mark_menard
  • 39. 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 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 Enable Labs !33 @mark_menard
  • 40. Extract Method Refactoring def print_invoice_for_amount (amount) print_header puts "Name: #{@name}" puts "Amount: #{amount}" end Enable Labs @mark_menard
  • 41. Extract Method Refactoring def print_invoice_for_amount (amount) print_header puts "Name: #{@name}" puts "Amount: #{amount}" end High level of abstraction Enable Labs @mark_menard
  • 42. Extract Method Refactoring def print_invoice_for_amount (amount) print_header puts "Name: #{@name}" puts "Amount: #{amount}" end Low level of abstraction Enable Labs @mark_menard
  • 43. Extract Method Refactoring def print_invoice_for_amount (amount) print_header puts "Name: #{@name}" puts "Amount: #{amount}" end Move this to here Enable Labs def print_invoice_for_amount (amount) print_header print_details (amount) end ! def print_details (amount) puts "Name: #{@name}" puts "Amount: #{amount}" end @mark_menard
  • 44. Extract Method Refactoring def print_invoice_for_amount (amount) print_header puts "Name: #{@name}" puts "Amount: #{amount}" end Move this to here Enable Labs Same level of abstraction def print_invoice_for_amount (amount) print_header print_details (amount) end ! def print_details (amount) puts "Name: #{@name}" puts "Amount: #{amount}" end @mark_menard
  • 45. 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 Enable Labs @mark_menard
  • 46. 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 && 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 Enable Labs !35 @mark_menard
  • 47. 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 && 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 Enable Labs !35 @mark_menard
  • 48. 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 && 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 Enable Labs !35 @mark_menard
  • 49. 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 && 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 end Enable Labs !36 @mark_menard
  • 50. 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 Enable Labs !37 @mark_menard
  • 51. # 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 Enable Labs !38 @mark_menard
  • 52. class 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 Enable Labs !39 @mark_menard
  • 53. 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 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 Enable Labs !40 @mark_menard
  • 54. 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 Enable Labs !41 @mark_menard
  • 55. 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 Enable Labs !41 @mark_menard
  • 56. 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 Enable Labs !41 @mark_menard
  • 57. 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 Enable Labs !42 @mark_menard
  • 58. 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 Enable Labs !42 @mark_menard
  • 59. 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 Enable Labs !42 @mark_menard
  • 60. class 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 Enable Labs !43 @mark_menard
  • 61. 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 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 Enable Labs !44 @mark_menard
  • 62. How do we write small classes? • • • • • • • Write small methods! Talk to the class! Find a good name! Isolate Responsibilities! Find cohesive sets of variables/properties! Extract Class! Move method Enable Labs @mark_menard
  • 63. What are the characteristics of a well designed small class? • • • • • Single responsibility! Cohesive properties! Small public interface (preferably a handful of methods at the most)! Implements a single Use Case if possible! Primary logic is expressed in a composed method! Enable Labs @mark_menard
  • 64. 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 Enable Labs !47 @mark_menard
  • 65. class 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 Enable Labs !48 @mark_menard
  • 67. How do we deal with Dependencies? • Dependency Inversion! • Depend on Stable Abstractions Enable Labs @mark_menard
  • 68. 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 Enable Labs !51 @mark_menard
  • 69. 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 Enable Labs !51 @mark_menard
  • 70. class Option ! ! ! def initialize (flag, raw_value) @flag = flag @raw_value = raw_value end ! class StringOption < Option ! ! attr_reader :flag, :raw_value end ! class IntegerOption < 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 Enable Labs ! 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 ! class BooleanOption < Option ! ! def valid? true end def value !!raw_value end end !52 @mark_menard
  • 71. ! ! class StringOption < Option 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 return nil unless raw_value raw_value[2..-1] end end ! class ! 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 Enable Labs def valid? return true unless raw_value raw_value && raw_value.length > 2 end ! 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 !53 @mark_menard
  • 72. class StringOption < Option ! class CommandLineOptions ! … ! def valid? options.all?(&:valid?) end ! def value (option_flag) options[option_flag].value end ! end Enable Labs ! 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 !53 @mark_menard
  • 73. 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 Enable Labs !54 @mark_menard
  • 74. How do we isolate abstractions? Separate the “what” from the “how”. Enable Labs @mark_menard
  • 75. ! 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 valid? options.values.all?(&:valid?) end Enable Labs !56 @mark_menard
  • 76. 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 def value (option_flag) options[option_flag].value end Enable Labs !57 @mark_menard
  • 77. 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 Enable Labs !58 @mark_menard
  • 78. ! def valid? options.values.all?(&:valid?) end ! def value (option_flag) options[option_flag].value end Enable Labs !59 @mark_menard
  • 79. CommandLineOptions builds an option object for each defined option (PENDING: Not yet implemented) is valid if all options are valid (PENDING: Not yet implemented) is invalid if any option is invalid (PENDING: Not yet implemented) option object conventions uses a StringOption for string options (PENDING: Not yet implemented) uses a BooleanOption for boolean options (PENDING: Not yet implemented) uses an IntegerOption for integer options (PENDING: Not yet implemented) Enable Labs !60 @mark_menard
  • 80. describe CommandLineOptions do it "builds an option object for each defined option" do options = CommandLineOptions.new([ "-v" ]) { option :v } expect(options.options.values.size).to eq(1) end ! ! ! ! ! ! it "is valid if all options are valid" do options = CommandLineOptions.new([ "-sfoo" ]) { option :s, :string } expect(options.valid?).to be_true end it "is invalid if any option is invalid" do options = CommandLineOptions.new([ "-s" ]) { option :s, :string } expect(options.valid?).to be_false end describe "option object conventions" do it "uses a StringOption for string options" do options = CommandLineOptions.new([ "-s" ]) { option :s, :string } expect(options.options[:s].class).to eq(StringOption) end it "uses a BooleanOption for boolean options" do options = CommandLineOptions.new([ "-s" ]) { option :v } expect(options.options[:v].class).to eq(BooleanOption) end it "uses an IntegerOption for integer options" do options = CommandLineOptions.new([ "-s" ]) { option :i, :integer } expect(options.options[:i].class).to eq(IntegerOption) end end end Enable Labs !61 @mark_menard
  • 81. describe CommandLineOptions do it "builds an option object for each defined option" do options = CommandLineOptions.new([ "-v" ]) { option :v } expect(options.options.values.size).to eq(1) end ! ! ! ! ! ! it "is valid if all options are valid" do options = CommandLineOptions.new([ "-sfoo" ]) { option :s, :string } expect(options.valid?).to be_true end it "is invalid if any option is invalid" do options = CommandLineOptions.new([ "-s" ]) { option :s, :string } expect(options.valid?).to be_false end describe "option object conventions" do it "uses a StringOption for string options" do options = CommandLineOptions.new([ "-s" ]) { option :s, :string } expect(options.options[:s].class).to eq(StringOption) end it "uses a BooleanOption for boolean options" do options = CommandLineOptions.new([ "-s" ]) { option :v } expect(options.options[:v].class).to eq(BooleanOption) end it "uses an IntegerOption for integer options" do options = CommandLineOptions.new([ "-s" ]) { option :i, :integer } expect(options.options[:i].class).to eq(IntegerOption) end end end Enable Labs !61 @mark_menard
  • 82. describe CommandLineOptions do it "builds an option object for each defined option" do options = CommandLineOptions.new([ "-v" ]) { option :v } expect(options.options.values.size).to eq(1) end ! ! ! ! ! ! it "is valid if all options are valid" do options = CommandLineOptions.new([ "-sfoo" ]) { option :s, :string } expect(options.valid?).to be_true end it "is invalid if any option is invalid" do options = CommandLineOptions.new([ "-s" ]) { option :s, :string } expect(options.valid?).to be_false end describe "option object conventions" do it "uses a StringOption for string options" do options = CommandLineOptions.new([ "-s" ]) { option :s, :string } expect(options.options[:s].class).to eq(StringOption) end it "uses a BooleanOption for boolean options" do options = CommandLineOptions.new([ "-s" ]) { option :v } expect(options.options[:v].class).to eq(BooleanOption) end it "uses an IntegerOption for integer options" do options = CommandLineOptions.new([ "-s" ]) { option :i, :integer } expect(options.options[:i].class).to eq(IntegerOption) end end end Enable Labs !61 @mark_menard
  • 83. 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 Enable Labs !62 @mark_menard
  • 85. some_ruby_program -v -efoo -i100 -afoo,bar,baz Enable Labs !64 @mark_menard
  • 86. describe "array options" do it "can return the value as an array" do expect(CommandLineOptions.new([ "-afoo,bar,baz" ]) { option :a, :array }.value(:a)).to eq(["foo", "bar", "baz"]) end end Enable Labs !65 @mark_menard
  • 87. describe "array options" do it "can return the value as an array" do expect(CommandLineOptions.new([ "-afoo,bar,baz" ]) { option :a, :array }.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 Enable Labs !65 @mark_menard
  • 88. CommandLineOptions 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 Enable Labs !66 @mark_menard
  • 89. Now We’re Done!! Let them implement their own option classes. It’s easy. Enable Labs @mark_menard
  • 90. Credits • Syntax highlighting: pbpaste | highlight --syntax=rb --style=edit-xcode -out-format=rtf | pbcopy! • Command line option example inspiration Uncle Bob. Enable Labs @mark_menard