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.
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,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 590655f0e3a1d6ace3edabc144bab9b497512816
4
+ data.tar.gz: 88d5907481c580e2a725f1078ac84029579a1b8e
5
+ SHA512:
6
+ metadata.gz: fb7129d4cef2b7aa3440033c81abf745c094316e44b0ddee3642b73c6208f2f2411d29cce030ba183baa676001204048dcc9510347bfff7aa4c28e4c77dadca5
7
+ data.tar.gz: 0552239f6f101b7d21e4c227e34bc4feb17c510c8b57693342934064037de01f6a4c7bfe48eb9eaa9875a8f4ac00fea2caa1a73c0c0788441b416130110057da
data/.gitignore CHANGED
@@ -17,3 +17,7 @@ test/version_tmp
17
17
  tmp
18
18
  /naught.org
19
19
  /naught.html
20
+ /bin
21
+ /TAGS
22
+ /gems.tags
23
+ /tags
@@ -0,0 +1,2 @@
1
+ env:
2
+ - TRAVIS=true
@@ -0,0 +1,6 @@
1
+ ## 0.0.3
2
+
3
+ Features:
4
+
5
+ - New "pebble" mode
6
+
data/Gemfile CHANGED
@@ -5,4 +5,5 @@ gemspec
5
5
 
6
6
  group :test do
7
7
  gem "libnotify"
8
+ gem 'coveralls', require: false
8
9
  end
data/Guardfile CHANGED
@@ -1,5 +1,15 @@
1
+ guard 'bundler' do
2
+ watch('Gemfile')
3
+ watch(/^.+\.gemspec/)
4
+ end
5
+
1
6
  guard :rspec, cli: '-fs --color --order rand' do
2
7
  watch(%r{^spec/.+_spec\.rb$})
3
8
  watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
9
  watch('spec/spec_helper.rb') { "spec" }
5
10
  end
11
+
12
+ guard 'ctags-bundler', emacs: true, src_path: ["lib", "spec/support"] do
13
+ watch(/^(lib|spec\/support)\/.*\.rb$/)
14
+ watch('Gemfile.lock')
15
+ end
@@ -0,0 +1,413 @@
1
+ [![Build Status](https://travis-ci.org/avdi/naught.png?branch=master)](https://travis-ci.org/avdi/naught)
2
+ [![Code Climate](https://codeclimate.com/github/avdi/naught.png)](https://codeclimate.com/github/avdi/naught)
3
+ [![Coverage Status](https://coveralls.io/repos/avdi/naught/badge.png?branch=master)](https://coveralls.io/r/avdi/naught?branch=master)
4
+ [![Gem Version](https://badge.fury.io/rb/naught.png)](http://badge.fury.io/rb/naught)
5
+
6
+ A quick intro to Naught
7
+ -------------------------
8
+
9
+ #### What's all this now then?
10
+
11
+ Naught is a toolkit for building [Null
12
+ Objects](http://en.wikipedia.org/wiki/Null_Object_pattern) in Ruby.
13
+
14
+ #### What's that supposed to mean?
15
+
16
+ Null Objects can make your code more
17
+ [confident](http://confidentruby.com).
18
+
19
+ Here's a method that's not very sure of itself.
20
+
21
+ ```ruby
22
+ class Geordi
23
+ def make_it_so(logger=nil)
24
+ logger && logger.info("Reversing the flux phase capacitance!")
25
+ logger && logger.info("Bounding a tachyon particle beam off of Data's cat!")
26
+ logger && logger.warn("Warning, bogon levels are rising!")
27
+ end
28
+ end
29
+ ```
30
+
31
+ Now, observe as we give it a dash of confidence with the Null Object
32
+ pattern!
33
+
34
+ ```ruby
35
+ class NullLogger
36
+ def debug(*); end
37
+ def info(*); end
38
+ def warn(*); end
39
+ def error(*); end
40
+ def fatal(*); end
41
+ end
42
+
43
+ class Geordi
44
+ def make_it_so(logger=NullLogger.new)
45
+ logger.info "Reversing the flux phase capacitance!"
46
+ logger.info "Bounding a tachyon particle beam off of Data's cat!"
47
+ logger.warn "Warning, bogon levels are rising!"
48
+ end
49
+ end
50
+ ```
51
+
52
+ By providing a `NullLogger` which implements [some of] the `Logger`
53
+ interface as no-op methods, we've gotten rid of those unsightly `&&`
54
+ operators.
55
+
56
+ #### That was simple enough. Why do I need a library for it?
57
+
58
+ You don't! The Null Object pattern is a very simple one at its core.
59
+
60
+ #### And yet here we are…
61
+
62
+ Yes. While you don't *need* a Null Object library, this one offers some
63
+ conveniences you probably won't find elsewhere.
64
+
65
+ But there's an even more important reason I wrote this library. In the
66
+ immortal last words of James T. Kirk: "It was… *fun!*"
67
+
68
+ #### OK, so how do I use this thing?
69
+
70
+ Well, what would you like to do?
71
+
72
+ #### I dunno, gimme an object that responds to any message with nil
73
+
74
+ Sure thing!
75
+
76
+ ```ruby
77
+ require 'naught'
78
+
79
+ NullObject = Naught.build
80
+
81
+ null = NullObject.new
82
+ null.foo # => nil
83
+ null.bar # => nil
84
+ ```
85
+
86
+ #### That was… weird. What's with this "build" business?
87
+
88
+ Naught is a *toolkit* for building null object classes. It is not a
89
+ one-size-fits-all solution.
90
+
91
+ What else can I make for you?
92
+
93
+ #### How about a "black hole" null object that supports infinite chaining of methods?
94
+
95
+ OK.
96
+
97
+ ```ruby
98
+ require 'naught'
99
+
100
+ BlackHole = Naught.build do |config|
101
+ config.black_hole
102
+ end
103
+
104
+ null = BlackHole.new
105
+ null.foo # => <null>
106
+ null.foo.bar.baz # => <null>
107
+ null << "hello" << "world" # => <null>
108
+ ```
109
+
110
+ ### What's that "config" thing?
111
+
112
+ That's what you use to customize the generated class to your
113
+ liking. Internally, Naught uses the [Builder
114
+ Pattern](http://en.wikipedia.org/wiki/Builder_pattern) to make this work..
115
+
116
+ #### Whatever. What if I want a null object that has conversions to Integer, String, etc. using sensible conversions to "zero values"?
117
+
118
+ We can do that.
119
+
120
+ ```ruby
121
+ require 'naught'
122
+
123
+ NullObject = Naught.build do |config|
124
+ config.define_explicit_conversions
125
+ end
126
+
127
+ null = NullObject.new
128
+
129
+ null.to_s # => ""
130
+ null.to_i # => 0
131
+ null.to_f # => 0.0
132
+ null.to_a # => []
133
+ null.to_h # => {}
134
+ null.to_c # => (0+0i)
135
+ null.to_r # => (0/1)
136
+ ```
137
+
138
+ #### 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 empty array?
139
+
140
+ Gotcha covered.
141
+
142
+ ```ruby
143
+ require 'naught'
144
+
145
+ NullObject = Naught.build do |config|
146
+ config.define_implicit_conversions
147
+ end
148
+
149
+ null = NullObject.new
150
+
151
+ null.to_str # => ""
152
+ null.to_ary # => []
153
+
154
+ a, b, c = []
155
+ a # => nil
156
+ b # => nil
157
+ c # => nil
158
+ x, y, z = null
159
+ x # => nil
160
+ y # => nil
161
+ z # => nil
162
+ ```
163
+
164
+ #### How about a null object that only stubs out the methods from a specific class?
165
+
166
+ That's what `mimic` is for.
167
+
168
+ ```ruby
169
+ require 'naught'
170
+
171
+ NullIO = Naught.build do |config|
172
+ config.mimic IO
173
+ end
174
+
175
+ null_io = NullIO.new
176
+
177
+ null_io << "foo" # => nil
178
+ null_io.readline # => nil
179
+ null_io.foobar # =>
180
+ # ~> -:11:in `<main>': undefined method `foobar' for
181
+ # <null:IO>:NullIO (NoMethodError)
182
+ ```
183
+
184
+ There is also `impersonate` which takes `mimic` one step further. The
185
+ generated null class will be derived from the impersonated class. This
186
+ is handy when refitting legacy code that contains type checks.
187
+
188
+ ```ruby
189
+ require 'naught'
190
+
191
+ NullIO = Naught.build do |config|
192
+ config.impersonate IO
193
+ end
194
+
195
+ null_io = NullIO.new
196
+ IO === null_io # => true
197
+
198
+ case null_io
199
+ when IO
200
+ puts "Yep, checks out!"
201
+ null_io << "some output"
202
+ else
203
+ raise "Hey, I expected an IO!"
204
+ end
205
+ # >> Yep, checks out!
206
+ ```
207
+
208
+ #### Alright smartypants. What if I want to add my own methods?
209
+
210
+ Not a problem, just define them in the `.build` block.
211
+
212
+ ```ruby
213
+ require 'naught'
214
+
215
+ NullObject = Naught.build do |config|
216
+ config.define_explicit_conversions
217
+ def to_path
218
+ "/dev/null"
219
+ end
220
+
221
+ # You can override methods generated by Naught
222
+ def to_s
223
+ "NOTHING TO SEE HERE MOVE ALONG"
224
+ end
225
+ end
226
+
227
+ null = NullObject.new
228
+ null.to_s # => "NOTHING TO SEE HERE MOVE ALONG"
229
+ null.to_path # => "/dev/null"
230
+ ```
231
+
232
+ #### Got anything else up your sleeve?
233
+
234
+ Well, we can make the null class a singleton, since null objects
235
+ generally have no state.
236
+
237
+ ```ruby
238
+ require 'naught'
239
+
240
+ NullObject = Naught.build do |config|
241
+ config.singleton
242
+ end
243
+
244
+ null = NullObject.instance
245
+
246
+ null.__id__ # => 17844080
247
+ NullObject.instance.__id__ # => 17844080
248
+ NullObject.new # =>
249
+ # ~> -:11:in `<main>': private method `new' called for
250
+ # NullObject:Class (NoMethodError)
251
+ ```
252
+
253
+ Speaking of null objects with state, we can also enable tracing. This is
254
+ handy for playing "where'd that null come from?!" Try doing *that* with
255
+ `nil`!
256
+
257
+ ```ruby
258
+ require 'naught'
259
+
260
+ NullObject = Naught.build do |config|
261
+ config.traceable
262
+ end
263
+
264
+ null = NullObject.new # line 7
265
+
266
+ null.__file__ # => "example.rb"
267
+ null.__line__ # => 7
268
+ ```
269
+
270
+ We can even conditionally enable either singleton mode (for production)
271
+ or tracing (for development). Here's an example of using the `$DEBUG`
272
+ global variable (set with the `-d` option to ruby) to choose which one.
273
+
274
+ ```ruby
275
+ require 'naught'
276
+
277
+ NullObject = Naught.build do |config|
278
+ if $DEBUG
279
+ config.traceable
280
+ else
281
+ config.singleton
282
+ end
283
+ end
284
+ ```
285
+
286
+ The only caveat is that when swapping between singleton and
287
+ non-singleton implementations, you should be careful to always
288
+ instantiate your null objects with `NullObject.get`, not `.new` or
289
+ `.instance`. `.get` will work whether the class is implemented as a
290
+ singleton or not.
291
+
292
+ ```ruby
293
+ NullObject.get # => <null>
294
+ ```
295
+
296
+ #### And if I want to know legacy code better?
297
+
298
+ Naught can make a null object behave as a pebble object.
299
+
300
+ ```ruby
301
+ require 'naught'
302
+
303
+ NullObject = Naught.build do |config|
304
+ if $DEBUG
305
+ config.pebble
306
+ else
307
+ config.black_hole
308
+ end
309
+ end
310
+ ```
311
+
312
+ Now you can pass the pebble object to your code and see which messages are sent to the pebble.
313
+
314
+ ```ruby
315
+ null = NullObject.new
316
+
317
+ class MyConsumer < Struct.new(:producer)
318
+ def consume
319
+ producer.produce
320
+ end
321
+ end
322
+
323
+ MyConsumer.new(null).consume
324
+ # >> produce() from consume
325
+ # => <null>
326
+ ```
327
+
328
+ #### Are you done yet?
329
+
330
+ Just one more thing. For maximum convenience, Naught-generated null
331
+ classes also come with a full suite of conversion functions which can be
332
+ included into your classes.
333
+
334
+ ```ruby
335
+ require 'naught'
336
+
337
+ NullObject = Naught.build
338
+
339
+ include NullObject::Conversions
340
+
341
+ # Convert nil to null objects. Everything else passes through.
342
+ Maybe(42) # => 42
343
+ Maybe(nil) # => <null>
344
+ Maybe(NullObject.get) # => <null>
345
+ Maybe{ 42 } # => 42
346
+
347
+ # Insist on a non-null (or nil) value
348
+ Just(42) # => 42
349
+ Just(nil) rescue $! # => #<ArgumentError: Null value: nil>
350
+ Just(NullObject.get) rescue $! # => #<ArgumentError: Null value: <null>>
351
+
352
+ # nils and nulls become nulls. Everything else is rejected.
353
+ Null() # => <null>
354
+ Null(42) rescue $! # => #<ArgumentError: 42 is not null!>
355
+ Null(nil) # => <null>
356
+ Null(NullObject.get) # => <null>
357
+
358
+ # Convert nulls back to nils. Everything else passes through. Useful
359
+ # for preventing null objects from "leaking" into public API return
360
+ # values.
361
+ Actual(42) # => 42
362
+ Actual(nil) # => nil
363
+ Actual(NullObject.get) # => nil
364
+ Actual { 42 } # => 42
365
+ ```
366
+
367
+ Installation
368
+ --------------
369
+
370
+ ``` {.example}
371
+ gem install naught
372
+ ```
373
+
374
+ Requirements
375
+ --------------
376
+
377
+ - Ruby 1.9
378
+
379
+ Contributing
380
+ --------------
381
+
382
+ - Fork, branch, submit PR, blah blah blah. Don't forget tests.
383
+
384
+ Who's responsible
385
+ -------------------
386
+
387
+ Naught is by [Avdi Grimm](http://devblog.avdi.org/).
388
+
389
+ Prior Art
390
+ ---------
391
+
392
+ This isn't the first Ruby Null Object library. Others to check out include:
393
+
394
+ - [NullAndVoid](https://github.com/jfelchner/null_and_void)
395
+ - [BlankSlate](https://github.com/saturnflyer/blank_slate)
396
+
397
+
398
+ Further reading
399
+ -----------------
400
+
401
+ - [Null Object: Something for
402
+ Nothing](http://www.two-sdg.demon.co.uk/curbralan/papers/europlop/NullObject.pdf)
403
+ (PDF) by Kevlin Henney
404
+ - [The Null Object
405
+ Pattern](http://www.cs.oberlin.edu/~jwalker/refs/woolf.ps) (PS) by
406
+ Bobby Woolf
407
+ - [NullObject](http://www.c2.com/cgi/wiki?NullObject) on WikiWiki
408
+ - [Null Object
409
+ pattern](http://en.wikipedia.org/wiki/Null_Object_pattern) on
410
+ Wikipedia
411
+ - [Null Objects and
412
+ Falsiness](http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness/),
413
+ by Avdi Grimm