naught 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.travis.yml +2 -0
  4. data/Changelog.md +6 -0
  5. data/Gemfile +1 -0
  6. data/Guardfile +10 -0
  7. data/README.markdown +413 -0
  8. data/Rakefile +5 -0
  9. data/lib/naught.rb +1 -4
  10. data/lib/naught/null_class_builder.rb +37 -137
  11. data/lib/naught/null_class_builder/command.rb +20 -0
  12. data/lib/naught/null_class_builder/commands.rb +8 -0
  13. data/lib/naught/null_class_builder/commands/define_explicit_conversions.rb +13 -24
  14. data/lib/naught/null_class_builder/commands/define_implicit_conversions.rb +14 -0
  15. data/lib/naught/null_class_builder/commands/impersonate.rb +9 -0
  16. data/lib/naught/null_class_builder/commands/mimic.rb +40 -0
  17. data/lib/naught/null_class_builder/commands/pebble.rb +36 -0
  18. data/lib/naught/null_class_builder/commands/predicates_return.rb +47 -0
  19. data/lib/naught/null_class_builder/commands/singleton.rb +24 -0
  20. data/lib/naught/null_class_builder/commands/traceable.rb +19 -0
  21. data/lib/naught/null_class_builder/conversions_module.rb +57 -0
  22. data/lib/naught/version.rb +1 -1
  23. data/naught.gemspec +2 -1
  24. data/spec/base_object_spec.rb +47 -0
  25. data/spec/basic_null_object_spec.rb +35 -0
  26. data/spec/blackhole_spec.rb +16 -0
  27. data/spec/conversions_spec.rb +20 -0
  28. data/spec/functions/actual_spec.rb +22 -0
  29. data/spec/functions/just_spec.rb +22 -0
  30. data/spec/functions/maybe_spec.rb +35 -0
  31. data/spec/functions/null_spec.rb +34 -0
  32. data/spec/implicit_conversions_spec.rb +25 -0
  33. data/spec/mimic_spec.rb +122 -0
  34. data/spec/naught/null_object_builder/command_spec.rb +10 -0
  35. data/spec/naught/null_object_builder_spec.rb +31 -0
  36. data/spec/naught_spec.rb +77 -411
  37. data/spec/pebble_spec.rb +75 -0
  38. data/spec/predicate_spec.rb +80 -0
  39. data/spec/singleton_null_object_spec.rb +35 -0
  40. data/spec/spec_helper.rb +13 -1
  41. data/spec/support/convertable_null.rb +4 -0
  42. metadata +76 -32
  43. data/.rspec +0 -1
  44. data/README.org +0 -340
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ module Naught
4
+ describe NullClassBuilder::Command do
5
+ it 'is abstract' do
6
+ command = NullClassBuilder::Command.new(nil)
7
+ expect{command.call}.to raise_error(NotImplementedError)
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ module Naught
4
+ class NullClassBuilder
5
+ module Commands
6
+ class TestCommand
7
+ end
8
+ end
9
+ end
10
+
11
+ describe NullClassBuilder do
12
+ subject(:builder) { NullClassBuilder.new }
13
+ it 'responds to commands defined in NullObjectBuilder::Commands' do
14
+ expect(builder).to respond_to(:test_command)
15
+ end
16
+
17
+ it 'translates method calls into command invocations including arguments' do
18
+ test_command = double
19
+ NullClassBuilder::Commands::TestCommand.should_receive(:new).
20
+ with(builder, "foo", 42).
21
+ and_return(test_command)
22
+ test_command.should_receive(:call).and_return("COMMAND RESULT")
23
+ expect(builder.test_command("foo", 42)).to eq("COMMAND RESULT")
24
+ end
25
+
26
+ it 'handles missing non-command missing methods normally' do
27
+ expect(builder).not_to respond_to(:nonexistant_method)
28
+ expect{builder.nonexistent_method}.to raise_error(NoMethodError)
29
+ end
30
+ end
31
+ end
@@ -1,429 +1,95 @@
1
1
  require 'spec_helper'
2
- require 'naught'
3
2
 
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
3
+ describe 'null object impersonating another type' do
4
+ class Point
5
+ def x; 23; end
6
+ def y; 42; end
33
7
  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({})
8
+
9
+ subject(:null) { impersonation_class.new }
10
+ let(:impersonation_class) {
11
+ Naught.build do |b|
12
+ b.impersonate Point
50
13
  end
14
+ }
15
+
16
+ it 'matches the impersonated type' do
17
+ expect(Point).to be === null
51
18
  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
-
19
+
20
+ it 'responds to methods from the impersonated type' do
21
+ expect(null.x).to be_nil
22
+ expect(null.y).to be_nil
74
23
  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
24
+
25
+ it 'does not respond to unknown methods' do
26
+ expect{null.foo}.to raise_error(NoMethodError)
107
27
  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
28
+ end
29
+
30
+ describe 'traceable null object' do
31
+ subject(:trace_null) {
32
+ null_object_and_line.first
33
+ }
34
+ let(:null_object_and_line) {
35
+ obj = trace_null_class.new; line = __LINE__;
36
+ [obj, line]
37
+ }
38
+ let(:instantiation_line) { null_object_and_line.last }
39
+ let(:trace_null_class) {
40
+ Naught.build do |b|
41
+ b.traceable
42
+ end
43
+ }
44
+
45
+ it 'remembers the file it was instantiated from' do
46
+ expect(trace_null.__file__).to eq(__FILE__)
121
47
  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
48
+
49
+ it 'remembers the line it was instantiated from' do
50
+ expect(trace_null.__line__).to eq(instantiation_line)
193
51
  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
52
+
53
+ def make_null
54
+ trace_null_class.get(caller: caller(1))
228
55
  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
56
+
57
+ it 'can accept custom backtrace info' do
58
+ obj = make_null; line = __LINE__
59
+ expect(obj.__line__).to eq(line)
254
60
  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
61
+ end
62
+
63
+ describe 'customized null object' do
64
+ subject(:custom_null) { custom_null_class.new }
65
+ let(:custom_null_class) {
66
+ Naught.build do |b|
67
+ b.define_explicit_conversions
68
+ def to_path
69
+ "/dev/null"
267
70
  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
71
+ def to_s
72
+ "NOTHING TO SEE HERE"
297
73
  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
74
  end
75
+ }
76
+
77
+ it 'responds to custom-defined methods' do
78
+ expect(custom_null.to_path).to eq("/dev/null")
388
79
  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
80
+
81
+ it 'allows generated methods to be overridden' do
82
+ expect(custom_null.to_s).to eq("NOTHING TO SEE HERE")
408
83
  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
84
+ end
85
+ TestNull = Naught.build
86
+
87
+ describe 'a named null object class' do
88
+ it 'has named ancestor modules' do
89
+ expect(TestNull.ancestors[0..2].map(&:name)).to eq([
90
+ 'TestNull',
91
+ 'TestNull::Customizations',
92
+ 'TestNull::GeneratedMethods'
93
+ ])
428
94
  end
429
95
  end