naught 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: