naught 0.0.1

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.
@@ -0,0 +1,429 @@
1
+ require 'spec_helper'
2
+ require 'naught'
3
+
4
+ module Naught
5
+ describe 'basic null object' do
6
+ subject(:null) { null_class.new }
7
+ let(:null_class) {
8
+ Naught.build
9
+ }
10
+ it 'responds to arbitrary messages and returns nil' do
11
+ expect(null.info).to be_nil
12
+ expect(null.foobaz).to be_nil
13
+ expect(null.to_s).to be_nil
14
+ end
15
+
16
+ it 'accepts any arguments for any messages' do
17
+ null.foobaz(1,2,3)
18
+ end
19
+ it 'reports that it responds to any message' do
20
+ expect(null).to respond_to(:info)
21
+ expect(null).to respond_to(:foobaz)
22
+ expect(null).to respond_to(:to_s)
23
+ end
24
+ it 'can be inspected' do
25
+ expect(null.inspect).to eq("<null>")
26
+ end
27
+ it 'knows its own class' do
28
+ expect(null.class).to eq(null_class)
29
+ end
30
+ it 'aliases .new to .get' do
31
+ expect(null_class.get.class).to be(null_class)
32
+ end
33
+ end
34
+ describe 'explicitly convertable null object' do
35
+ subject(:null) { null_class.new }
36
+ let(:null_class) {
37
+ Naught.build do |b|
38
+ b.define_explicit_conversions
39
+ end
40
+ }
41
+
42
+ it "defines common explicit conversions to return zero values" do
43
+ expect(null.to_s).to eq("")
44
+ expect(null.to_a).to eq([])
45
+ expect(null.to_i).to eq(0)
46
+ expect(null.to_f).to eq(0.0)
47
+ expect(null.to_c).to eq(Complex(0))
48
+ expect(null.to_r).to eq(Rational(0))
49
+ expect(null.to_h).to eq({})
50
+ end
51
+ end
52
+ describe 'implicitly convertable null object' do
53
+ subject(:null) { null_class.new }
54
+ let(:null_class) {
55
+ Naught.build do |b|
56
+ b.define_implicit_conversions
57
+ end
58
+ }
59
+ it 'implicitly splats the same way an empty array does' do
60
+ a, b = null
61
+ expect(a).to be_nil
62
+ expect(b).to be_nil
63
+ end
64
+ it 'is implicitly convertable to String' do
65
+ expect(eval(null)).to be_nil
66
+ end
67
+ it 'implicitly converts to an empty array' do
68
+ expect(null.to_ary).to eq([])
69
+ end
70
+ it 'implicitly converts to an empty string' do
71
+ expect(null.to_str).to eq("")
72
+ end
73
+
74
+ end
75
+ describe 'singleton null object' do
76
+ subject(:null_class) {
77
+ Naught.build do |b|
78
+ b.singleton
79
+ end
80
+ }
81
+
82
+ it 'does not respond to .new' do
83
+ expect{ null_class.new }.to raise_error
84
+ end
85
+
86
+ it 'has only one instance' do
87
+ null1 = null_class.instance
88
+ null2 = null_class.instance
89
+ expect(null1).to be(null2)
90
+ end
91
+
92
+ it 'can be cloned' do
93
+ null = null_class.instance
94
+ expect(null.clone).to be_nil
95
+ end
96
+
97
+ it 'can be duplicated' do
98
+ null = null_class.instance
99
+ expect(null.dup).to be_nil
100
+ end
101
+ it 'aliases .instance to .get' do
102
+ expect(null_class.get).to be null_class.instance
103
+ end
104
+ it 'permits arbitrary arguments to be passed to .get' do
105
+ null_class.get(42, foo: "bar")
106
+ end
107
+ end
108
+ describe 'black hole null object' do
109
+ subject(:null) { null_class.new }
110
+ let(:null_class) {
111
+ Naught.build do |b|
112
+ b.black_hole
113
+ end
114
+ }
115
+
116
+ it 'returns self from arbitray method calls' do
117
+ expect(null.info).to be(null)
118
+ expect(null.foobaz).to be(null)
119
+ expect(null << "bar").to be(null)
120
+ end
121
+ end
122
+ describe 'null object mimicking a class' do
123
+ class User
124
+ def login
125
+ "bob"
126
+ end
127
+ end
128
+
129
+ module Authorizable
130
+ def authorized_for?(object)
131
+ true
132
+ end
133
+ end
134
+
135
+ class LibraryPatron < User
136
+ include Authorizable
137
+
138
+ def member?; true; end
139
+ def name; "Bob"; end
140
+ def notify_of_overdue_books(titles)
141
+ puts "Notifying Bob his books are overdue..."
142
+ end
143
+ end
144
+
145
+ subject(:null) { mimic_class.new }
146
+ let(:mimic_class) {
147
+ Naught.build do |b|
148
+ b.mimic LibraryPatron
149
+ end
150
+ }
151
+ it 'responds to all methods defined on the target class' do
152
+ expect(null.member?).to be_nil
153
+ expect(null.name).to be_nil
154
+ expect(null.notify_of_overdue_books(['The Grapes of Wrath'])).to be_nil
155
+ end
156
+
157
+ it 'does not respond to methods not defined on the target class' do
158
+ expect{null.foobar}.to raise_error(NoMethodError)
159
+ end
160
+
161
+ it 'reports which messages it does and does not respond to' do
162
+ expect(null).to respond_to(:member?)
163
+ expect(null).to respond_to(:name)
164
+ expect(null).to respond_to(:notify_of_overdue_books)
165
+ expect(null).not_to respond_to(:foobar)
166
+ end
167
+ it 'has an informative inspect string' do
168
+ expect(null.inspect).to eq("<null:Naught::LibraryPatron>")
169
+ end
170
+
171
+ it 'excludes Object methods from being mimicked' do
172
+ expect(null.object_id).not_to be_nil
173
+ expect(null.hash).not_to be_nil
174
+ end
175
+
176
+ it 'includes inherited methods' do
177
+ expect(null.authorized_for?('something')).to be_nil
178
+ expect(null.login).to be_nil
179
+ end
180
+
181
+ describe 'with include_super: false' do
182
+ let(:mimic_class) {
183
+ Naught.build do |b|
184
+ b.mimic LibraryPatron, include_super: false
185
+ end
186
+ }
187
+
188
+ it 'excludes inherited methods' do
189
+ expect(null).to_not respond_to(:authorized_for?)
190
+ expect(null).to_not respond_to(:login)
191
+ end
192
+ end
193
+ end
194
+ describe 'using mimic with black_hole' do
195
+ require 'logger'
196
+ subject(:null) { mimic_class.new }
197
+ let(:mimic_class) {
198
+ Naught.build do |b|
199
+ b.mimic Logger
200
+ b.black_hole
201
+ end
202
+ }
203
+
204
+ def self.it_behaves_like_a_black_hole_mimic
205
+ it 'returns self from mimicked methods' do
206
+ expect(null.info).to equal(null)
207
+ expect(null.error).to equal(null)
208
+ expect(null << "test").to equal(null)
209
+ end
210
+
211
+ it 'does not respond to methods not defined on the target class' do
212
+ expect{null.foobar}.to raise_error(NoMethodError)
213
+ end
214
+ end
215
+
216
+ it_behaves_like_a_black_hole_mimic
217
+
218
+ describe '(reverse order)' do
219
+ let(:mimic_class) {
220
+ Naught.build do |b|
221
+ b.black_hole
222
+ b.mimic Logger
223
+ end
224
+ }
225
+
226
+ it_behaves_like_a_black_hole_mimic
227
+ end
228
+ end
229
+ describe 'null object impersonating another type' do
230
+ class Point
231
+ def x; 23; end
232
+ def y; 42; end
233
+ end
234
+
235
+ subject(:null) { impersonation_class.new }
236
+ let(:impersonation_class) {
237
+ Naught.build do |b|
238
+ b.impersonate Point
239
+ end
240
+ }
241
+
242
+ it 'matches the impersonated type' do
243
+ expect(Point).to be === null
244
+ end
245
+
246
+ it 'responds to methods from the impersonated type' do
247
+ expect(null.x).to be_nil
248
+ expect(null.y).to be_nil
249
+ end
250
+
251
+ it 'does not respond to unknown methods' do
252
+ expect{null.foo}.to raise_error(NoMethodError)
253
+ end
254
+ end
255
+ describe 'traceable null object' do
256
+ subject(:trace_null) {
257
+ null_object_and_line.first
258
+ }
259
+ let(:null_object_and_line) {
260
+ obj = trace_null_class.new; line = __LINE__;
261
+ [obj, line]
262
+ }
263
+ let(:instantiation_line) { null_object_and_line.last }
264
+ let(:trace_null_class) {
265
+ Naught.build do |b|
266
+ b.traceable
267
+ end
268
+ }
269
+
270
+ it 'remembers the file it was instantiated from' do
271
+ expect(trace_null.__file__).to eq(__FILE__)
272
+ end
273
+
274
+ it 'remembers the line it was instantiated from' do
275
+ expect(trace_null.__line__).to eq(instantiation_line)
276
+ end
277
+ def make_null
278
+ trace_null_class.get(caller: caller(1))
279
+ end
280
+
281
+ it 'can accept custom backtrace info' do
282
+ obj = make_null; line = __LINE__
283
+ expect(obj.__line__).to eq(line)
284
+ end
285
+ end
286
+ describe 'customized null object' do
287
+ subject(:custom_null) { custom_null_class.new }
288
+ let(:custom_null_class) {
289
+ Naught.build do |b|
290
+ b.define_explicit_conversions
291
+ def to_path
292
+ "/dev/null"
293
+ end
294
+ def to_s
295
+ "NOTHING TO SEE HERE"
296
+ end
297
+ end
298
+ }
299
+
300
+ it 'responds to custom-defined methods' do
301
+ expect(custom_null.to_path).to eq("/dev/null")
302
+ end
303
+
304
+ it 'allows generated methods to be overridden' do
305
+ expect(custom_null.to_s).to eq("NOTHING TO SEE HERE")
306
+ end
307
+ end
308
+ TestNull = Naught.build
309
+
310
+ describe 'a named null object class' do
311
+ it 'has named ancestor modules' do
312
+ expect(TestNull.ancestors[0..2].map(&:name)).to eq([
313
+ 'Naught::TestNull',
314
+ 'Naught::TestNull::Customizations',
315
+ 'Naught::TestNull::GeneratedMethods'
316
+ ])
317
+ end
318
+ end
319
+ ConvertableNull = Naught.build do |b|
320
+ b.null_equivalents << ""
321
+ b.traceable
322
+ end
323
+
324
+ describe 'Null()' do
325
+ include ConvertableNull::Conversions
326
+
327
+ specify 'given no input, returns a null object' do
328
+ expect(Null().class).to be(ConvertableNull)
329
+ end
330
+
331
+ specify 'given nil, returns a null object' do
332
+ expect(Null(nil).class).to be(ConvertableNull)
333
+ end
334
+
335
+ specify 'given a null object, returns the same null object' do
336
+ null = ConvertableNull.get
337
+ expect(Null(null)).to be(null)
338
+ end
339
+
340
+ specify 'given anything in null_equivalents, return a null object' do
341
+ expect(Null("").class).to be(ConvertableNull)
342
+ end
343
+
344
+ specify 'given anything else, raises an ArgumentError' do
345
+ expect{Null(false)}.to raise_error(ArgumentError)
346
+ expect{Null("hello")}.to raise_error(ArgumentError)
347
+ end
348
+
349
+ it 'generates null objects with useful trace info' do
350
+ null = Null(); line = __LINE__
351
+ expect(null.__line__).to eq(line)
352
+ expect(null.__file__).to eq(__FILE__)
353
+ end
354
+
355
+ end
356
+ describe 'Maybe()' do
357
+ include ConvertableNull::Conversions
358
+
359
+ specify 'given nil, returns a null object' do
360
+ expect(Maybe(nil).class).to be(ConvertableNull)
361
+ end
362
+
363
+ specify 'given a null object, returns the same null object' do
364
+ null = ConvertableNull.get
365
+ expect(Maybe(null)).to be(null)
366
+ end
367
+
368
+ specify 'given anything in null_equivalents, return a null object' do
369
+ expect(Maybe("").class).to be(ConvertableNull)
370
+ end
371
+
372
+ specify 'given anything else, returns the input unchanged' do
373
+ expect(Maybe(false)).to be(false)
374
+ str = "hello"
375
+ expect(Maybe(str)).to be(str)
376
+ end
377
+
378
+ it 'generates null objects with useful trace info' do
379
+ null = Maybe(); line = __LINE__
380
+ expect(null.__line__).to eq(line)
381
+ expect(null.__file__).to eq(__FILE__)
382
+ end
383
+
384
+ it 'also works with blocks' do
385
+ expect(Maybe{nil}.class).to eq(ConvertableNull)
386
+ expect(Maybe{"foo"}).to eq("foo")
387
+ end
388
+ end
389
+ describe 'Just()' do
390
+ include ConvertableNull::Conversions
391
+
392
+ specify 'passes non-nullish values through' do
393
+ expect(Just(false)).to be(false)
394
+ str = "hello"
395
+ expect(Just(str)).to be(str)
396
+ end
397
+
398
+ specify 'rejects nullish values' do
399
+ expect{Just(nil)}.to raise_error(ArgumentError)
400
+ expect{Just("")}.to raise_error(ArgumentError)
401
+ expect{Just(ConvertableNull.get)}.to raise_error(ArgumentError)
402
+ end
403
+
404
+ it 'also works with blocks' do
405
+ expect{Just{nil}.class}.to raise_error(ArgumentError)
406
+ expect(Just{"foo"}).to eq("foo")
407
+ end
408
+ end
409
+ describe 'Actual()' do
410
+ include ConvertableNull::Conversions
411
+
412
+ specify 'given a null object, returns nil' do
413
+ null = ConvertableNull.get
414
+ expect(Actual(null)).to be_nil
415
+ end
416
+
417
+ specify 'given anything else, returns the input unchanged' do
418
+ expect(Actual(false)).to be(false)
419
+ str = "hello"
420
+ expect(Actual(str)).to be(str)
421
+ expect(Actual(nil)).to be_nil
422
+ end
423
+
424
+ it 'also works with blocks' do
425
+ expect(Actual{ConvertableNull.new}).to be_nil
426
+ expect(Actual{"foo"}).to eq("foo")
427
+ end
428
+ end
429
+ end
@@ -0,0 +1 @@
1
+ $LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
metadata ADDED
@@ -0,0 +1,161 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: naught
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Avdi Grimm
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-05-29 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.3'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.3'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: guard
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: guard-rspec
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: Naught is a toolkit for building Null Objects
95
+ email:
96
+ - avdi@avdi.org
97
+ executables:
98
+ - autospec
99
+ - coderay
100
+ - guard
101
+ - htmldiff
102
+ - ldiff
103
+ - pry
104
+ - rake
105
+ - rspec
106
+ - thor
107
+ extensions: []
108
+ extra_rdoc_files: []
109
+ files:
110
+ - .gitignore
111
+ - .rspec
112
+ - Gemfile
113
+ - Guardfile
114
+ - LICENSE.txt
115
+ - README.org
116
+ - Rakefile
117
+ - bin/autospec
118
+ - bin/coderay
119
+ - bin/guard
120
+ - bin/htmldiff
121
+ - bin/ldiff
122
+ - bin/pry
123
+ - bin/rake
124
+ - bin/rspec
125
+ - bin/thor
126
+ - lib/naught.rb
127
+ - lib/naught/null_class_builder.rb
128
+ - lib/naught/null_class_builder/commands/define_explicit_conversions.rb
129
+ - lib/naught/version.rb
130
+ - naught.gemspec
131
+ - spec/naught_spec.rb
132
+ - spec/spec_helper.rb
133
+ homepage: https://github.com/avdi/naught
134
+ licenses:
135
+ - MIT
136
+ post_install_message:
137
+ rdoc_options: []
138
+ require_paths:
139
+ - lib
140
+ required_ruby_version: !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ! '>='
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ required_rubygems_version: !ruby/object:Gem::Requirement
147
+ none: false
148
+ requirements:
149
+ - - ! '>='
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ requirements: []
153
+ rubyforge_project:
154
+ rubygems_version: 1.8.24
155
+ signing_key:
156
+ specification_version: 3
157
+ summary: Naught is a toolkit for building Null Objects
158
+ test_files:
159
+ - spec/naught_spec.rb
160
+ - spec/spec_helper.rb
161
+ has_rdoc: