naught 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +2 -0
- data/Changelog.md +6 -0
- data/Gemfile +1 -0
- data/Guardfile +10 -0
- data/README.markdown +413 -0
- data/Rakefile +5 -0
- data/lib/naught.rb +1 -4
- data/lib/naught/null_class_builder.rb +37 -137
- data/lib/naught/null_class_builder/command.rb +20 -0
- data/lib/naught/null_class_builder/commands.rb +8 -0
- data/lib/naught/null_class_builder/commands/define_explicit_conversions.rb +13 -24
- data/lib/naught/null_class_builder/commands/define_implicit_conversions.rb +14 -0
- data/lib/naught/null_class_builder/commands/impersonate.rb +9 -0
- data/lib/naught/null_class_builder/commands/mimic.rb +40 -0
- data/lib/naught/null_class_builder/commands/pebble.rb +36 -0
- data/lib/naught/null_class_builder/commands/predicates_return.rb +47 -0
- data/lib/naught/null_class_builder/commands/singleton.rb +24 -0
- data/lib/naught/null_class_builder/commands/traceable.rb +19 -0
- data/lib/naught/null_class_builder/conversions_module.rb +57 -0
- data/lib/naught/version.rb +1 -1
- data/naught.gemspec +2 -1
- data/spec/base_object_spec.rb +47 -0
- data/spec/basic_null_object_spec.rb +35 -0
- data/spec/blackhole_spec.rb +16 -0
- data/spec/conversions_spec.rb +20 -0
- data/spec/functions/actual_spec.rb +22 -0
- data/spec/functions/just_spec.rb +22 -0
- data/spec/functions/maybe_spec.rb +35 -0
- data/spec/functions/null_spec.rb +34 -0
- data/spec/implicit_conversions_spec.rb +25 -0
- data/spec/mimic_spec.rb +122 -0
- data/spec/naught/null_object_builder/command_spec.rb +10 -0
- data/spec/naught/null_object_builder_spec.rb +31 -0
- data/spec/naught_spec.rb +77 -411
- data/spec/pebble_spec.rb +75 -0
- data/spec/predicate_spec.rb +80 -0
- data/spec/singleton_null_object_spec.rb +35 -0
- data/spec/spec_helper.rb +13 -1
- data/spec/support/convertable_null.rb +4 -0
- metadata +76 -32
- data/.rspec +0 -1
- data/README.org +0 -340
data/spec/pebble_spec.rb
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
describe 'pebble null object' do
|
5
|
+
class Caller
|
6
|
+
def call_method(thing)
|
7
|
+
thing.info
|
8
|
+
end
|
9
|
+
|
10
|
+
def call_method_inside_block(thing)
|
11
|
+
2.times.each { thing.info }
|
12
|
+
end
|
13
|
+
|
14
|
+
def call_method_inside_nested_block(thing)
|
15
|
+
2.times.each { 2.times.each { thing.info } }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
subject(:null) { null_class.new }
|
20
|
+
let(:null_class) {
|
21
|
+
output = test_output # getting local binding
|
22
|
+
Naught.build do |b|
|
23
|
+
b.pebble output
|
24
|
+
end
|
25
|
+
}
|
26
|
+
|
27
|
+
let(:test_output) { StringIO.new }
|
28
|
+
|
29
|
+
it 'prints the name of the method called' do
|
30
|
+
expect(test_output).to receive(:puts).with(/^info\(\)/)
|
31
|
+
null.info
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'prints the arguments received' do
|
35
|
+
expect(test_output).to receive(:puts).with(/^info\(\'foo\', 5, \:sym\)/)
|
36
|
+
null.info("foo", 5, :sym)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'prints the name of the caller' do
|
40
|
+
expect(test_output).to receive(:puts).with(/from call_method$/)
|
41
|
+
Caller.new.call_method(null)
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'returns self' do
|
45
|
+
expect(null.info).to be(null)
|
46
|
+
end
|
47
|
+
|
48
|
+
context "when is called from a block" do
|
49
|
+
it "prints the indication of a block" do
|
50
|
+
expect(test_output).to receive(:puts).twice.
|
51
|
+
with(/from block/)
|
52
|
+
Caller.new.call_method_inside_block(null)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "prints the name of the method that has the block" do
|
56
|
+
expect(test_output).to receive(:puts).twice.
|
57
|
+
with(/in call_method_inside_block$/)
|
58
|
+
Caller.new.call_method_inside_block(null)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
context "when is called from many levels blocks" do
|
63
|
+
it "prints the indication of blocks and its levels" do
|
64
|
+
expect(test_output).to receive(:puts).exactly(4).times.
|
65
|
+
with(/from block \(2 levels\)/)
|
66
|
+
Caller.new.call_method_inside_nested_block(null)
|
67
|
+
end
|
68
|
+
|
69
|
+
it "prints the name of the method that has the block" do
|
70
|
+
expect(test_output).to receive(:puts).exactly(4).times.
|
71
|
+
with(/in call_method_inside_nested_block$/)
|
72
|
+
Caller.new.call_method_inside_nested_block(null)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'a null object with predicates_return(false)' do
|
4
|
+
subject(:null) { null_class.new }
|
5
|
+
let(:null_class) {
|
6
|
+
Naught.build do |config|
|
7
|
+
config.predicates_return false
|
8
|
+
end
|
9
|
+
}
|
10
|
+
|
11
|
+
it 'responds to predicate-style methods with false' do
|
12
|
+
expect(null.too_much_coffee?).to eq(false)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'responds to other methods with nil' do
|
16
|
+
expect(null.foobar).to eq(nil)
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '(black hole)' do
|
20
|
+
let(:null_class) {
|
21
|
+
Naught.build do |config|
|
22
|
+
config.black_hole
|
23
|
+
config.predicates_return false
|
24
|
+
end
|
25
|
+
}
|
26
|
+
|
27
|
+
it 'responds to predicate-style methods with false' do
|
28
|
+
expect(null.too_much_coffee?).to eq(false)
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'responds to other methods with self' do
|
32
|
+
expect(null.foobar).to be(null)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe '(black hole, reverse order config)' do
|
37
|
+
let(:null_class) {
|
38
|
+
Naught.build do |config|
|
39
|
+
config.predicates_return false
|
40
|
+
config.black_hole
|
41
|
+
end
|
42
|
+
}
|
43
|
+
|
44
|
+
it 'responds to predicate-style methods with false' do
|
45
|
+
expect(null.too_much_coffee?).to eq(false)
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'responds to other methods with self' do
|
49
|
+
expect(null.foobar).to be(null)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
class Coffee
|
55
|
+
def black?; true; end
|
56
|
+
def origin; "Ethiopia"; end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '(mimic)' do
|
60
|
+
let(:null_class) {
|
61
|
+
Naught.build do |config|
|
62
|
+
config.mimic Coffee
|
63
|
+
config.predicates_return false
|
64
|
+
end
|
65
|
+
}
|
66
|
+
|
67
|
+
it 'responds to predicate-style methods with false' do
|
68
|
+
expect(null.black?).to eq(false)
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'responds to other methods with nil' do
|
72
|
+
expect(null.origin).to be(nil)
|
73
|
+
end
|
74
|
+
|
75
|
+
it 'does not respond to undefined methods' do
|
76
|
+
expect(null).not_to respond_to(:leaf_variety)
|
77
|
+
expect{null.leaf_variety}.to raise_error(NoMethodError)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'singleton null object' do
|
4
|
+
subject(:null_class) {
|
5
|
+
Naught.build do |b|
|
6
|
+
b.singleton
|
7
|
+
end
|
8
|
+
}
|
9
|
+
|
10
|
+
it 'does not respond to .new' do
|
11
|
+
expect{ null_class.new }.to raise_error
|
12
|
+
end
|
13
|
+
|
14
|
+
it 'has only one instance' do
|
15
|
+
null1 = null_class.instance
|
16
|
+
null2 = null_class.instance
|
17
|
+
expect(null1).to be(null2)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'can be cloned' do
|
21
|
+
null = null_class.instance
|
22
|
+
expect(null.clone).to be(null)
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'can be duplicated' do
|
26
|
+
null = null_class.instance
|
27
|
+
expect(null.dup).to be(null)
|
28
|
+
end
|
29
|
+
it 'aliases .instance to .get' do
|
30
|
+
expect(null_class.get).to be null_class.instance
|
31
|
+
end
|
32
|
+
it 'permits arbitrary arguments to be passed to .get' do
|
33
|
+
null_class.get(42, foo: "bar")
|
34
|
+
end
|
35
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1 +1,13 @@
|
|
1
|
-
|
1
|
+
GEM_ROOT = File.expand_path("../../", __FILE__)
|
2
|
+
$:.unshift File.join(GEM_ROOT, "lib")
|
3
|
+
|
4
|
+
if ENV["TRAVIS"]
|
5
|
+
require 'coveralls'
|
6
|
+
Coveralls.wear!
|
7
|
+
else
|
8
|
+
require 'simplecov'
|
9
|
+
SimpleCov.start
|
10
|
+
end
|
11
|
+
|
12
|
+
require 'naught'
|
13
|
+
Dir[File.join(GEM_ROOT, "spec", "support", "**/*.rb")].each { |f| require f }
|
metadata
CHANGED
@@ -1,20 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: naught
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.0.3
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Avdi Grimm
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-11-08 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: bundler
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
17
|
- - ~>
|
20
18
|
- !ruby/object:Gem::Version
|
@@ -22,7 +20,6 @@ dependencies:
|
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
24
|
- - ~>
|
28
25
|
- !ruby/object:Gem::Version
|
@@ -30,65 +27,71 @@ dependencies:
|
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rake
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - '>='
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - '>='
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: rspec
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- -
|
45
|
+
- - ~>
|
52
46
|
- !ruby/object:Gem::Version
|
53
|
-
version: '
|
47
|
+
version: '2.14'
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- -
|
52
|
+
- - ~>
|
60
53
|
- !ruby/object:Gem::Version
|
61
|
-
version: '
|
54
|
+
version: '2.14'
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: guard
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
|
-
- -
|
59
|
+
- - '>='
|
68
60
|
- !ruby/object:Gem::Version
|
69
61
|
version: '0'
|
70
62
|
type: :development
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
|
-
- -
|
66
|
+
- - '>='
|
76
67
|
- !ruby/object:Gem::Version
|
77
68
|
version: '0'
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
70
|
name: guard-rspec
|
80
71
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
72
|
requirements:
|
83
|
-
- -
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - '>='
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: guard-bundler
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - '>='
|
84
88
|
- !ruby/object:Gem::Version
|
85
89
|
version: '0'
|
86
90
|
type: :development
|
87
91
|
prerelease: false
|
88
92
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
93
|
requirements:
|
91
|
-
- -
|
94
|
+
- - '>='
|
92
95
|
- !ruby/object:Gem::Version
|
93
96
|
version: '0'
|
94
97
|
description: Naught is a toolkit for building Null Objects
|
@@ -99,45 +102,86 @@ extensions: []
|
|
99
102
|
extra_rdoc_files: []
|
100
103
|
files:
|
101
104
|
- .gitignore
|
102
|
-
- .
|
105
|
+
- .travis.yml
|
106
|
+
- Changelog.md
|
103
107
|
- Gemfile
|
104
108
|
- Guardfile
|
105
109
|
- LICENSE.txt
|
106
|
-
- README.
|
110
|
+
- README.markdown
|
107
111
|
- Rakefile
|
108
112
|
- lib/naught.rb
|
109
113
|
- lib/naught/null_class_builder.rb
|
114
|
+
- lib/naught/null_class_builder/command.rb
|
115
|
+
- lib/naught/null_class_builder/commands.rb
|
110
116
|
- lib/naught/null_class_builder/commands/define_explicit_conversions.rb
|
117
|
+
- lib/naught/null_class_builder/commands/define_implicit_conversions.rb
|
118
|
+
- lib/naught/null_class_builder/commands/impersonate.rb
|
119
|
+
- lib/naught/null_class_builder/commands/mimic.rb
|
120
|
+
- lib/naught/null_class_builder/commands/pebble.rb
|
121
|
+
- lib/naught/null_class_builder/commands/predicates_return.rb
|
122
|
+
- lib/naught/null_class_builder/commands/singleton.rb
|
123
|
+
- lib/naught/null_class_builder/commands/traceable.rb
|
124
|
+
- lib/naught/null_class_builder/conversions_module.rb
|
111
125
|
- lib/naught/version.rb
|
112
126
|
- naught.gemspec
|
127
|
+
- spec/base_object_spec.rb
|
128
|
+
- spec/basic_null_object_spec.rb
|
129
|
+
- spec/blackhole_spec.rb
|
130
|
+
- spec/conversions_spec.rb
|
131
|
+
- spec/functions/actual_spec.rb
|
132
|
+
- spec/functions/just_spec.rb
|
133
|
+
- spec/functions/maybe_spec.rb
|
134
|
+
- spec/functions/null_spec.rb
|
135
|
+
- spec/implicit_conversions_spec.rb
|
136
|
+
- spec/mimic_spec.rb
|
137
|
+
- spec/naught/null_object_builder/command_spec.rb
|
138
|
+
- spec/naught/null_object_builder_spec.rb
|
113
139
|
- spec/naught_spec.rb
|
140
|
+
- spec/pebble_spec.rb
|
141
|
+
- spec/predicate_spec.rb
|
142
|
+
- spec/singleton_null_object_spec.rb
|
114
143
|
- spec/spec_helper.rb
|
144
|
+
- spec/support/convertable_null.rb
|
115
145
|
homepage: https://github.com/avdi/naught
|
116
146
|
licenses:
|
117
147
|
- MIT
|
148
|
+
metadata: {}
|
118
149
|
post_install_message:
|
119
150
|
rdoc_options: []
|
120
151
|
require_paths:
|
121
152
|
- lib
|
122
153
|
required_ruby_version: !ruby/object:Gem::Requirement
|
123
|
-
none: false
|
124
154
|
requirements:
|
125
|
-
- -
|
155
|
+
- - '>='
|
126
156
|
- !ruby/object:Gem::Version
|
127
157
|
version: '0'
|
128
158
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
|
-
none: false
|
130
159
|
requirements:
|
131
|
-
- -
|
160
|
+
- - '>='
|
132
161
|
- !ruby/object:Gem::Version
|
133
162
|
version: '0'
|
134
163
|
requirements: []
|
135
164
|
rubyforge_project:
|
136
|
-
rubygems_version:
|
165
|
+
rubygems_version: 2.0.3
|
137
166
|
signing_key:
|
138
|
-
specification_version:
|
167
|
+
specification_version: 4
|
139
168
|
summary: Naught is a toolkit for building Null Objects
|
140
169
|
test_files:
|
170
|
+
- spec/base_object_spec.rb
|
171
|
+
- spec/basic_null_object_spec.rb
|
172
|
+
- spec/blackhole_spec.rb
|
173
|
+
- spec/conversions_spec.rb
|
174
|
+
- spec/functions/actual_spec.rb
|
175
|
+
- spec/functions/just_spec.rb
|
176
|
+
- spec/functions/maybe_spec.rb
|
177
|
+
- spec/functions/null_spec.rb
|
178
|
+
- spec/implicit_conversions_spec.rb
|
179
|
+
- spec/mimic_spec.rb
|
180
|
+
- spec/naught/null_object_builder/command_spec.rb
|
181
|
+
- spec/naught/null_object_builder_spec.rb
|
141
182
|
- spec/naught_spec.rb
|
183
|
+
- spec/pebble_spec.rb
|
184
|
+
- spec/predicate_spec.rb
|
185
|
+
- spec/singleton_null_object_spec.rb
|
142
186
|
- spec/spec_helper.rb
|
143
|
-
|
187
|
+
- spec/support/convertable_null.rb
|
data/.rspec
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
-fs --color --order rand
|
data/README.org
DELETED
@@ -1,340 +0,0 @@
|
|
1
|
-
#+TITLE: Naught: A Ruby Null Object Library
|
2
|
-
|
3
|
-
* A quick intro to Naught
|
4
|
-
|
5
|
-
*What's all this now then?*
|
6
|
-
|
7
|
-
Naught is a toolkit for building [[http://en.wikipedia.org/wiki/Null_Object_pattern][Null Objects]] in Ruby.
|
8
|
-
|
9
|
-
*What's that supposed to mean?*
|
10
|
-
|
11
|
-
Null Objects can make your code more [[http://confidentruby.com][confident]].
|
12
|
-
|
13
|
-
Here's a method that's not very sure of itself.
|
14
|
-
|
15
|
-
#+BEGIN_SRC ruby
|
16
|
-
class Geordi
|
17
|
-
def make_it_so(logger=nil)
|
18
|
-
logger && logger.info "Reversing the flux phase capacitance!"
|
19
|
-
logger && logger.info "Bounding a tachyon particle beam off of Data's cat!"
|
20
|
-
logger && logger.warn "Warning, bogon levels are rising!"
|
21
|
-
end
|
22
|
-
end
|
23
|
-
#+END_SRC
|
24
|
-
|
25
|
-
Now, observe as we give it a dash of confidence with the Null Object
|
26
|
-
pattern!
|
27
|
-
|
28
|
-
#+BEGIN_SRC ruby
|
29
|
-
class NullLogger
|
30
|
-
def debug(*); end
|
31
|
-
def info(*); end
|
32
|
-
def warn(*); end
|
33
|
-
def error(*); end
|
34
|
-
def fatal(*); end
|
35
|
-
end
|
36
|
-
|
37
|
-
class Geordi
|
38
|
-
def make_it_so(logger=NullLogger.new)
|
39
|
-
logger.info "Reversing the flux phase capacitance!"
|
40
|
-
logger.info "Bounding a tachyon particle beam off of Data's cat!"
|
41
|
-
logger.warn "Warning, bogon levels are rising!"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
#+END_SRC
|
45
|
-
|
46
|
-
By providing a =NullLogger= which implements [some of] the =Logger=
|
47
|
-
interface as no-op methods, we've gotten rid of those unsightly =&&=
|
48
|
-
operators.
|
49
|
-
|
50
|
-
*That was simple enough. Why do I need a library for it?*
|
51
|
-
|
52
|
-
You don't! The Null Object pattern is a very simple one at its core.
|
53
|
-
|
54
|
-
*And yet here we are...*
|
55
|
-
|
56
|
-
Yes. While you don't /need/ a Null Object library, this one offers
|
57
|
-
some conveniences you probably won't find elsewhere.
|
58
|
-
|
59
|
-
But there's an even more important reason I wrote this library. In
|
60
|
-
the immortal last words of James T. Kirk: "It was... /fun!/"
|
61
|
-
|
62
|
-
*OK, so how do I use this thing?*
|
63
|
-
|
64
|
-
Well, what would you like to do?
|
65
|
-
|
66
|
-
*I dunno, gimme an object that responds to any message with nil*
|
67
|
-
|
68
|
-
Sure thing!
|
69
|
-
|
70
|
-
#+BEGIN_SRC ruby
|
71
|
-
require 'naught'
|
72
|
-
|
73
|
-
NullObject = Naught.build
|
74
|
-
|
75
|
-
null = NullObject.new
|
76
|
-
null.foo # => nil
|
77
|
-
null.bar # => nil
|
78
|
-
#+END_SRC
|
79
|
-
|
80
|
-
*That was... weird. What's with this "build" business?*
|
81
|
-
|
82
|
-
Naught is a /toolkit/ for building null object classes. It is not a
|
83
|
-
one-size-fits-all solution.
|
84
|
-
|
85
|
-
What else can I make for you?
|
86
|
-
|
87
|
-
*How about a "black hole" null object that supports infinite chaining
|
88
|
-
of methods?*
|
89
|
-
|
90
|
-
OK.
|
91
|
-
|
92
|
-
#+BEGIN_SRC ruby
|
93
|
-
require 'naught'
|
94
|
-
|
95
|
-
BlackHole = Naught.build do |b|
|
96
|
-
b.black_hole
|
97
|
-
end
|
98
|
-
|
99
|
-
null = BlackHole.new
|
100
|
-
null.foo # => <null>
|
101
|
-
null.foo.bar.baz # => <null>
|
102
|
-
null << "hello" << "world" # => <null>
|
103
|
-
#+END_SRC
|
104
|
-
|
105
|
-
*What's that "b" thing?*
|
106
|
-
|
107
|
-
That stands for "builder". Naught uses the [[http://en.wikipedia.org/wiki/Builder_pattern][Builder Pattern]] for
|
108
|
-
rolling custom null object classes.
|
109
|
-
|
110
|
-
*Whatever. What if I want a null object that has conversions to Integer, String, etc. using sensible conversions to "zero values"?*
|
111
|
-
|
112
|
-
We can do that.
|
113
|
-
|
114
|
-
#+BEGIN_SRC ruby
|
115
|
-
require 'naught'
|
116
|
-
|
117
|
-
NullObject = Naught.build do |b|
|
118
|
-
b.define_explicit_conversions
|
119
|
-
end
|
120
|
-
|
121
|
-
null = NullObject.new
|
122
|
-
|
123
|
-
null.to_s # => ""
|
124
|
-
null.to_i # => 0
|
125
|
-
null.to_f # => 0.0
|
126
|
-
null.to_a # => []
|
127
|
-
null.to_h # => {}
|
128
|
-
null.to_c # => (0+0i)
|
129
|
-
null.to_r # => (0/1)
|
130
|
-
#+END_SRC
|
131
|
-
|
132
|
-
*Ah, but what about implicit conversions such as #to_str? Like what if I want a null object that implicitly splats the same way as an
|
133
|
-
empty array?*
|
134
|
-
|
135
|
-
Gotcha covered.
|
136
|
-
|
137
|
-
#+BEGIN_SRC ruby
|
138
|
-
require 'naught'
|
139
|
-
|
140
|
-
NullObject = Naught.build do |b|
|
141
|
-
b.define_implicit_conversions
|
142
|
-
end
|
143
|
-
|
144
|
-
null = NullObject.new
|
145
|
-
|
146
|
-
null.to_str # => ""
|
147
|
-
null.to_ary # => []
|
148
|
-
|
149
|
-
a, b, c = []
|
150
|
-
a # => nil
|
151
|
-
b # => nil
|
152
|
-
c # => nil
|
153
|
-
x, y, z = null
|
154
|
-
x # => nil
|
155
|
-
y # => nil
|
156
|
-
z # => nil
|
157
|
-
#+END_SRC
|
158
|
-
|
159
|
-
*How about a null object that only stubs out the methods from a specific class?*
|
160
|
-
|
161
|
-
That's what =mimic= is for.
|
162
|
-
|
163
|
-
#+BEGIN_SRC ruby
|
164
|
-
require 'naught'
|
165
|
-
|
166
|
-
NullIO = Naught.build do |b|
|
167
|
-
b.mimic IO
|
168
|
-
end
|
169
|
-
|
170
|
-
null_io = NullIO.new
|
171
|
-
|
172
|
-
null_io << "foo" # => nil
|
173
|
-
null_io.readline # => nil
|
174
|
-
null_io.foobar # =>
|
175
|
-
# ~> -:11:in `<main>': undefined method `foobar' for
|
176
|
-
# <null:IO>:NullIO (NoMethodError)
|
177
|
-
#+END_SRC
|
178
|
-
|
179
|
-
There is also =impersonate= which takes =mimic= one step further. The
|
180
|
-
generated null class will be derived from the impersonated class.
|
181
|
-
This is handy when refitting legacy code that contains type checks.
|
182
|
-
|
183
|
-
#+BEGIN_SRC ruby
|
184
|
-
require 'naught'
|
185
|
-
|
186
|
-
NullIO = Naught.build do |b|
|
187
|
-
b.impersonate IO
|
188
|
-
end
|
189
|
-
|
190
|
-
null_io = NullIO.new
|
191
|
-
IO === null_io # => true
|
192
|
-
|
193
|
-
case null_io
|
194
|
-
when IO
|
195
|
-
puts "Yep, checks out!"
|
196
|
-
null_io << "some output"
|
197
|
-
else
|
198
|
-
raise "Hey, I expected an IO!"
|
199
|
-
end
|
200
|
-
# >> Yep, checks out!
|
201
|
-
#+END_SRC
|
202
|
-
|
203
|
-
*Alright smartypants. What if I want to add my own methods?*
|
204
|
-
|
205
|
-
Not a problem, just define them in the =.build= block.
|
206
|
-
|
207
|
-
#+BEGIN_SRC ruby
|
208
|
-
require 'naught'
|
209
|
-
|
210
|
-
NullObject = Naught.build do |b|
|
211
|
-
b.define_explicit_conversions
|
212
|
-
def to_s
|
213
|
-
"NOTHING TO SEE HERE MOVE ALONG"
|
214
|
-
end
|
215
|
-
|
216
|
-
def to_path
|
217
|
-
"/dev/null"
|
218
|
-
end
|
219
|
-
end
|
220
|
-
|
221
|
-
null = NullObject.new
|
222
|
-
null.to_s # => "NOTHING TO SEE HERE MOVE ALONG"
|
223
|
-
null.to_path # => "/dev/null"
|
224
|
-
#+END_SRC
|
225
|
-
|
226
|
-
*Got anything else up your sleeve?*
|
227
|
-
|
228
|
-
Well, we can make the null class a singleton, since null objects
|
229
|
-
generally have no state.
|
230
|
-
|
231
|
-
#+BEGIN_SRC ruby
|
232
|
-
require 'naught'
|
233
|
-
|
234
|
-
NullObject = Naught.build do |b|
|
235
|
-
b.singleton
|
236
|
-
end
|
237
|
-
|
238
|
-
null = NullObject.instance
|
239
|
-
|
240
|
-
null.__id__ # => 17844080
|
241
|
-
NullObject.instance.__id__ # => 17844080
|
242
|
-
NullObject.new # =>
|
243
|
-
# ~> -:11:in `<main>': private method `new' called for
|
244
|
-
# NullObject:Class (NoMethodError)
|
245
|
-
#+END_SRC
|
246
|
-
|
247
|
-
Speaking of null objects with state, we can also enable tracing. This
|
248
|
-
is handy for playing "where'd that null come from?!" Try doing /that/
|
249
|
-
with =nil=!
|
250
|
-
|
251
|
-
#+BEGIN_SRC ruby
|
252
|
-
require 'naught'
|
253
|
-
|
254
|
-
NullObject = Naught.build do |b|
|
255
|
-
b.traceable
|
256
|
-
end
|
257
|
-
|
258
|
-
null = NullObject.new # line 7
|
259
|
-
|
260
|
-
null.__file__ # => "example.rb"
|
261
|
-
null.__line__ # => 7
|
262
|
-
#+END_SRC
|
263
|
-
|
264
|
-
We can even conditionally enable either singleton mode (for
|
265
|
-
production) or tracing (for development). Here's an example of using
|
266
|
-
the =$DEBUG= global variable (set with the =-d= option to ruby) to
|
267
|
-
choose which one.
|
268
|
-
|
269
|
-
#+BEGIN_SRC ruby
|
270
|
-
require 'naught'
|
271
|
-
|
272
|
-
NullObject = Naught.build do |b|
|
273
|
-
if $DEBUG
|
274
|
-
b.traceable
|
275
|
-
else
|
276
|
-
b.singleton
|
277
|
-
end
|
278
|
-
end
|
279
|
-
#+END_SRC
|
280
|
-
|
281
|
-
The only caveat is that when swapping between singleton and
|
282
|
-
non-singleton implementations, you should be careful to always
|
283
|
-
instantiate your null objects with =NullObject.get=, not =.new= or
|
284
|
-
=.instance=. =.get= will work whether the class is implemented as a
|
285
|
-
singleton or not.
|
286
|
-
|
287
|
-
#+BEGIN_SRC ruby
|
288
|
-
NullObject.get # => <null>
|
289
|
-
#+END_SRC
|
290
|
-
|
291
|
-
*Are you done yet?*
|
292
|
-
|
293
|
-
Just one more thing. For maximum convenience, Naught-generated null
|
294
|
-
classes also come with a full suite of conversion functions which can
|
295
|
-
be included into your classes.
|
296
|
-
|
297
|
-
#+BEGIN_SRC ruby
|
298
|
-
require 'naught'
|
299
|
-
|
300
|
-
NullObject = Naught.build
|
301
|
-
|
302
|
-
include NullObject::Conversions
|
303
|
-
|
304
|
-
# Convert nil to null objects. Everything else passes through.
|
305
|
-
Maybe(42) # => 42
|
306
|
-
Maybe(nil) # => <null>
|
307
|
-
Maybe(NullObject.get) # => <null>
|
308
|
-
Maybe{ 42 } # => 42
|
309
|
-
|
310
|
-
# Insist on a non-null (or nil) value
|
311
|
-
Just(42) # => 42
|
312
|
-
Just(nil) rescue $! # => #<ArgumentError: Null value: nil>
|
313
|
-
Just(NullObject.get) rescue $! # => #<ArgumentError: Null value: <null>>
|
314
|
-
|
315
|
-
# nils and nulls become nulls. Everything else is rejected.
|
316
|
-
Null() # => <null>
|
317
|
-
Null(42) rescue $! # => #<ArgumentError: 42 is not null!>
|
318
|
-
Null(nil) # => <null>
|
319
|
-
Null(NullObject.get) # => <null>
|
320
|
-
|
321
|
-
# Convert nulls back to nils. Everything else passes throuhgh. Useful
|
322
|
-
# for preventing null objects from "leaking" into public API return
|
323
|
-
# values.
|
324
|
-
Actual(42) # => 42
|
325
|
-
Actual(nil) # => nil
|
326
|
-
Actual(NullObject.get) # => nil
|
327
|
-
Actual { 42 } # => 42
|
328
|
-
#+END_SRC
|
329
|
-
|
330
|
-
* Requirements
|
331
|
-
|
332
|
-
- Ruby 1.9
|
333
|
-
|
334
|
-
* Contributing
|
335
|
-
|
336
|
-
- Fork, branch, submit PR, blah blah blah. Don't forget tests.
|
337
|
-
|
338
|
-
* Who's responsible
|
339
|
-
|
340
|
-
Naught is by [[http://devblog.avdi.org/][Avdi Grimm]].
|