null_and_void 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,240 @@
1
+ Null And Void
2
+ ================================
3
+
4
+ **Create Null Objects with Ease**
5
+
6
+ <img src="http://cdn.memegenerator.net/instances/300x/34275719.jpg" align="right" />
7
+
8
+ Using the Null Object pattern in Ruby is rather easy so why create a gem?
9
+
10
+ I found myself using the same implementation around the [Null Object
11
+ pattern](https://en.wikipedia.org/wiki/Null_object_pattern#Ruby) over and over.
12
+ Rather than duplicating the logic in every one of my projects, I decided to
13
+ package it up as a gem.
14
+
15
+ In the process of doing research to make sure I was implementing this gem the
16
+ best way possible, I came across [an article by Avdi
17
+ Grimm](http://devblog.avdi.org/2011/05/30/null-objects-and-falsiness). In the
18
+ article Avdi brings up a lot of the same ideas I was implementing in my gem.
19
+ Additionally, he had come up with some [other](#helper-methods)
20
+ [nicities](#maybies) that I hadn't thought of.
21
+
22
+ <br/>
23
+ <br/>
24
+ <br/>
25
+ <br/>
26
+ <br/>
27
+ <br/>
28
+ <br/>
29
+
30
+ Installation
31
+ --------------------------------
32
+
33
+ gem install null_and_void
34
+
35
+ Usage
36
+ --------------------------------
37
+
38
+ Whenever you want to return a Null Object you can do something like:
39
+
40
+ ```ruby
41
+ def first_tps_line_item
42
+ TpsReport.line_items.first || NullAndVoid::NullObject.instance
43
+ end
44
+ ```
45
+
46
+ Alternatively, you can [define your own Null Objects](#defineyourownnullobjects)
47
+ such as:
48
+
49
+ ```ruby
50
+ def first_tps_line_item
51
+ TpsReport.line_items.first || NullTpsLineItem.instance
52
+ end
53
+ ```
54
+
55
+ If you decide to [use the mixin](#includeintoyourobjectsoptional) you can call
56
+ the `as_null_object` method on the class and use convention to figure out which
57
+ Null Object you want:
58
+
59
+ ```ruby
60
+ def first_tps_line_item
61
+ TpsReport.line_items.first || TpsLineItem.as_null_object
62
+ end
63
+ ```
64
+
65
+ In this case it will return a `NullTpsLineItem` if it's defined and otherwise
66
+ will return a `NullAndVoid::NullObject`.
67
+
68
+ Implementation
69
+ --------------------------------
70
+
71
+ ### Method Implementation #####################################################
72
+
73
+ With the exception of those defined below, any calls to any methods on a Null
74
+ Object that inherits from `NullAndVoid::NullObject` will result in itself.
75
+
76
+ **Example:**
77
+
78
+ ```ruby
79
+ null_object = NullAndVoid::NullObject.instance
80
+
81
+ null_object == null_object.foobar # => true
82
+ ```
83
+
84
+ ### Helper Methods ############################################################
85
+
86
+ Many common Ruby methods are implemented as 'noops' such as:
87
+
88
+ ```ruby
89
+ null_object = NullUser.instance
90
+
91
+ null_object.to_i # => 0
92
+ null_object.to_int # => 0
93
+ null_object.to_f # => 0.0
94
+ null_object.to_r # => (0/1)
95
+ null_object.to_a # => []
96
+ null_object.to_ary # => []
97
+ null_object.to_hash # => {}
98
+ null_object.to_html # => ''
99
+ null_object.to_json # => ''
100
+ null_object.to_xml # => ''
101
+ null_object.to_model # => User.new
102
+ null_object.to_s # => ''
103
+ ```
104
+
105
+ ### Falsity ###################################################################
106
+
107
+ `NullAndVoid::NullObject` attempts to be falsey whenever possible however it's
108
+ my opinion that this is an anti-pattern and only the basic attempts are made to
109
+ make this work. For example:
110
+
111
+ ```ruby
112
+ null_object = NullAndVoid::NullObject.instance
113
+
114
+ !null_object # => true
115
+ null_object.nil? # => true
116
+ null_object.blank? # => true
117
+ null_object.present? # => false
118
+ ```
119
+
120
+ ### Singleton Implementation ##################################################
121
+
122
+ `NullAndVoid::NullObject` is a singleton. All calls to an implementation of it
123
+ will result in the same object. Therefore:
124
+
125
+ ```ruby
126
+ NullUser.instance == User.new.as_null_object == User.as_null_object # => true
127
+ ```
128
+
129
+ ### Define Your Own Null Objects ##############################################
130
+
131
+ When implementing a Null Object for a specific class that you own, simply
132
+ include `NullAndVoid::Nullified` like so:
133
+
134
+ ```ruby
135
+ require 'null_and_void/nullified'
136
+
137
+ class NullUser
138
+ include NullAndVoid::Nullified
139
+
140
+ def admin?
141
+ false
142
+ end
143
+ end
144
+ ```
145
+
146
+ ### Maybies ###################################################################
147
+
148
+ Unfortunately when you're programming in the 'Real World &copy;" you don't
149
+ always have control over the API you're using. And sometimes, those APIs,
150
+ rather than doing the sane thing and always returning the same type no matter
151
+ what, instead return nils.
152
+
153
+ In these situations, you could do this:
154
+
155
+ ```ruby
156
+ class Foo
157
+ def bar
158
+ call_some_method_that_could_return_nils || NullAndVoid::NullObject.instance
159
+ end
160
+ end
161
+ ```
162
+
163
+ Or instead, you can simply mixin `NullAndVoid::Mayby` and it will handle that
164
+ for you. For example:
165
+
166
+ ```ruby
167
+ class Foo
168
+ include NullAndVoid::Maybe
169
+
170
+ def bar
171
+ maybe(call_some_method_that_could_return_nils)
172
+ end
173
+ end
174
+ ```
175
+
176
+ If you'd rather not mix it into your class, you can also just call it directly:
177
+
178
+ ```ruby
179
+ class Foo
180
+ def bar
181
+ NullAndVoid.maybe(call_some_method_that_could_return_nils)
182
+ end
183
+ end
184
+ ```
185
+
186
+ ### Include Into Your Objects (Optional) ######################################
187
+
188
+ You can add Null Object creation to your class by including
189
+ `NullAndVoid::ModelSupport`.
190
+
191
+ ```ruby
192
+ class User
193
+ include NullAndVoid::ModelSupport
194
+
195
+ def admin?
196
+ type == 'admin'
197
+ end
198
+ end
199
+ ```
200
+
201
+ This will endow your class with two methods `.as_null_object` and
202
+ `#as_null_object`. Both of which return either the Null Object for that class
203
+ (eg `NullUser`) or the generic `NullAndVoid::NullObject`.
204
+
205
+ Now, when calling:
206
+
207
+ ```ruby
208
+ User.as_null_object
209
+ ```
210
+
211
+ or:
212
+
213
+ ```ruby
214
+ User.new.as_null_object
215
+ ```
216
+
217
+ Will return:
218
+
219
+ ```ruby
220
+ NullUser.instance
221
+ ```
222
+
223
+ Issues
224
+ ------
225
+
226
+ If you have problems, please create a [Github issue](issues).
227
+
228
+ Credits
229
+ -------
230
+
231
+ ![thekompanee](http://www.thekompanee.com/public_files/kompanee-github-readme-logo.png)
232
+
233
+ null_and_void is maintained by [The Kompanee, Ltd.](http://www.thekompanee.com)
234
+
235
+ The names and logos for The Kompanee are trademarks of The Kompanee, Ltd.
236
+
237
+ License
238
+ -------
239
+
240
+ null_and_void is Copyright &copy; 2013 The Kompanee. It is free software, and may be redistributed under the terms specified in the LICENSE file.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,7 @@
1
+ require 'null_and_void/version'
2
+ require 'null_and_void/null_object'
3
+ require 'null_and_void/maybe'
4
+
5
+ module NullAndVoid
6
+ extend NullAndVoid::Maybe
7
+ end
@@ -0,0 +1,54 @@
1
+ require 'active_support/inflector/methods'
2
+
3
+ module NullAndVoid
4
+ module Convertible
5
+ def to_i
6
+ 0
7
+ end
8
+
9
+ alias :to_int :to_i
10
+
11
+ def to_f
12
+ 0.0
13
+ end
14
+
15
+ def to_r
16
+ Rational(0)
17
+ end
18
+
19
+ def to_a
20
+ Array.new
21
+ end
22
+
23
+ alias :to_ary :to_a
24
+
25
+ def to_hash
26
+ Hash.new
27
+ end
28
+
29
+ def to_html
30
+ ''
31
+ end
32
+
33
+ def to_json
34
+ ''
35
+ end
36
+
37
+ def to_xml
38
+ ''
39
+ end
40
+
41
+ def to_s
42
+ ''
43
+ end
44
+
45
+ def to_model
46
+ base_path = ActiveSupport::Inflector.demodulize(self.class.name)
47
+ module_path = ActiveSupport::Inflector.deconstantize(self.class.name)
48
+ source_model = base_path.gsub(/^Null/, '')
49
+ source_model_path = "#{module_path}::#{source_model}"
50
+
51
+ ActiveSupport::Inflector.constantize(source_model_path).new
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,19 @@
1
+ module NullAndVoid
2
+ module Falsifiable
3
+ def !
4
+ true
5
+ end
6
+
7
+ def blank?
8
+ true
9
+ end
10
+
11
+ def nil?
12
+ true
13
+ end
14
+
15
+ def present?
16
+ false
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,9 @@
1
+ require 'null_and_void/null_object'
2
+
3
+ module NullAndVoid
4
+ module Maybe
5
+ def maybe(parameter)
6
+ parameter.nil? ? NullAndVoid::NullObject.instance : parameter
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,27 @@
1
+ require 'active_support/inflector/methods'
2
+ require 'null_and_void/null_object'
3
+
4
+ module NullAndVoid
5
+ module ModelSupport
6
+ module ClassMethods
7
+ def as_null_object
8
+ base_path = ActiveSupport::Inflector.demodulize(self.name)
9
+ module_path = ActiveSupport::Inflector.deconstantize(self.name)
10
+ null_object_base = "Null#{base_path}"
11
+ source_model_path = "#{module_path}::#{null_object_base}"
12
+
13
+ ActiveSupport::Inflector.constantize(source_model_path).new
14
+ rescue NameError
15
+ NullAndVoid::NullObject.instance
16
+ end
17
+ end
18
+
19
+ def as_null_object
20
+ self.class.as_null_object
21
+ end
22
+
23
+ def self.included(base)
24
+ base.extend ClassMethods
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ require 'null_and_void/nullified'
2
+
3
+ module NullAndVoid
4
+ class NullObject
5
+ include ::NullAndVoid::Nullified
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ require 'singleton'
2
+ require 'null_and_void/stubbable'
3
+ require 'null_and_void/convertible'
4
+ require 'null_and_void/falsifiable'
5
+
6
+ module NullAndVoid
7
+ module Nullified
8
+ def self.included(base)
9
+ base.send(:include, Singleton)
10
+ base.send(:include, Stubbable)
11
+ base.send(:include, Convertible)
12
+ base.send(:include, Falsifiable)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ module NullAndVoid
2
+ module Stubbable
3
+ def method_missing(*args, &block)
4
+ self
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module NullAndVoid
2
+ VERSION = '1.0.0'
3
+ end
@@ -0,0 +1,70 @@
1
+ require 'rspectacular'
2
+ require 'null_and_void/convertible'
3
+
4
+ module FooBar
5
+ class NullObject
6
+ include NullAndVoid::Convertible
7
+ end
8
+
9
+ class NullBaz
10
+ include NullAndVoid::Convertible
11
+ end
12
+
13
+ class Object
14
+ end
15
+ end
16
+
17
+ describe NullAndVoid::Convertible do
18
+ let(:null_object) { FooBar::NullObject.new }
19
+
20
+ it 'is zero when the attempt is made to convert it to an Integer' do
21
+ null_object.to_i.should be_an Integer
22
+ null_object.to_i.should be_zero
23
+
24
+ null_object.to_int.should be_an Integer
25
+ null_object.to_int.should be_zero
26
+ end
27
+
28
+ it 'is zero when the attempt is made to convert it to a Float' do
29
+ null_object.to_f.should be_a Float
30
+ null_object.to_f.should be_zero
31
+ end
32
+
33
+ it 'is zero when the attempt is made to convert it to a Rational' do
34
+ null_object.to_r.should be_a Rational
35
+ null_object.to_r.should be_zero
36
+ end
37
+
38
+ it 'is an empty Array when the attempt is made to convert it to an Array' do
39
+ null_object.to_a.should eql []
40
+ null_object.to_ary.should eql []
41
+ end
42
+
43
+ it 'is an empty Hash when the attempt is made to convert it to an Hash' do
44
+ null_object.to_hash.should eql Hash.new
45
+ end
46
+
47
+ it 'is an empty String when the attempt is made to convert it to HTML' do
48
+ null_object.to_html.should eql ''
49
+ end
50
+
51
+ it 'is an empty String when the attempt is made to convert it to JSON' do
52
+ null_object.to_json.should eql ''
53
+ end
54
+
55
+ it 'is an empty String when the attempt is made to convert it to XML' do
56
+ null_object.to_xml.should eql ''
57
+ end
58
+
59
+ it 'is an empty String when the attempt is made to convert it to a String' do
60
+ null_object.to_s.should eql ''
61
+ end
62
+
63
+ it 'is the class the Null Object is based on when the attempt is made to convert it to a value object' do
64
+ null_object.to_model.should be_a FooBar::Object
65
+ end
66
+
67
+ it 'raises an error if the model the Null Object is based on does not exist' do
68
+ expect { FooBar::NullBaz.new.to_model }.to raise_error NameError
69
+ end
70
+ end
@@ -0,0 +1,26 @@
1
+ require 'rspectacular'
2
+ require 'null_and_void/falsifiable'
3
+
4
+ class NullObject
5
+ include NullAndVoid::Falsifiable
6
+ end
7
+
8
+ describe NullAndVoid::Falsifiable do
9
+ let(:null_object) { NullObject.new }
10
+
11
+ it 'can be double inverted to be false' do
12
+ (!!null_object).should be_false
13
+ end
14
+
15
+ it 'is blank' do
16
+ null_object.should be_blank
17
+ end
18
+
19
+ it 'is nil' do
20
+ null_object.should be_nil
21
+ end
22
+
23
+ it 'is not present' do
24
+ null_object.should_not be_present
25
+ end
26
+ end
@@ -0,0 +1,20 @@
1
+ require 'rspectacular'
2
+ require 'null_and_void/maybe'
3
+
4
+ class MyObject
5
+ include NullAndVoid::Maybe
6
+ end
7
+
8
+ describe NullAndVoid::Maybe do
9
+ context 'when it is passed a nil' do
10
+ it 'is a null object' do
11
+ MyObject.new.maybe(nil).should be_a NullAndVoid::NullObject
12
+ end
13
+ end
14
+
15
+ context 'when it is passed something other than nil' do
16
+ it 'is the non-nil object' do
17
+ MyObject.new.maybe(1).should eql 1
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,31 @@
1
+ require 'rspectacular'
2
+ require 'null_and_void/model_support'
3
+
4
+ module FooBar
5
+ class MyNullableModel
6
+ include NullAndVoid::ModelSupport
7
+ end
8
+
9
+ class NullMyNullableModel
10
+ end
11
+ end
12
+
13
+ class NonNullableModel
14
+ include NullAndVoid::ModelSupport
15
+ end
16
+
17
+ describe NullAndVoid::ModelSupport do
18
+ let(:model) { FooBar::MyNullableModel.new }
19
+
20
+ it 'can convert a model into its Null Object if it exists' do
21
+ model.as_null_object.should be_a FooBar::NullMyNullableModel
22
+ end
23
+
24
+ it "can convert the model's class into its Null Object if it exists" do
25
+ model.class.as_null_object.should be_a FooBar::NullMyNullableModel
26
+ end
27
+
28
+ it 'can convert a model into a generic Null Object a model-specific Null Object does not exist' do
29
+ NonNullableModel.as_null_object.should be_a NullAndVoid::NullObject
30
+ end
31
+ end
@@ -0,0 +1,8 @@
1
+ require 'rspectacular'
2
+ require 'null_and_void'
3
+
4
+ describe NullAndVoid do
5
+ it 'can perform "maybies"' do
6
+ NullAndVoid.maybe(nil).should be_a NullAndVoid::NullObject
7
+ end
8
+ end
@@ -0,0 +1,22 @@
1
+ require 'rspectacular'
2
+ require 'null_and_void/null_object'
3
+
4
+ describe NullAndVoid::NullObject do
5
+ let(:null_object) { NullAndVoid::NullObject.instance }
6
+
7
+ it 'is a singleton' do
8
+ null_object == NullAndVoid::NullObject.instance
9
+ end
10
+
11
+ it 'is stubbable' do
12
+ null_object.should be_a NullAndVoid::Stubbable
13
+ end
14
+
15
+ it 'is convertible' do
16
+ null_object.should be_a NullAndVoid::Convertible
17
+ end
18
+
19
+ it 'is falsifiable' do
20
+ null_object.should be_a NullAndVoid::Falsifiable
21
+ end
22
+ end
@@ -0,0 +1,39 @@
1
+ require 'rspectacular'
2
+ require 'null_and_void/null_object'
3
+ require 'null_and_void/nullified'
4
+
5
+ class NullWidget
6
+ include NullAndVoid::Nullified
7
+
8
+ def my_null_object_method
9
+ 'i am null'
10
+ end
11
+ end
12
+
13
+ describe NullAndVoid::Nullified do
14
+ let(:null_object) { NullWidget.instance }
15
+
16
+ it 'is a singleton' do
17
+ null_object == NullWidget.instance
18
+ end
19
+
20
+ it 'is not a NullAndVoid::NullObject singleton' do
21
+ null_object != NullAndVoid::NullObject.instance
22
+ end
23
+
24
+ it 'handles undefined methods' do
25
+ null_object.foobar.should eql NullWidget.instance
26
+ end
27
+
28
+ it 'handles conversions' do
29
+ null_object.to_a.should eql []
30
+ end
31
+
32
+ it 'is falsifiable' do
33
+ (!!null_object).should be_false
34
+ end
35
+
36
+ it 'can implement its own methods' do
37
+ null_object.my_null_object_method.should eql 'i am null'
38
+ end
39
+ end
@@ -0,0 +1,14 @@
1
+ require 'rspectacular'
2
+ require 'null_and_void/stubbable'
3
+
4
+ class NullObject
5
+ include NullAndVoid::Stubbable
6
+ end
7
+
8
+ describe NullAndVoid::Stubbable do
9
+ let(:null_object) { NullObject.new }
10
+
11
+ it 'returns itself for all unknown method calls' do
12
+ null_object.foobar.should eql null_object
13
+ end
14
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: null_and_void
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - jfelchner
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-03 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: '3.1'
21
+ none: false
22
+ prerelease: false
23
+ type: :runtime
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ version: '3.1'
29
+ none: false
30
+ - !ruby/object:Gem::Dependency
31
+ name: rspec
32
+ requirement: !ruby/object:Gem::Requirement
33
+ requirements:
34
+ - - ~>
35
+ - !ruby/object:Gem::Version
36
+ version: '2.12'
37
+ none: false
38
+ prerelease: false
39
+ type: :development
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ~>
43
+ - !ruby/object:Gem::Version
44
+ version: '2.12'
45
+ none: false
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspectacular
48
+ requirement: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ~>
51
+ - !ruby/object:Gem::Version
52
+ version: '0.7'
53
+ none: false
54
+ prerelease: false
55
+ type: :development
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ~>
59
+ - !ruby/object:Gem::Version
60
+ version: '0.7'
61
+ none: false
62
+ description: Makes generating Null Objects easy.
63
+ email: accounts+git@thekompanee.com
64
+ executables: []
65
+ extensions: []
66
+ extra_rdoc_files:
67
+ - README.md
68
+ files:
69
+ - lib/null_and_void/convertible.rb
70
+ - lib/null_and_void/falsifiable.rb
71
+ - lib/null_and_void/maybe.rb
72
+ - lib/null_and_void/model_support.rb
73
+ - lib/null_and_void/null_object.rb
74
+ - lib/null_and_void/nullified.rb
75
+ - lib/null_and_void/stubbable.rb
76
+ - lib/null_and_void/version.rb
77
+ - lib/null_and_void.rb
78
+ - Rakefile
79
+ - README.md
80
+ - spec/convertible_spec.rb
81
+ - spec/falsifiable_spec.rb
82
+ - spec/maybe_spec.rb
83
+ - spec/model_support_spec.rb
84
+ - spec/null_and_void_spec.rb
85
+ - spec/null_object_spec.rb
86
+ - spec/nullified_spec.rb
87
+ - spec/stubbable_spec.rb
88
+ homepage: https://github.com/jfelchner/null_and_void
89
+ licenses: []
90
+ post_install_message:
91
+ rdoc_options:
92
+ - --charset = UTF-8
93
+ require_paths:
94
+ - lib
95
+ required_ruby_version: !ruby/object:Gem::Requirement
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ none: false
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ none: false
107
+ requirements: []
108
+ rubyforge_project: null_and_void
109
+ rubygems_version: 1.8.25
110
+ signing_key:
111
+ specification_version: 3
112
+ summary: Easy Null Objects
113
+ test_files:
114
+ - spec/convertible_spec.rb
115
+ - spec/falsifiable_spec.rb
116
+ - spec/maybe_spec.rb
117
+ - spec/model_support_spec.rb
118
+ - spec/null_and_void_spec.rb
119
+ - spec/null_object_spec.rb
120
+ - spec/nullified_spec.rb
121
+ - spec/stubbable_spec.rb
122
+ has_rdoc: