SlideShare una empresa de Scribd logo
1 de 100
Descargar para leer sin conexión
Write Small Things
Mark Menard
@mark_menard

Enable Labs

Los Angeles Ruby Conf 2014
@mark_menard

Enable Labs

1

WiteSmallThings - February 8, 2014
Who is Mark Menard
Husband of Sylva!
Father of Ezra and Avi!
From and Resides inTroy, NY!
Owner of Enable Labs a boutique consultancy doing Web and Mobile
Development!
Has done for about five years!
Doing software development for a long time

@mark_menard

Enable Labs

2

WiteSmallThings - February 8, 2014
‘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

@mark_menard

Enable Labs

3

WiteSmallThings - February 8, 2014
Introduction

@mark_menard

Enable Labs

4

WiteSmallThings - February 8, 2014
What is meant by small?

@mark_menard

Enable Labs

5

WiteSmallThings - February 8, 2014
It’s not about
total line count.

@mark_menard

Enable Labs

6

WiteSmallThings - February 8, 2014
It’s not about
method count.

@mark_menard

Enable Labs

7

WiteSmallThings - February 8, 2014
It’s not about
class count.

@mark_menard

Enable Labs

8

WiteSmallThings - February 8, 2014
So, what do I mean by small things?
Small methods!

Small classes

@mark_menard

Enable Labs

9

WiteSmallThings - February 8, 2014
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
@mark_menard

Enable Labs

10

WiteSmallThings - February 8, 2014
What are the challenges of small
code?
•
•

Dependency Management!
Context Management

@mark_menard

Enable Labs

11

WiteSmallThings - February 8, 2014
The goal: Small units of
understandable code that
are amenable to change.

@mark_menard

Enable Labs

12

WiteSmallThings - February 8, 2014
Our Three Main Tools
•
•
•

Extract Method!
Extract Class!
Composed Method

@mark_menard

Enable Labs

13

WiteSmallThings - February 8, 2014
Methods

@mark_menard

Enable Labs

14

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

Enable Labs

15

WiteSmallThings - February 8, 2014
The first rule of methods:

@mark_menard

Enable Labs

16

WiteSmallThings - February 8, 2014
Do one thing. Do it well. Do only
that thing.

@mark_menard

Enable Labs

17

WiteSmallThings - February 8, 2014
Use Descriptive Names

@mark_menard

Enable Labs

18

WiteSmallThings - February 8, 2014
The fewer arguments the better.

@mark_menard

Enable Labs

19

WiteSmallThings - February 8, 2014
Separate Queries from Commands

@mark_menard

Enable Labs

20

WiteSmallThings - February 8, 2014
Don’t Repeat Yourself

@mark_menard

Enable Labs

21

WiteSmallThings - February 8, 2014
Let’s Build a Command Line
Option Library

@mark_menard

Enable Labs

22

WiteSmallThings - February 8, 2014
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

Enable Labs

!23

23

@mark_menard

WiteSmallThings - February 8, 2014
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

Enable Labs

!24

24

@mark_menard

WiteSmallThings - February 8, 2014
CommandLineOptions
options
are true if present
are false if absent
!

Finished in 0.00206 seconds
2 examples, 2 failures

Enable Labs

!25

25

@mark_menard

WiteSmallThings - February 8, 2014
class 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
!
options = CommandLineOptions.new(ARGV) do
def option (option_flag)
option :c
options << option_flag
option :v
end
option :e
!
end
end

Enable Labs

!26

26

@mark_menard

WiteSmallThings - February 8, 2014
CommandLineOptions
options
are true if present
are false if absent
!

Finished in 0.00206 seconds
2 examples, 0 failures

Enable Labs

!27

27

@mark_menard

WiteSmallThings - February 8, 2014
Done!

@mark_menard

Enable Labs

28

WiteSmallThings - February 8, 2014
# some_ruby_program -v -efoo
!

options = CommandLineOptions.new(ARGV) do
option :v
option :e, :string
end
!

if options.has(:v)
# Do something
end
!

if options.has(:e)
some_value = options.value(:e)
# Do something with the expression
end

Enable Labs

!29

29

@mark_menard

WiteSmallThings - February 8, 2014
describe CommandLineOptions do
!

describe "boolean options" do
let(:options) { CommandLineOptions.new { option :c } }
it "are true if present" do …
it "are false if absent" do …
end
!

describe "string options" do
let(:options) { CommandLineOptions.new { option :e, :string } }
it "must have content" do …
it "can return the value" do …
it "return nil if not in argv" do …
end
end

Enable Labs

!30

30

@mark_menard

WiteSmallThings - February 8, 2014
CommandLineOptions	
boolean options	
are true if present	
are false if absent	
string options	
must have content (FAILED - 1)	
can return the value (FAILED - 2)	
return nil if not in argv (FAILED - 3)	

!

Failures:	

!

!

!

!

1) CommandLineOptions string options must have content	
Failure/Error: expect(options.valid?).to be_false	
NoMethodError:	
undefined method `valid?' for #<CommandLineOptions:0x00000102cc8fa0>	
# ./spec/command_line_options_spec.rb:26:in `block (3 levels) in <top (required)>'	
2) CommandLineOptions string options can return the value	
Failure/Error: expect(options.value(:e)).to eq("foo")	
NoMethodError:	
undefined method `value' for #<CommandLineOptions:0x00000102cca1c0>	
# ./spec/command_line_options_spec.rb:31:in `block (3 levels) in <top (required)>'	
3) CommandLineOptions string options return nil if not in argv	
Failure/Error: expect(options.value(:e)).to be_nil	
NoMethodError:	
undefined method `value' for #<CommandLineOptions:0x00000102cc2a38>	
# ./spec/command_line_options_spec.rb:35:in `block (3 levels) in <top (required)>'	

Finished in 0.00205 seconds	
5 examples, 3 failures

Enable Labs

!31

31

@mark_menard

WiteSmallThings - February 8, 2014
def valid?
options.each do |option_flag, option_type|
raw_option_value = argv.find { |arg| arg =~ /-#{option_flag}/ }
return false if raw_option_value.length < 3 && option_type == :string
end
end

Enable Labs

!32

32

@mark_menard

WiteSmallThings - February 8, 2014
CommandLineOptions	
boolean options	
are true if present	
are false if absent	
string options	
must have content	
can return the value (FAILED - 1)	
return nil if not in argv (FAILED - 2)	

!
Failures:	

!
1) CommandLineOptions string options can return the value	
Failure/Error: expect(options.value(:e)).to eq("foo")	
NoMethodError:	
undefined method `value' for #<CommandLineOptions:0x00000101cbb6f8>	
# ./spec/command_line_options_spec.rb:31:in `block (3 levels) in <top (required)>'	

!
2) CommandLineOptions string options return nil if not in argv	
Failure/Error: expect(options.value(:e)).to be_nil	
NoMethodError:	
undefined method `value' for #<CommandLineOptions:0x00000101cba2a8>	
# ./spec/command_line_options_spec.rb:35:in `block (3 levels) in <top (required)>'	

!
Finished in 0.00206 seconds	
5 examples, 2 failures

Enable Labs

!33

33

@mark_menard

WiteSmallThings - February 8, 2014
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 raw_option_value[2..-1] if option_type == :string
end

Enable Labs

!34

34

@mark_menard

WiteSmallThings - February 8, 2014
CommandLineOptions	
boolean options	
are true if present	
are false if absent	
string options	
must have content	
can return the value	
return nil if not in argv	
!

Finished in 0.00236 seconds	
5 examples, 0 failures	

Enable Labs

!35

35

@mark_menard

WiteSmallThings - February 8, 2014
def valid?
options.each do |option_flag, option_type|
raw_option_value = argv.find { |arg| arg =~ /-#{option_flag}/ }
return false if raw_option_value.length < 3 && option_type == :string
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 raw_option_value[2..-1] if option_type == :string
end

Enable Labs

!36

36

@mark_menard

WiteSmallThings - February 8, 2014
Extract Method Refactoring
def print_invoice_for_amount (amount)
print_header
puts "Name: #{@name}"
puts "Amount: #{amount}"
end

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

Move this to here
High level of abstraction
Low level of abstraction

@mark_menard

Enable Labs

37

WiteSmallThings - February 8, 2014
!

!

def valid?
options.each do |option_flag, option_type|
return false if option_type == :string && raw_value_for_option(option_flag).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 raw_option_value[2..-1] if option_type == :string
end
private def raw_value_for_option (option_flag)
argv.find { |arg| arg =~ /-#{option_flag}/ }
end

Enable Labs

!38

38

@mark_menard

WiteSmallThings - February 8, 2014
CommandLineOptions	
boolean options	
are true if present	
are false if absent	
string options	
must have content	
can return the value	
return nil if not in argv	
!

Finished in 0.00236 seconds	
5 examples, 0 failures	

Enable Labs

!39

39

@mark_menard

WiteSmallThings - February 8, 2014
class 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, option_type = :boolean)
options[option_flag] = option_type
end
def valid?
options.each do |option_flag, option_type|
return false if option_type == :string && raw_value_for_option(option_flag).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 raw_option_value[2..-1] if option_type == :string
end
private def raw_value_for_option (option_flag)
argv.find { |arg| arg =~ /-#{option_flag}/ }
end

end

Enable Labs

!40

40

@mark_menard

WiteSmallThings - February 8, 2014
!

!

def valid?
options.each do |option_flag, option_type|
return false if option_type == :string && raw_value_for_option(option_flag).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 raw_option_value[2..-1] if option_type == :string
end
private def raw_value_for_option (option_flag)
argv.find { |arg| arg =~ /-#{option_flag}/ }
end

Enable Labs

!41

41

@mark_menard

WiteSmallThings - February 8, 2014
Done!

@mark_menard

Enable Labs

42

WiteSmallThings - February 8, 2014
# 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

!43

43

@mark_menard

WiteSmallThings - February 8, 2014
describe "integer options" do
it "must have content"
it "must be an integer"
it "can return the value as an integer"
it "returns nil if not in argv"
end

Enable Labs

!44

44

@mark_menard

WiteSmallThings - February 8, 2014
CommandLineOptions	
boolean options	
are true if present	
are false if absent	
string options	
must have content	
can return the value	
return nil if not in argv	
integer options	
must have content (PENDING: No reason given)	
must be an integer (PENDING: Not yet implemented)	
can return the value as an integer (PENDING: Not yet implemented)	
returns nil if not in argv (PENDING: Not yet implemented)	

Enable Labs

!45

45

@mark_menard

WiteSmallThings - February 8, 2014
let(:options) { CommandLineOptions.new { option :i, :integer } }
!

it "must have content" do
options.argv = [ "-i" ]
expect(options.valid?).to be_false
end

Enable Labs

!46

46

@mark_menard

WiteSmallThings - February 8, 2014
def valid?
options.each do |option_flag, option_type|
return false if option_type == :string &&
raw_value_for_option(option_flag).length < 3
end
end

Enable Labs

!47

47

@mark_menard

WiteSmallThings - February 8, 2014
def valid?
options.each do |option_flag, option_type|
return false if (option_type == :string || option_type == :integer)&&
raw_value_for_option(option_flag).length < 3
end
end

Enable Labs

!48

48

@mark_menard

WiteSmallThings - February 8, 2014
it "must be an integer" do
options.argv = [ "-inot_an_integer" ]
expect(options.valid?).to be_false
end

Enable Labs

!49

49

@mark_menard

WiteSmallThings - February 8, 2014
def valid?
options.each do |option_flag, option_type|
return false if (option_type == :string || option_type == :integer) &&
raw_value_for_option(option_flag).length < 3
return false unless option_type == :integer &&
(Integer(raw_value_for_option(option_flag)[2..-1]) rescue false)
end
end

Enable Labs

!50

50

@mark_menard

WiteSmallThings - February 8, 2014
CommandLineOptions	
boolean options	
are true if present	
are false if absent	
string options	
must have 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 (PENDING: Not yet implemented)	
returns nil if not in argv (PENDING: Not yet implemented)	

Enable Labs

!51

51

@mark_menard

WiteSmallThings - February 8, 2014
class 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, option_type = :boolean)
options[option_flag] = option_type
end
def valid?
options.each do |option_flag, option_type|
return false if (option_type == :string || option_type == :integer) &&
raw_value_for_option(option_flag).length < 3
return false unless option_type == :integer &&
(Integer(raw_value_for_option(option_flag)[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 raw_option_value[2..-1] if option_type == :string
end
private def raw_value_for_option (option_flag)
argv.find { |arg| arg =~ /-#{option_flag}/ }
end

end

Enable Labs

!52

52

@mark_menard

WiteSmallThings - February 8, 2014
def valid?
options.each do |option_flag, option_type|
return false if (option_type == :string || option_type == :integer) &&
raw_value_for_option(option_flag).length < 3
return false unless option_type == :integer &&
(Integer(raw_value_for_option(option_flag)[2..-1]) rescue false)
end
end

Enable Labs

!54

54

@mark_menard

WiteSmallThings - February 8, 2014
Integer("foo") rescue false #=> false
"foo".to_i
#=> 0 !!!!!!!

Enable Labs

!55

55

@mark_menard

WiteSmallThings - February 8, 2014
!

def valid?
options.each do |option_flag, option_type|
case(option_type)
when :string
return false if raw_value_for_option(option_flag).length < 3
when :integer
return false if raw_value_for_option(option_flag).length < 3
return false unless (Integer(raw_value_for_option(option_flag)[2..-1]) rescue false)
end
end
end

Enable Labs

!56

56

@mark_menard

WiteSmallThings - February 8, 2014
def valid?
options.each do |option_flag, option_type|
case(option_type)
when :string
return false if raw_value_for_option(option_flag).length < 3
when :integer
return false if raw_value_for_option(option_flag).length < 3
return false unless (Integer(raw_value_for_option(option_flag)[2..-1]) rescue false)
when :boolean
end
end
end

Enable Labs

!57

57

@mark_menard

WiteSmallThings - February 8, 2014
CommandLineOptions	
boolean options	
are true if present	
are false if absent	
string options	
must have 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 (PENDING: Not yet implemented)	
returns nil if not in argv (PENDING: Not yet implemented)	

Enable Labs

!58

58

@mark_menard

WiteSmallThings - February 8, 2014
def valid?
options.each do |option_flag, option_type|
case(option_type)
when :string
return false unless string_option_valid?(raw_value_for_option(option_flag))
when :integer
return false unless integer_option_valid?(raw_value_for_option(option_flag))
when :boolean
end
end
end

!
private def string_option_valid? (raw_value)
extract_value_from_raw_value(raw_value).length > 0
end

!
private def integer_option_valid? (raw_value)
extract_value_from_raw_value(raw_value).length > 0 &&
(Integer(extract_value_from_raw_value(raw_value)) rescue false)
end

Enable Labs

!59

59

@mark_menard

WiteSmallThings - February 8, 2014
private def boolean_option_valid? (raw_value)
true
end

Enable Labs

!60

60

@mark_menard

WiteSmallThings - February 8, 2014
CommandLineOptions	
boolean options	
are true if present	
are false if absent	
string options	
must have 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 (PENDING: Not yet implemented)	
returns nil if not in argv (PENDING: Not yet implemented)	

Enable Labs

!61

61

@mark_menard

WiteSmallThings - February 8, 2014
def valid?
options.each do |option_flag, option_type|
return false unless send("#{option_type}_option_valid?", raw_value_for_option(option_flag))
end
end

Enable Labs

!62

62

@mark_menard

WiteSmallThings - February 8, 2014
def valid?
options.each do |option_flag, option_type|
return false unless option_valid?(option_type, raw_value_for_option(option_flag))
end
end

!
private def option_valid? (option_type, raw_value)
send("#{option_type}_option_valid?", raw_value)
end

Enable Labs

!63

63

@mark_menard

WiteSmallThings - February 8, 2014
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 (PENDING: Not yet implemented)	

!
Pending:	
CommandLineOptions integer options returns nil if not in argv	
# Not yet implemented	
# ./spec/command_line_options_spec.rb:61	

!
Finished in 0.00291 seconds	
10 examples, 0 failures, 1 pending	

Enable Labs

!64

64

@mark_menard

WiteSmallThings - February 8, 2014
!
!

class 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 valid?
options.each do |option_flag, option_type|
return false unless option_valid?(option_type, raw_value_for_option(option_flag))
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 extract_value_from_raw_value(raw_option_value) if option_type == :string
end
private def option (option_flag, option_type = :boolean)
options[option_flag] = option_type
end
private def option_valid? (option_type, raw_value)
send("#{option_type}_option_valid?", raw_value)
end
private def string_option_valid? (raw_value)
extract_value_from_raw_value(raw_value).length > 0
end
private def integer_option_valid? (raw_value)
extract_value_from_raw_value(raw_value).length > 0 && (Integer(extract_value_from_raw_value(raw_value)) rescue false)
end
private def boolean_option_valid? (raw_value)
true
end
private def extract_value_from_raw_value (raw_value)
raw_value[2..-1]
end
private def raw_value_for_option (option_flag)
argv.find { |arg| arg =~ /-#{option_flag}/ }
end

end

Enable Labs

!65

65

@mark_menard

WiteSmallThings - February 8, 2014
def has (option_flag)
options.include?(option_flag) && argv.include?("-#{option_flag}")
end

!
def valid?
options.each do |option_flag, option_type|
return false unless option_valid?(option_type, raw_value_for_option(option_flag))
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 extract_value_from_raw_value(raw_option_value) if option_type == :string
end

Enable Labs

!66

66

@mark_menard

WiteSmallThings - February 8, 2014
Classes

@mark_menard

Enable Labs

67

WiteSmallThings - February 8, 2014
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

@mark_menard

Enable Labs

68

WiteSmallThings - February 8, 2014
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!
Dependencies are injected

@mark_menard

Enable Labs

69

WiteSmallThings - February 8, 2014
describe StringOption do
let(:string_option) { StringOption.new('s', '-sfoo') }
!

it
it
it
it
end

"has a flag"
"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

!70

70

@mark_menard

WiteSmallThings - February 8, 2014
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 (PENDING: Not yet implemented)	

!
StringOption	
has a flag (PENDING: No reason given)	
is valid when it has a value (PENDING: Not yet implemented)	
can return it's value when present (PENDING: Not yet implemented)	
returns nil if the flag has no raw value (PENDING: Not yet implemented)	

Enable Labs

!71

71

@mark_menard

WiteSmallThings - February 8, 2014
class StringOption
!

attr_reader :flag
!

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

end

Enable Labs

!72

72

@mark_menard

WiteSmallThings - February 8, 2014
it "is valid when it has a value" do
expect(string_option.valid?).to be_true
end

Enable Labs

!73

73

@mark_menard

WiteSmallThings - February 8, 2014
class StringOption

class CommandLineOptions

!
!

!

private def string_option_valid? (raw_value)
extract_value_from_raw_value(raw_value).length > 0
end

!

end

!
!
!

attr_reader :flag, :raw_value
def initialize (flag, raw_value)
@flag = flag
@raw_value = raw_value
end
def valid?
extract_value_from_raw_value.length > 0
end
private def extract_value_from_raw_value
raw_value[2..-1]
end

end

Enable Labs

!74

74

@mark_menard

WiteSmallThings - February 8, 2014
class StringOption
!

attr_reader :flag, :raw_value
!

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

def valid?
extract_value_from_raw_value.length > 0
end
!

private def extract_value_from_raw_value
raw_value[2..-1]
end
!

end

Enable Labs

!75

75

@mark_menard

WiteSmallThings - February 8, 2014
private def string_option_valid? (raw_value)
StringOption.new("", raw_value).valid?
end

Enable Labs

!76

76

@mark_menard

WiteSmallThings - February 8, 2014
class IntegerOption
!
attr_reader :flag, :raw_value
!
def initialize (flag, raw_value)
@flag = flag
@raw_value = raw_value
end
!
def valid?
extract_value_from_raw_value.length > 0 && real_value_is_integer?
end
!
private def extract_value_from_raw_value
raw_value[2..-1]
end
!
private def real_value_is_integer?
(Integer(extract_value_from_raw_value) rescue false)
end
!
end

Enable Labs

!77

77

@mark_menard

WiteSmallThings - February 8, 2014
class StringOption
!
attr_reader :flag, :raw_value
!
def initialize (flag, raw_value)
@flag = flag
@raw_value = raw_value
end
!
def valid?
extract_value_from_raw_value.length > 0
end
!
private def extract_value_from_raw_value
raw_value[2..-1]
end
!
end

Enable Labs

!78

78

@mark_menard

WiteSmallThings - February 8, 2014
class OptionWithContent
!
attr_reader :flag, :raw_value
!
def initialize (flag, raw_value)
@flag = flag
@raw_value = raw_value
end
!
def valid?
extract_value_from_raw_value.length > 0
end
!
private def extract_value_from_raw_value
raw_value[2..-1]
end
!
end

Enable Labs

!79

79

@mark_menard

WiteSmallThings - February 8, 2014
class StringOption < OptionWithContent
end
!

class IntegerOption < OptionWithContent
!

def valid?
super && real_value_is_integer?
end
!

private def real_value_is_integer?
(Integer(extract_value_from_raw_value) rescue false)
end
!

end

Enable Labs

!80

80

@mark_menard

WiteSmallThings - February 8, 2014
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 (PENDING: Not yet implemented)	

!
StringOption	
has a flag	
is valid when it has a value	
can return it's value when present (PENDING: Not yet implemented)	
returns nil if the flag has no raw value (PENDING: Not yet implemented)	

Enable Labs

!81

81

@mark_menard

WiteSmallThings - February 8, 2014
Dependencies

@mark_menard

Enable Labs

83

WiteSmallThings - February 8, 2014
How do we deal with
Dependencies?
•
•

Dependency Injection!
Depend on Abstractions

@mark_menard

Enable Labs

82

WiteSmallThings - February 8, 2014
private def option (option_flag, option_type = :boolean)
options[option_flag] = option_type
end

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

private def option (option_flag, option_type = :boolean)
option_class = "#{option_type}_option".camelize.constantize
options[option_flag] = option_class.new(option_flag, nil)
end

Enable Labs

!84

84

@mark_menard

WiteSmallThings - February 8, 2014
How do we isolate abstractions?
Separate the “what”
from the “how”.

@mark_menard

Enable Labs

85

WiteSmallThings - February 8, 2014
CommandLineOptions	
boolean options	
are true if present	
are false if absent	
string options	
must have content (FAILED - 1)	
is valid when there is content (FAILED - 2)	
can return the value (FAILED - 3)	
return nil if not in argv	
integer options	
must have content (FAILED - 4)	
must be an integer (FAILED - 5)	
can return the value as an integer	
returns nil if not in argv (PENDING: Not yet implemented)	

!
StringOption	
has a flag	
is valid when it has a value	
can return it's value when present (PENDING: Not yet implemented)	
returns nil if the flag has no raw value (PENDING: Not yet implemented)	

Enable Labs

!86

86

@mark_menard

WiteSmallThings - February 8, 2014
def valid?
options.each do |option_flag, option_type|
return false unless option_valid?(option_type, raw_value_for_option(option_flag))
end
end

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

Enable Labs

!87

87

@mark_menard

WiteSmallThings - February 8, 2014
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 (FAILED - 1)	
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 (PENDING: Not yet implemented)	

!
StringOption	
has a flag	
is valid when it has a value	
can return it's value when present (PENDING: Not yet implemented)	
returns nil if the flag has no raw value (PENDING: Not yet implemented)	

Enable Labs

!88

88

@mark_menard

WiteSmallThings - February 8, 2014
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 raw_option_value[2..-1] if option_type == :string
end

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

Enable Labs

!89

89

@mark_menard

WiteSmallThings - February 8, 2014
1) CommandLineOptions string options can return the value	
Failure/Error: expect(options.value(:e)).to eq("foo")	
NoMethodError:	
undefined method `value' for #<StringOption:0x00000101b85b30 @flag=:e, @raw_value="-efoo">	
# ./lib/command_line_options.rb:26:in `value'	
# ./spec/command_line_options_spec.rb:36:in `block (3 levels) in <top (required)>'	

Enable Labs

!90

90

@mark_menard

WiteSmallThings - February 8, 2014
# OptionWithContent
def value
return nil if option_unset?
valid? ? extract_value_from_raw_value : nil
end

# IntegerOption
def value
return if option_unset?
Integer(extract_value_from_raw_value)
end

Enable Labs

!91

91

@mark_menard

WiteSmallThings - February 8, 2014
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	

!
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

!92

92

@mark_menard

WiteSmallThings - February 8, 2014
Done!

@mark_menard

Enable Labs

93

WiteSmallThings - February 8, 2014
class CommandLineOptions

!

!
!
!
!
!

!
!
!
!

def initialize (argv = [], &block)
@options = {}
@argv = argv
instance_eval &block
populate_options_with_raw_values
end
def valid?
options.values.all?(&:valid?)
end
def value (option_flag)
options[option_flag].value
end
private
attr_reader :options, :argv
def populate_options_with_raw_values
options.each do |option_flag, option|
option.raw_value = raw_value_for_option(option_flag)
end
end
def option (option_flag, option_type = :boolean)
options[option_flag] = get_option_class(option_type).new(option_flag, nil)
end
def get_option_class (option_type)
"#{option_type}_option".camelize.constantize
end
def raw_value_for_option (option_flag)
argv.find { |arg| arg =~ /-#{option_flag}/ }
end

end

Enable Labs

!94

94

@mark_menard

WiteSmallThings - February 8, 2014
!

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

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

!95

95

@mark_menard

WiteSmallThings - February 8, 2014
Then

@mark_menard

Enable Labs

96

WiteSmallThings - February 8, 2014
some_ruby_program -v -efoo -i100 -afoo,bar,baz

Enable Labs

!97

97

@mark_menard

WiteSmallThings - February 8, 2014
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

!98

98

@mark_menard

WiteSmallThings - February 8, 2014
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

!99

99

@mark_menard

WiteSmallThings - February 8, 2014
Done!

@mark_menard

Enable Labs

100

WiteSmallThings - February 8, 2014
Enable Labs
Start Today

@mark_menard

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

Enable Labs

101

WiteSmallThings - February 8, 2014

Más contenido relacionado

La actualidad más candente

Object Orientation vs. Functional Programming in Python
Object Orientation vs. Functional Programming in PythonObject Orientation vs. Functional Programming in Python
Object Orientation vs. Functional Programming in PythonPython Ireland
 
MongoDB World 2019: BSON Transpilers: Transpiling from Any Language to Any La...
MongoDB World 2019: BSON Transpilers: Transpiling from Any Language to Any La...MongoDB World 2019: BSON Transpilers: Transpiling from Any Language to Any La...
MongoDB World 2019: BSON Transpilers: Transpiling from Any Language to Any La...MongoDB
 
EmberConf 2021 - Crossfile Codemodding with Joshua Lawrence
EmberConf 2021 - Crossfile Codemodding with Joshua LawrenceEmberConf 2021 - Crossfile Codemodding with Joshua Lawrence
EmberConf 2021 - Crossfile Codemodding with Joshua LawrenceJoshua Lawrence
 
Slicing, Dicing, And Linting OpenAPI
Slicing, Dicing, And Linting OpenAPISlicing, Dicing, And Linting OpenAPI
Slicing, Dicing, And Linting OpenAPIlestrrat
 
Functional Programming - Worth the Effort
Functional Programming - Worth the EffortFunctional Programming - Worth the Effort
Functional Programming - Worth the EffortBoldRadius Solutions
 
Coding Best Practices
Coding Best PracticesCoding Best Practices
Coding Best Practicesmh_azad
 

La actualidad más candente (7)

Object Orientation vs. Functional Programming in Python
Object Orientation vs. Functional Programming in PythonObject Orientation vs. Functional Programming in Python
Object Orientation vs. Functional Programming in Python
 
MongoDB World 2019: BSON Transpilers: Transpiling from Any Language to Any La...
MongoDB World 2019: BSON Transpilers: Transpiling from Any Language to Any La...MongoDB World 2019: BSON Transpilers: Transpiling from Any Language to Any La...
MongoDB World 2019: BSON Transpilers: Transpiling from Any Language to Any La...
 
EmberConf 2021 - Crossfile Codemodding with Joshua Lawrence
EmberConf 2021 - Crossfile Codemodding with Joshua LawrenceEmberConf 2021 - Crossfile Codemodding with Joshua Lawrence
EmberConf 2021 - Crossfile Codemodding with Joshua Lawrence
 
Slicing, Dicing, And Linting OpenAPI
Slicing, Dicing, And Linting OpenAPISlicing, Dicing, And Linting OpenAPI
Slicing, Dicing, And Linting OpenAPI
 
Chtp408
Chtp408Chtp408
Chtp408
 
Functional Programming - Worth the Effort
Functional Programming - Worth the EffortFunctional Programming - Worth the Effort
Functional Programming - Worth the Effort
 
Coding Best Practices
Coding Best PracticesCoding Best Practices
Coding Best Practices
 

Más de 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
 

Más de 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
 

Último

Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Mark Simos
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenHervé Boutemy
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationSafe Software
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubKalema Edgar
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Mattias Andersson
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii SoldatenkoFwdays
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Enterprise Knowledge
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfAlex Barbosa Coqueiro
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsMemoori
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clashcharlottematthew16
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Patryk Bandurski
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024BookNet Canada
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsMiki Katsuragi
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxhariprasad279825
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsMark Billinghurst
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Commit University
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupFlorian Wilhelm
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):comworks
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostZilliz
 

Último (20)

Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
Tampa BSides - Chef's Tour of Microsoft Security Adoption Framework (SAF)
 
DevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache MavenDevoxxFR 2024 Reproducible Builds with Apache Maven
DevoxxFR 2024 Reproducible Builds with Apache Maven
 
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry InnovationBeyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
Beyond Boundaries: Leveraging No-Code Solutions for Industry Innovation
 
Unleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding ClubUnleash Your Potential - Namagunga Girls Coding Club
Unleash Your Potential - Namagunga Girls Coding Club
 
Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?Are Multi-Cloud and Serverless Good or Bad?
Are Multi-Cloud and Serverless Good or Bad?
 
"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko"Debugging python applications inside k8s environment", Andrii Soldatenko
"Debugging python applications inside k8s environment", Andrii Soldatenko
 
Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024Designing IA for AI - Information Architecture Conference 2024
Designing IA for AI - Information Architecture Conference 2024
 
Unraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdfUnraveling Multimodality with Large Language Models.pdf
Unraveling Multimodality with Large Language Models.pdf
 
AI as an Interface for Commercial Buildings
AI as an Interface for Commercial BuildingsAI as an Interface for Commercial Buildings
AI as an Interface for Commercial Buildings
 
Powerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time ClashPowerpoint exploring the locations used in television show Time Clash
Powerpoint exploring the locations used in television show Time Clash
 
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
Integration and Automation in Practice: CI/CD in Mule Integration and Automat...
 
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
Transcript: New from BookNet Canada for 2024: BNC CataList - Tech Forum 2024
 
DMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special EditionDMCC Future of Trade Web3 - Special Edition
DMCC Future of Trade Web3 - Special Edition
 
Vertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering TipsVertex AI Gemini Prompt Engineering Tips
Vertex AI Gemini Prompt Engineering Tips
 
Artificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptxArtificial intelligence in cctv survelliance.pptx
Artificial intelligence in cctv survelliance.pptx
 
Human Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR SystemsHuman Factors of XR: Using Human Factors to Design XR Systems
Human Factors of XR: Using Human Factors to Design XR Systems
 
Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!Nell’iperspazio con Rocket: il Framework Web di Rust!
Nell’iperspazio con Rocket: il Framework Web di Rust!
 
Streamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project SetupStreamlining Python Development: A Guide to a Modern Project Setup
Streamlining Python Development: A Guide to a Modern Project Setup
 
CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):CloudStudio User manual (basic edition):
CloudStudio User manual (basic edition):
 
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage CostLeverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
Leverage Zilliz Serverless - Up to 50X Saving for Your Vector Storage Cost
 

Write Small Things (Code)

  • 1. Write Small Things Mark Menard @mark_menard Enable Labs Los Angeles Ruby Conf 2014 @mark_menard Enable Labs 1 WiteSmallThings - February 8, 2014
  • 2. Who is Mark Menard Husband of Sylva! Father of Ezra and Avi! From and Resides inTroy, NY! Owner of Enable Labs a boutique consultancy doing Web and Mobile Development! Has done for about five years! Doing software development for a long time @mark_menard Enable Labs 2 WiteSmallThings - February 8, 2014
  • 3. ‘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 @mark_menard Enable Labs 3 WiteSmallThings - February 8, 2014
  • 5. What is meant by small? @mark_menard Enable Labs 5 WiteSmallThings - February 8, 2014
  • 6. It’s not about total line count. @mark_menard Enable Labs 6 WiteSmallThings - February 8, 2014
  • 7. It’s not about method count. @mark_menard Enable Labs 7 WiteSmallThings - February 8, 2014
  • 8. It’s not about class count. @mark_menard Enable Labs 8 WiteSmallThings - February 8, 2014
  • 9. So, what do I mean by small things? Small methods! Small classes @mark_menard Enable Labs 9 WiteSmallThings - February 8, 2014
  • 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 @mark_menard Enable Labs 10 WiteSmallThings - February 8, 2014
  • 11. What are the challenges of small code? • • Dependency Management! Context Management @mark_menard Enable Labs 11 WiteSmallThings - February 8, 2014
  • 12. The goal: Small units of understandable code that are amenable to change. @mark_menard Enable Labs 12 WiteSmallThings - February 8, 2014
  • 13. Our Three Main Tools • • • Extract Method! Extract Class! Composed Method @mark_menard Enable Labs 13 WiteSmallThings - February 8, 2014
  • 15. “The object programs that live best and longest are those with short methods.”! ! ! ! ! ! ! -Refactoring by Fields, Harvey, Fowler, Black @mark_menard Enable Labs 15 WiteSmallThings - February 8, 2014
  • 16. The first rule of methods: @mark_menard Enable Labs 16 WiteSmallThings - February 8, 2014
  • 17. Do one thing. Do it well. Do only that thing. @mark_menard Enable Labs 17 WiteSmallThings - February 8, 2014
  • 18. Use Descriptive Names @mark_menard Enable Labs 18 WiteSmallThings - February 8, 2014
  • 19. The fewer arguments the better. @mark_menard Enable Labs 19 WiteSmallThings - February 8, 2014
  • 20. Separate Queries from Commands @mark_menard Enable Labs 20 WiteSmallThings - February 8, 2014
  • 21. Don’t Repeat Yourself @mark_menard Enable Labs 21 WiteSmallThings - February 8, 2014
  • 22. Let’s Build a Command Line Option Library @mark_menard Enable Labs 22 WiteSmallThings - February 8, 2014
  • 23. 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 Enable Labs !23 23 @mark_menard WiteSmallThings - February 8, 2014
  • 24. 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 Enable Labs !24 24 @mark_menard WiteSmallThings - February 8, 2014
  • 25. CommandLineOptions options are true if present are false if absent ! Finished in 0.00206 seconds 2 examples, 2 failures Enable Labs !25 25 @mark_menard WiteSmallThings - February 8, 2014
  • 26. class 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 ! options = CommandLineOptions.new(ARGV) do def option (option_flag) option :c options << option_flag option :v end option :e ! end end Enable Labs !26 26 @mark_menard WiteSmallThings - February 8, 2014
  • 27. CommandLineOptions options are true if present are false if absent ! Finished in 0.00206 seconds 2 examples, 0 failures Enable Labs !27 27 @mark_menard WiteSmallThings - February 8, 2014
  • 29. # some_ruby_program -v -efoo ! options = CommandLineOptions.new(ARGV) do option :v option :e, :string end ! if options.has(:v) # Do something end ! if options.has(:e) some_value = options.value(:e) # Do something with the expression end Enable Labs !29 29 @mark_menard WiteSmallThings - February 8, 2014
  • 30. describe CommandLineOptions do ! describe "boolean options" do let(:options) { CommandLineOptions.new { option :c } } it "are true if present" do … it "are false if absent" do … end ! describe "string options" do let(:options) { CommandLineOptions.new { option :e, :string } } it "must have content" do … it "can return the value" do … it "return nil if not in argv" do … end end Enable Labs !30 30 @mark_menard WiteSmallThings - February 8, 2014
  • 31. CommandLineOptions boolean options are true if present are false if absent string options must have content (FAILED - 1) can return the value (FAILED - 2) return nil if not in argv (FAILED - 3) ! Failures: ! ! ! ! 1) CommandLineOptions string options must have content Failure/Error: expect(options.valid?).to be_false NoMethodError: undefined method `valid?' for #<CommandLineOptions:0x00000102cc8fa0> # ./spec/command_line_options_spec.rb:26:in `block (3 levels) in <top (required)>' 2) CommandLineOptions string options can return the value Failure/Error: expect(options.value(:e)).to eq("foo") NoMethodError: undefined method `value' for #<CommandLineOptions:0x00000102cca1c0> # ./spec/command_line_options_spec.rb:31:in `block (3 levels) in <top (required)>' 3) CommandLineOptions string options return nil if not in argv Failure/Error: expect(options.value(:e)).to be_nil NoMethodError: undefined method `value' for #<CommandLineOptions:0x00000102cc2a38> # ./spec/command_line_options_spec.rb:35:in `block (3 levels) in <top (required)>' Finished in 0.00205 seconds 5 examples, 3 failures Enable Labs !31 31 @mark_menard WiteSmallThings - February 8, 2014
  • 32. def valid? options.each do |option_flag, option_type| raw_option_value = argv.find { |arg| arg =~ /-#{option_flag}/ } return false if raw_option_value.length < 3 && option_type == :string end end Enable Labs !32 32 @mark_menard WiteSmallThings - February 8, 2014
  • 33. CommandLineOptions boolean options are true if present are false if absent string options must have content can return the value (FAILED - 1) return nil if not in argv (FAILED - 2) ! Failures: ! 1) CommandLineOptions string options can return the value Failure/Error: expect(options.value(:e)).to eq("foo") NoMethodError: undefined method `value' for #<CommandLineOptions:0x00000101cbb6f8> # ./spec/command_line_options_spec.rb:31:in `block (3 levels) in <top (required)>' ! 2) CommandLineOptions string options return nil if not in argv Failure/Error: expect(options.value(:e)).to be_nil NoMethodError: undefined method `value' for #<CommandLineOptions:0x00000101cba2a8> # ./spec/command_line_options_spec.rb:35:in `block (3 levels) in <top (required)>' ! Finished in 0.00206 seconds 5 examples, 2 failures Enable Labs !33 33 @mark_menard WiteSmallThings - February 8, 2014
  • 34. 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 raw_option_value[2..-1] if option_type == :string end Enable Labs !34 34 @mark_menard WiteSmallThings - February 8, 2014
  • 35. CommandLineOptions boolean options are true if present are false if absent string options must have content can return the value return nil if not in argv ! Finished in 0.00236 seconds 5 examples, 0 failures Enable Labs !35 35 @mark_menard WiteSmallThings - February 8, 2014
  • 36. def valid? options.each do |option_flag, option_type| raw_option_value = argv.find { |arg| arg =~ /-#{option_flag}/ } return false if raw_option_value.length < 3 && option_type == :string 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 raw_option_value[2..-1] if option_type == :string end Enable Labs !36 36 @mark_menard WiteSmallThings - February 8, 2014
  • 37. Extract Method Refactoring def print_invoice_for_amount (amount) print_header puts "Name: #{@name}" puts "Amount: #{amount}" end 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 Move this to here High level of abstraction Low level of abstraction @mark_menard Enable Labs 37 WiteSmallThings - February 8, 2014
  • 38. ! ! def valid? options.each do |option_flag, option_type| return false if option_type == :string && raw_value_for_option(option_flag).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 raw_option_value[2..-1] if option_type == :string end private def raw_value_for_option (option_flag) argv.find { |arg| arg =~ /-#{option_flag}/ } end Enable Labs !38 38 @mark_menard WiteSmallThings - February 8, 2014
  • 39. CommandLineOptions boolean options are true if present are false if absent string options must have content can return the value return nil if not in argv ! Finished in 0.00236 seconds 5 examples, 0 failures Enable Labs !39 39 @mark_menard WiteSmallThings - February 8, 2014
  • 40. class 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, option_type = :boolean) options[option_flag] = option_type end def valid? options.each do |option_flag, option_type| return false if option_type == :string && raw_value_for_option(option_flag).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 raw_option_value[2..-1] if option_type == :string end private def raw_value_for_option (option_flag) argv.find { |arg| arg =~ /-#{option_flag}/ } end end Enable Labs !40 40 @mark_menard WiteSmallThings - February 8, 2014
  • 41. ! ! def valid? options.each do |option_flag, option_type| return false if option_type == :string && raw_value_for_option(option_flag).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 raw_option_value[2..-1] if option_type == :string end private def raw_value_for_option (option_flag) argv.find { |arg| arg =~ /-#{option_flag}/ } end Enable Labs !41 41 @mark_menard WiteSmallThings - February 8, 2014
  • 43. # 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 !43 43 @mark_menard WiteSmallThings - February 8, 2014
  • 44. describe "integer options" do it "must have content" it "must be an integer" it "can return the value as an integer" it "returns nil if not in argv" end Enable Labs !44 44 @mark_menard WiteSmallThings - February 8, 2014
  • 45. CommandLineOptions boolean options are true if present are false if absent string options must have content can return the value return nil if not in argv integer options must have content (PENDING: No reason given) must be an integer (PENDING: Not yet implemented) can return the value as an integer (PENDING: Not yet implemented) returns nil if not in argv (PENDING: Not yet implemented) Enable Labs !45 45 @mark_menard WiteSmallThings - February 8, 2014
  • 46. let(:options) { CommandLineOptions.new { option :i, :integer } } ! it "must have content" do options.argv = [ "-i" ] expect(options.valid?).to be_false end Enable Labs !46 46 @mark_menard WiteSmallThings - February 8, 2014
  • 47. def valid? options.each do |option_flag, option_type| return false if option_type == :string && raw_value_for_option(option_flag).length < 3 end end Enable Labs !47 47 @mark_menard WiteSmallThings - February 8, 2014
  • 48. def valid? options.each do |option_flag, option_type| return false if (option_type == :string || option_type == :integer)&& raw_value_for_option(option_flag).length < 3 end end Enable Labs !48 48 @mark_menard WiteSmallThings - February 8, 2014
  • 49. it "must be an integer" do options.argv = [ "-inot_an_integer" ] expect(options.valid?).to be_false end Enable Labs !49 49 @mark_menard WiteSmallThings - February 8, 2014
  • 50. def valid? options.each do |option_flag, option_type| return false if (option_type == :string || option_type == :integer) && raw_value_for_option(option_flag).length < 3 return false unless option_type == :integer && (Integer(raw_value_for_option(option_flag)[2..-1]) rescue false) end end Enable Labs !50 50 @mark_menard WiteSmallThings - February 8, 2014
  • 51. CommandLineOptions boolean options are true if present are false if absent string options must have 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 (PENDING: Not yet implemented) returns nil if not in argv (PENDING: Not yet implemented) Enable Labs !51 51 @mark_menard WiteSmallThings - February 8, 2014
  • 52. class 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, option_type = :boolean) options[option_flag] = option_type end def valid? options.each do |option_flag, option_type| return false if (option_type == :string || option_type == :integer) && raw_value_for_option(option_flag).length < 3 return false unless option_type == :integer && (Integer(raw_value_for_option(option_flag)[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 raw_option_value[2..-1] if option_type == :string end private def raw_value_for_option (option_flag) argv.find { |arg| arg =~ /-#{option_flag}/ } end end Enable Labs !52 52 @mark_menard WiteSmallThings - February 8, 2014
  • 53. def valid? options.each do |option_flag, option_type| return false if (option_type == :string || option_type == :integer) && raw_value_for_option(option_flag).length < 3 return false unless option_type == :integer && (Integer(raw_value_for_option(option_flag)[2..-1]) rescue false) end end Enable Labs !54 54 @mark_menard WiteSmallThings - February 8, 2014
  • 54. Integer("foo") rescue false #=> false "foo".to_i #=> 0 !!!!!!! Enable Labs !55 55 @mark_menard WiteSmallThings - February 8, 2014
  • 55. ! def valid? options.each do |option_flag, option_type| case(option_type) when :string return false if raw_value_for_option(option_flag).length < 3 when :integer return false if raw_value_for_option(option_flag).length < 3 return false unless (Integer(raw_value_for_option(option_flag)[2..-1]) rescue false) end end end Enable Labs !56 56 @mark_menard WiteSmallThings - February 8, 2014
  • 56. def valid? options.each do |option_flag, option_type| case(option_type) when :string return false if raw_value_for_option(option_flag).length < 3 when :integer return false if raw_value_for_option(option_flag).length < 3 return false unless (Integer(raw_value_for_option(option_flag)[2..-1]) rescue false) when :boolean end end end Enable Labs !57 57 @mark_menard WiteSmallThings - February 8, 2014
  • 57. CommandLineOptions boolean options are true if present are false if absent string options must have 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 (PENDING: Not yet implemented) returns nil if not in argv (PENDING: Not yet implemented) Enable Labs !58 58 @mark_menard WiteSmallThings - February 8, 2014
  • 58. def valid? options.each do |option_flag, option_type| case(option_type) when :string return false unless string_option_valid?(raw_value_for_option(option_flag)) when :integer return false unless integer_option_valid?(raw_value_for_option(option_flag)) when :boolean end end end ! private def string_option_valid? (raw_value) extract_value_from_raw_value(raw_value).length > 0 end ! private def integer_option_valid? (raw_value) extract_value_from_raw_value(raw_value).length > 0 && (Integer(extract_value_from_raw_value(raw_value)) rescue false) end Enable Labs !59 59 @mark_menard WiteSmallThings - February 8, 2014
  • 59. private def boolean_option_valid? (raw_value) true end Enable Labs !60 60 @mark_menard WiteSmallThings - February 8, 2014
  • 60. CommandLineOptions boolean options are true if present are false if absent string options must have 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 (PENDING: Not yet implemented) returns nil if not in argv (PENDING: Not yet implemented) Enable Labs !61 61 @mark_menard WiteSmallThings - February 8, 2014
  • 61. def valid? options.each do |option_flag, option_type| return false unless send("#{option_type}_option_valid?", raw_value_for_option(option_flag)) end end Enable Labs !62 62 @mark_menard WiteSmallThings - February 8, 2014
  • 62. def valid? options.each do |option_flag, option_type| return false unless option_valid?(option_type, raw_value_for_option(option_flag)) end end ! private def option_valid? (option_type, raw_value) send("#{option_type}_option_valid?", raw_value) end Enable Labs !63 63 @mark_menard WiteSmallThings - February 8, 2014
  • 63. 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 (PENDING: Not yet implemented) ! Pending: CommandLineOptions integer options returns nil if not in argv # Not yet implemented # ./spec/command_line_options_spec.rb:61 ! Finished in 0.00291 seconds 10 examples, 0 failures, 1 pending Enable Labs !64 64 @mark_menard WiteSmallThings - February 8, 2014
  • 64. ! ! class 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 valid? options.each do |option_flag, option_type| return false unless option_valid?(option_type, raw_value_for_option(option_flag)) 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 extract_value_from_raw_value(raw_option_value) if option_type == :string end private def option (option_flag, option_type = :boolean) options[option_flag] = option_type end private def option_valid? (option_type, raw_value) send("#{option_type}_option_valid?", raw_value) end private def string_option_valid? (raw_value) extract_value_from_raw_value(raw_value).length > 0 end private def integer_option_valid? (raw_value) extract_value_from_raw_value(raw_value).length > 0 && (Integer(extract_value_from_raw_value(raw_value)) rescue false) end private def boolean_option_valid? (raw_value) true end private def extract_value_from_raw_value (raw_value) raw_value[2..-1] end private def raw_value_for_option (option_flag) argv.find { |arg| arg =~ /-#{option_flag}/ } end end Enable Labs !65 65 @mark_menard WiteSmallThings - February 8, 2014
  • 65. def has (option_flag) options.include?(option_flag) && argv.include?("-#{option_flag}") end ! def valid? options.each do |option_flag, option_type| return false unless option_valid?(option_type, raw_value_for_option(option_flag)) 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 extract_value_from_raw_value(raw_option_value) if option_type == :string end Enable Labs !66 66 @mark_menard WiteSmallThings - February 8, 2014
  • 67. 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 @mark_menard Enable Labs 68 WiteSmallThings - February 8, 2014
  • 68. 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! Dependencies are injected @mark_menard Enable Labs 69 WiteSmallThings - February 8, 2014
  • 69. describe StringOption do let(:string_option) { StringOption.new('s', '-sfoo') } ! it it it it end "has a flag" "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 !70 70 @mark_menard WiteSmallThings - February 8, 2014
  • 70. 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 (PENDING: Not yet implemented) ! StringOption has a flag (PENDING: No reason given) is valid when it has a value (PENDING: Not yet implemented) can return it's value when present (PENDING: Not yet implemented) returns nil if the flag has no raw value (PENDING: Not yet implemented) Enable Labs !71 71 @mark_menard WiteSmallThings - February 8, 2014
  • 71. class StringOption ! attr_reader :flag ! def initialize (flag, raw_value) @flag = flag @raw_value = raw_value end ! end Enable Labs !72 72 @mark_menard WiteSmallThings - February 8, 2014
  • 72. it "is valid when it has a value" do expect(string_option.valid?).to be_true end Enable Labs !73 73 @mark_menard WiteSmallThings - February 8, 2014
  • 73. class StringOption class CommandLineOptions ! ! ! private def string_option_valid? (raw_value) extract_value_from_raw_value(raw_value).length > 0 end ! end ! ! ! attr_reader :flag, :raw_value def initialize (flag, raw_value) @flag = flag @raw_value = raw_value end def valid? extract_value_from_raw_value.length > 0 end private def extract_value_from_raw_value raw_value[2..-1] end end Enable Labs !74 74 @mark_menard WiteSmallThings - February 8, 2014
  • 74. class StringOption ! attr_reader :flag, :raw_value ! def initialize (flag, raw_value) @flag = flag @raw_value = raw_value end ! def valid? extract_value_from_raw_value.length > 0 end ! private def extract_value_from_raw_value raw_value[2..-1] end ! end Enable Labs !75 75 @mark_menard WiteSmallThings - February 8, 2014
  • 75. private def string_option_valid? (raw_value) StringOption.new("", raw_value).valid? end Enable Labs !76 76 @mark_menard WiteSmallThings - February 8, 2014
  • 76. class IntegerOption ! attr_reader :flag, :raw_value ! def initialize (flag, raw_value) @flag = flag @raw_value = raw_value end ! def valid? extract_value_from_raw_value.length > 0 && real_value_is_integer? end ! private def extract_value_from_raw_value raw_value[2..-1] end ! private def real_value_is_integer? (Integer(extract_value_from_raw_value) rescue false) end ! end Enable Labs !77 77 @mark_menard WiteSmallThings - February 8, 2014
  • 77. class StringOption ! attr_reader :flag, :raw_value ! def initialize (flag, raw_value) @flag = flag @raw_value = raw_value end ! def valid? extract_value_from_raw_value.length > 0 end ! private def extract_value_from_raw_value raw_value[2..-1] end ! end Enable Labs !78 78 @mark_menard WiteSmallThings - February 8, 2014
  • 78. class OptionWithContent ! attr_reader :flag, :raw_value ! def initialize (flag, raw_value) @flag = flag @raw_value = raw_value end ! def valid? extract_value_from_raw_value.length > 0 end ! private def extract_value_from_raw_value raw_value[2..-1] end ! end Enable Labs !79 79 @mark_menard WiteSmallThings - February 8, 2014
  • 79. class StringOption < OptionWithContent end ! class IntegerOption < OptionWithContent ! def valid? super && real_value_is_integer? end ! private def real_value_is_integer? (Integer(extract_value_from_raw_value) rescue false) end ! end Enable Labs !80 80 @mark_menard WiteSmallThings - February 8, 2014
  • 80. 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 (PENDING: Not yet implemented) ! StringOption has a flag is valid when it has a value can return it's value when present (PENDING: Not yet implemented) returns nil if the flag has no raw value (PENDING: Not yet implemented) Enable Labs !81 81 @mark_menard WiteSmallThings - February 8, 2014
  • 82. How do we deal with Dependencies? • • Dependency Injection! Depend on Abstractions @mark_menard Enable Labs 82 WiteSmallThings - February 8, 2014
  • 83. private def option (option_flag, option_type = :boolean) options[option_flag] = option_type end 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 private def option (option_flag, option_type = :boolean) option_class = "#{option_type}_option".camelize.constantize options[option_flag] = option_class.new(option_flag, nil) end Enable Labs !84 84 @mark_menard WiteSmallThings - February 8, 2014
  • 84. How do we isolate abstractions? Separate the “what” from the “how”. @mark_menard Enable Labs 85 WiteSmallThings - February 8, 2014
  • 85. CommandLineOptions boolean options are true if present are false if absent string options must have content (FAILED - 1) is valid when there is content (FAILED - 2) can return the value (FAILED - 3) return nil if not in argv integer options must have content (FAILED - 4) must be an integer (FAILED - 5) can return the value as an integer returns nil if not in argv (PENDING: Not yet implemented) ! StringOption has a flag is valid when it has a value can return it's value when present (PENDING: Not yet implemented) returns nil if the flag has no raw value (PENDING: Not yet implemented) Enable Labs !86 86 @mark_menard WiteSmallThings - February 8, 2014
  • 86. def valid? options.each do |option_flag, option_type| return false unless option_valid?(option_type, raw_value_for_option(option_flag)) end end def valid? options.values.all?(&:valid?) end Enable Labs !87 87 @mark_menard WiteSmallThings - February 8, 2014
  • 87. 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 (FAILED - 1) 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 (PENDING: Not yet implemented) ! StringOption has a flag is valid when it has a value can return it's value when present (PENDING: Not yet implemented) returns nil if the flag has no raw value (PENDING: Not yet implemented) Enable Labs !88 88 @mark_menard WiteSmallThings - February 8, 2014
  • 88. 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 raw_option_value[2..-1] if option_type == :string end def value (option_flag) options[option_flag].value end Enable Labs !89 89 @mark_menard WiteSmallThings - February 8, 2014
  • 89. 1) CommandLineOptions string options can return the value Failure/Error: expect(options.value(:e)).to eq("foo") NoMethodError: undefined method `value' for #<StringOption:0x00000101b85b30 @flag=:e, @raw_value="-efoo"> # ./lib/command_line_options.rb:26:in `value' # ./spec/command_line_options_spec.rb:36:in `block (3 levels) in <top (required)>' Enable Labs !90 90 @mark_menard WiteSmallThings - February 8, 2014
  • 90. # OptionWithContent def value return nil if option_unset? valid? ? extract_value_from_raw_value : nil end # IntegerOption def value return if option_unset? Integer(extract_value_from_raw_value) end Enable Labs !91 91 @mark_menard WiteSmallThings - February 8, 2014
  • 91. 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 ! 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 !92 92 @mark_menard WiteSmallThings - February 8, 2014
  • 93. class CommandLineOptions ! ! ! ! ! ! ! ! ! ! def initialize (argv = [], &block) @options = {} @argv = argv instance_eval &block populate_options_with_raw_values end def valid? options.values.all?(&:valid?) end def value (option_flag) options[option_flag].value end private attr_reader :options, :argv def populate_options_with_raw_values options.each do |option_flag, option| option.raw_value = raw_value_for_option(option_flag) end end def option (option_flag, option_type = :boolean) options[option_flag] = get_option_class(option_type).new(option_flag, nil) end def get_option_class (option_type) "#{option_type}_option".camelize.constantize end def raw_value_for_option (option_flag) argv.find { |arg| arg =~ /-#{option_flag}/ } end end Enable Labs !94 94 @mark_menard WiteSmallThings - February 8, 2014
  • 94. ! def valid? options.values.all?(&:valid?) end ! def value (option_flag) options[option_flag].value end Enable Labs !95 95 @mark_menard WiteSmallThings - February 8, 2014
  • 96. some_ruby_program -v -efoo -i100 -afoo,bar,baz Enable Labs !97 97 @mark_menard WiteSmallThings - February 8, 2014
  • 97. 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 !98 98 @mark_menard WiteSmallThings - February 8, 2014
  • 98. 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 !99 99 @mark_menard WiteSmallThings - February 8, 2014