naught 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ /naught.org
19
+ /naught.html
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ -fs --color --order rand
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in naught.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem "libnotify"
8
+ end
data/Guardfile ADDED
@@ -0,0 +1,5 @@
1
+ guard :rspec, cli: '-fs --color --order rand' do
2
+ watch(%r{^spec/.+_spec\.rb$})
3
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
4
+ watch('spec/spec_helper.rb') { "spec" }
5
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Avdi Grimm
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.org ADDED
@@ -0,0 +1,340 @@
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]].
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/autospec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'autospec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'autospec')
data/bin/coderay ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'coderay' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('coderay', 'coderay')
data/bin/guard ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'guard' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('guard', 'guard')