write small things (code)
DESCRIPTION
Writing small code is hard. You know you should, but how do you actually do it? It's so much easier to write a large class. In this talk we'll build up a set of small classes starting from nothing using a set of directed refactorings applied as we build. All while keeping our classes small. We'll identify abstractions yearning to be free of their big object cages. In the process we'll also see how basic patterns such as composition, delegation and dependency injection emerge from using small objects. We'll even write some tests too.TRANSCRIPT
![Page 1: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/1.jpg)
Enable Labs @mark_menard
Write Small ThingsMark Menard
Los Angeles Ruby Conf 2014
@mark_menard Enable Labs
1 WiteSmallThings - February 8, 2014
![Page 2: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/2.jpg)
Enable Labs @mark_menard
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
2 WiteSmallThings - February 8, 2014
![Page 3: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/3.jpg)
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
3 WiteSmallThings - February 8, 2014
![Page 4: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/4.jpg)
Enable Labs @mark_menard
Introduction
4 WiteSmallThings - February 8, 2014
![Page 5: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/5.jpg)
Enable Labs @mark_menard
What is meant by small?
5 WiteSmallThings - February 8, 2014
![Page 6: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/6.jpg)
Enable Labs @mark_menard
It’s not about total line count.
6 WiteSmallThings - February 8, 2014
![Page 7: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/7.jpg)
Enable Labs @mark_menard
It’s not about method count.
7 WiteSmallThings - February 8, 2014
![Page 8: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/8.jpg)
Enable Labs @mark_menard
It’s not about class count.
8 WiteSmallThings - February 8, 2014
![Page 9: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/9.jpg)
Enable Labs @mark_menard
So, what do I mean by small things?
Small methods! Small classes
9 WiteSmallThings - February 8, 2014
![Page 10: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/10.jpg)
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
10 WiteSmallThings - February 8, 2014
![Page 11: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/11.jpg)
Enable Labs @mark_menard
What are the challenges of small code?• Dependency Management!• Context Management
11 WiteSmallThings - February 8, 2014
![Page 12: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/12.jpg)
Enable Labs @mark_menard
The goal: Small units of understandable code that are amenable to change.
12 WiteSmallThings - February 8, 2014
![Page 13: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/13.jpg)
Enable Labs @mark_menard
Our Three Main Tools
• Extract Method!
• Extract Class!
• Composed Method
13 WiteSmallThings - February 8, 2014
![Page 14: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/14.jpg)
Enable Labs @mark_menard
Methods
14 WiteSmallThings - February 8, 2014
![Page 15: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/15.jpg)
Enable Labs @mark_menard
“The object programs that live best and longest are those with
short methods.”!! ! ! ! ! ! -Refactoring by Fields, Harvey, Fowler, Black
15 WiteSmallThings - February 8, 2014
![Page 16: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/16.jpg)
Enable Labs @mark_menard
The first rule of methods:
16 WiteSmallThings - February 8, 2014
![Page 17: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/17.jpg)
Enable Labs @mark_menard
Do one thing. Do it well. Do only that thing.
17 WiteSmallThings - February 8, 2014
![Page 18: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/18.jpg)
Enable Labs @mark_menard
Use Descriptive Names
18 WiteSmallThings - February 8, 2014
![Page 19: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/19.jpg)
Enable Labs @mark_menard
The fewer arguments the better.
19 WiteSmallThings - February 8, 2014
![Page 20: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/20.jpg)
Enable Labs @mark_menard
Separate Queries from Commands
20 WiteSmallThings - February 8, 2014
![Page 21: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/21.jpg)
Enable Labs @mark_menard
Don’t Repeat Yourself
21 WiteSmallThings - February 8, 2014
![Page 22: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/22.jpg)
Enable Labs @mark_menard
Let’s Build a Command Line Option Library
22 WiteSmallThings - February 8, 2014
![Page 23: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/23.jpg)
Enable Labs @mark_menard!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
23 WiteSmallThings - February 8, 2014
![Page 24: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/24.jpg)
Enable Labs @mark_menard!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
24 WiteSmallThings - February 8, 2014
![Page 25: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/25.jpg)
Enable Labs @mark_menard!25
CommandLineOptions options are true if present are false if absent !
Finished in 0.00206 seconds 2 examples, 2 failures
25 WiteSmallThings - February 8, 2014
![Page 26: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/26.jpg)
Enable Labs @mark_menard!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 ! def option (option_flag) options << option_flag end !end
options = CommandLineOptions.new(ARGV) do option :c option :v option :e end
26 WiteSmallThings - February 8, 2014
![Page 27: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/27.jpg)
Enable Labs @mark_menard!27
CommandLineOptions options are true if present are false if absent !
Finished in 0.00206 seconds 2 examples, 0 failures
27 WiteSmallThings - February 8, 2014
![Page 28: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/28.jpg)
Enable Labs @mark_menard
Done!
28 WiteSmallThings - February 8, 2014
![Page 29: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/29.jpg)
Enable Labs @mark_menard!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
29 WiteSmallThings - February 8, 2014
![Page 30: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/30.jpg)
Enable Labs @mark_menard
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
!30
30 WiteSmallThings - February 8, 2014
![Page 31: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/31.jpg)
Enable Labs @mark_menard
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 seconds5 examples, 3 failures
!31
31 WiteSmallThings - February 8, 2014
![Page 32: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/32.jpg)
Enable Labs @mark_menard!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
32 WiteSmallThings - February 8, 2014
![Page 33: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/33.jpg)
Enable Labs @mark_menard
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 seconds5 examples, 2 failures
!33
33 WiteSmallThings - February 8, 2014
![Page 34: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/34.jpg)
Enable Labs @mark_menard!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
34 WiteSmallThings - February 8, 2014
![Page 35: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/35.jpg)
Enable Labs @mark_menard!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 seconds5 examples, 0 failures
35 WiteSmallThings - February 8, 2014
![Page 36: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/36.jpg)
Enable Labs @mark_menard
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
!36
36 WiteSmallThings - February 8, 2014
![Page 37: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/37.jpg)
Enable Labs @mark_menard
Extract Method Refactoringdef 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
High level of abstraction
Low level of abstraction
Same level of abstraction
Move this to here
37 WiteSmallThings - February 8, 2014
![Page 38: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/38.jpg)
Enable Labs @mark_menard!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
38 WiteSmallThings - February 8, 2014
![Page 39: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/39.jpg)
Enable Labs @mark_menard!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 seconds5 examples, 0 failures
39 WiteSmallThings - February 8, 2014
![Page 40: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/40.jpg)
Enable Labs @mark_menard!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
40 WiteSmallThings - February 8, 2014
![Page 41: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/41.jpg)
Enable Labs @mark_menard!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
41 WiteSmallThings - February 8, 2014
![Page 42: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/42.jpg)
Enable Labs @mark_menard
Done!
42 WiteSmallThings - February 8, 2014
![Page 43: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/43.jpg)
Enable Labs @mark_menard!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
43 WiteSmallThings - February 8, 2014
![Page 44: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/44.jpg)
Enable Labs @mark_menard!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
44 WiteSmallThings - February 8, 2014
![Page 45: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/45.jpg)
Enable Labs @mark_menard!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)
45 WiteSmallThings - February 8, 2014
![Page 46: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/46.jpg)
Enable Labs @mark_menard!46
let(:options) { CommandLineOptions.new { option :i, :integer } } ! it "must have content" do options.argv = [ "-i" ] expect(options.valid?).to be_false end
46 WiteSmallThings - February 8, 2014
![Page 47: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/47.jpg)
Enable Labs @mark_menard!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
47 WiteSmallThings - February 8, 2014
![Page 48: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/48.jpg)
Enable Labs @mark_menard!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
48 WiteSmallThings - February 8, 2014
![Page 49: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/49.jpg)
Enable Labs @mark_menard!49
it "must be an integer" do options.argv = [ "-inot_an_integer" ] expect(options.valid?).to be_false end
49 WiteSmallThings - February 8, 2014
![Page 50: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/50.jpg)
Enable Labs @mark_menard!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
50 WiteSmallThings - February 8, 2014
![Page 51: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/51.jpg)
Enable Labs @mark_menard!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)
51 WiteSmallThings - February 8, 2014
![Page 52: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/52.jpg)
Enable Labs @mark_menard!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
52 WiteSmallThings - February 8, 2014
![Page 53: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/53.jpg)
Enable Labs @mark_menard!54
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
54 WiteSmallThings - February 8, 2014
![Page 54: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/54.jpg)
Enable Labs @mark_menard!55
Integer("foo") rescue false #=> false "foo".to_i #=> 0 !!!!!!!
55 WiteSmallThings - February 8, 2014
![Page 55: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/55.jpg)
Enable Labs @mark_menard!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) ! end end end
56 WiteSmallThings - February 8, 2014
![Page 56: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/56.jpg)
Enable Labs @mark_menard!57
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
57 WiteSmallThings - February 8, 2014
![Page 57: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/57.jpg)
Enable Labs @mark_menard!58
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)
58 WiteSmallThings - February 8, 2014
![Page 58: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/58.jpg)
Enable Labs @mark_menard!59
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
59 WiteSmallThings - February 8, 2014
![Page 59: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/59.jpg)
Enable Labs @mark_menard!60
private def boolean_option_valid? (raw_value) true end
60 WiteSmallThings - February 8, 2014
![Page 60: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/60.jpg)
Enable Labs @mark_menard!61
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)
61 WiteSmallThings - February 8, 2014
![Page 61: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/61.jpg)
Enable Labs @mark_menard!62
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
62 WiteSmallThings - February 8, 2014
![Page 62: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/62.jpg)
Enable Labs @mark_menard!63
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
63 WiteSmallThings - February 8, 2014
![Page 63: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/63.jpg)
Enable Labs @mark_menard!64
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 seconds10 examples, 0 failures, 1 pending
64 WiteSmallThings - February 8, 2014
![Page 64: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/64.jpg)
Enable Labs @mark_menard!65
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
65 WiteSmallThings - February 8, 2014
![Page 65: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/65.jpg)
Enable Labs @mark_menard!66
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
66 WiteSmallThings - February 8, 2014
![Page 66: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/66.jpg)
Enable Labs @mark_menard
Classes
67 WiteSmallThings - February 8, 2014
![Page 67: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/67.jpg)
Enable Labs @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
68 WiteSmallThings - February 8, 2014
![Page 68: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/68.jpg)
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!
• Dependencies are injected
69 WiteSmallThings - February 8, 2014
![Page 69: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/69.jpg)
Enable Labs @mark_menard!70
describe StringOption do let(:string_option) { StringOption.new('s', '-sfoo') } ! it "has a flag" it "is valid when it has a value" it "can return it's value when present" it "returns nil if the flag has no raw value" end
70 WiteSmallThings - February 8, 2014
![Page 70: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/70.jpg)
Enable Labs @mark_menard!71
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)
71 WiteSmallThings - February 8, 2014
![Page 71: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/71.jpg)
Enable Labs @mark_menard!72
class StringOption !
attr_reader :flag !
def initialize (flag, raw_value) @flag = flag @raw_value = raw_value end !
end
72 WiteSmallThings - February 8, 2014
![Page 72: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/72.jpg)
Enable Labs @mark_menard!73
it "is valid when it has a value" do expect(string_option.valid?).to be_true end
73 WiteSmallThings - February 8, 2014
![Page 73: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/73.jpg)
Enable Labs @mark_menard!74
class CommandLineOptions ! private def string_option_valid? (raw_value) extract_value_from_raw_value(raw_value).length > 0 end !end
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
74 WiteSmallThings - February 8, 2014
![Page 74: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/74.jpg)
Enable Labs @mark_menard!75
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
75 WiteSmallThings - February 8, 2014
![Page 75: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/75.jpg)
Enable Labs @mark_menard!76
private def string_option_valid? (raw_value) StringOption.new("", raw_value).valid? end
76 WiteSmallThings - February 8, 2014
![Page 76: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/76.jpg)
Enable Labs @mark_menard!77
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
77 WiteSmallThings - February 8, 2014
![Page 77: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/77.jpg)
Enable Labs @mark_menard!78
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
78 WiteSmallThings - February 8, 2014
![Page 78: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/78.jpg)
Enable Labs @mark_menard!79
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
79 WiteSmallThings - February 8, 2014
![Page 79: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/79.jpg)
Enable Labs @mark_menard!80
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
80 WiteSmallThings - February 8, 2014
![Page 80: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/80.jpg)
Enable Labs @mark_menard!81
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)
81 WiteSmallThings - February 8, 2014
![Page 81: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/81.jpg)
Enable Labs @mark_menard
Dependencies
83 WiteSmallThings - February 8, 2014
![Page 82: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/82.jpg)
Enable Labs @mark_menard
How do we deal with Dependencies?• Dependency Injection!
• Depend on Abstractions
82 WiteSmallThings - February 8, 2014
![Page 83: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/83.jpg)
Enable Labs @mark_menard!84
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
84 WiteSmallThings - February 8, 2014
![Page 84: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/84.jpg)
Enable Labs @mark_menard
How do we isolate abstractions?
Separate the “what” from the “how”.
85 WiteSmallThings - February 8, 2014
![Page 85: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/85.jpg)
Enable Labs @mark_menard!86
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)
86 WiteSmallThings - February 8, 2014
![Page 86: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/86.jpg)
Enable Labs @mark_menard!87
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
87 WiteSmallThings - February 8, 2014
![Page 87: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/87.jpg)
Enable Labs @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 (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)
88 WiteSmallThings - February 8, 2014
![Page 88: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/88.jpg)
Enable Labs @mark_menard!89
def value (option_flag) options[option_flag].value 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
89 WiteSmallThings - February 8, 2014
![Page 89: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/89.jpg)
Enable Labs @mark_menard!90
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)>'
90 WiteSmallThings - February 8, 2014
![Page 90: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/90.jpg)
Enable Labs @mark_menard!91
# 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
91 WiteSmallThings - February 8, 2014
![Page 91: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/91.jpg)
Enable Labs @mark_menard!92
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
92 WiteSmallThings - February 8, 2014
![Page 92: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/92.jpg)
Enable Labs @mark_menard
Done!
93 WiteSmallThings - February 8, 2014
![Page 93: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/93.jpg)
Enable Labs @mark_menard!94
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
94 WiteSmallThings - February 8, 2014
![Page 94: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/94.jpg)
Enable Labs @mark_menard!95
!
def valid? options.values.all?(&:valid?) end !
def value (option_flag) options[option_flag].value end
95 WiteSmallThings - February 8, 2014
![Page 95: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/95.jpg)
Enable Labs @mark_menard
Then
96 WiteSmallThings - February 8, 2014
![Page 96: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/96.jpg)
Enable Labs @mark_menard!97
some_ruby_program -v -efoo -i100 -afoo,bar,baz
97 WiteSmallThings - February 8, 2014
![Page 97: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/97.jpg)
Enable Labs @mark_menard!98
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
98 WiteSmallThings - February 8, 2014
![Page 98: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/98.jpg)
Enable Labs @mark_menard!99
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
99 WiteSmallThings - February 8, 2014
![Page 99: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/99.jpg)
Enable Labs @mark_menard
Done!
100 WiteSmallThings - February 8, 2014
![Page 100: Write Small Things (Code)](https://reader033.vdocuments.site/reader033/viewer/2022051514/54bc89274a79591e3e8b4599/html5/thumbnails/100.jpg)
Enable Labs @mark_menard
Start Todayhttp://www.enablelabs.com/
866-895-8189
Enable Labs@mark_menard
101 WiteSmallThings - February 8, 2014