null_and_void 1.0.0

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.
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: