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.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/.travis.yml +2 -0
- data/Changelog.md +6 -0
- data/Gemfile +1 -0
- data/Guardfile +10 -0
- data/README.markdown +413 -0
- data/Rakefile +5 -0
- data/lib/naught.rb +1 -4
- data/lib/naught/null_class_builder.rb +37 -137
- data/lib/naught/null_class_builder/command.rb +20 -0
- data/lib/naught/null_class_builder/commands.rb +8 -0
- data/lib/naught/null_class_builder/commands/define_explicit_conversions.rb +13 -24
- data/lib/naught/null_class_builder/commands/define_implicit_conversions.rb +14 -0
- data/lib/naught/null_class_builder/commands/impersonate.rb +9 -0
- data/lib/naught/null_class_builder/commands/mimic.rb +40 -0
- data/lib/naught/null_class_builder/commands/pebble.rb +36 -0
- data/lib/naught/null_class_builder/commands/predicates_return.rb +47 -0
- data/lib/naught/null_class_builder/commands/singleton.rb +24 -0
- data/lib/naught/null_class_builder/commands/traceable.rb +19 -0
- data/lib/naught/null_class_builder/conversions_module.rb +57 -0
- data/lib/naught/version.rb +1 -1
- data/naught.gemspec +2 -1
- data/spec/base_object_spec.rb +47 -0
- data/spec/basic_null_object_spec.rb +35 -0
- data/spec/blackhole_spec.rb +16 -0
- data/spec/conversions_spec.rb +20 -0
- data/spec/functions/actual_spec.rb +22 -0
- data/spec/functions/just_spec.rb +22 -0
- data/spec/functions/maybe_spec.rb +35 -0
- data/spec/functions/null_spec.rb +34 -0
- data/spec/implicit_conversions_spec.rb +25 -0
- data/spec/mimic_spec.rb +122 -0
- data/spec/naught/null_object_builder/command_spec.rb +10 -0
- data/spec/naught/null_object_builder_spec.rb +31 -0
- data/spec/naught_spec.rb +77 -411
- data/spec/pebble_spec.rb +75 -0
- data/spec/predicate_spec.rb +80 -0
- data/spec/singleton_null_object_spec.rb +35 -0
- data/spec/spec_helper.rb +13 -1
- data/spec/support/convertable_null.rb +4 -0
- metadata +76 -32
- data/.rspec +0 -1
- data/README.org +0 -340
data/Rakefile
CHANGED
data/lib/naught.rb
CHANGED
@@ -1,14 +1,11 @@
|
|
1
1
|
require "naught/version"
|
2
2
|
require 'naught/null_class_builder'
|
3
|
-
require 'naught/null_class_builder/commands
|
3
|
+
require 'naught/null_class_builder/commands'
|
4
4
|
|
5
5
|
module Naught
|
6
6
|
def self.build(&customization_block)
|
7
7
|
builder = NullClassBuilder.new
|
8
8
|
builder.customize(&customization_block)
|
9
|
-
unless builder.interface_defined?
|
10
|
-
builder.respond_to_any_message
|
11
|
-
end
|
12
9
|
builder.generate_class
|
13
10
|
end
|
14
11
|
module NullObjectTag
|
@@ -1,9 +1,13 @@
|
|
1
|
+
require 'naught/null_class_builder/conversions_module'
|
2
|
+
|
1
3
|
module Naught
|
2
4
|
class NullClassBuilder
|
3
5
|
# make sure this module exists
|
4
6
|
module Commands
|
5
7
|
end
|
6
8
|
|
9
|
+
attr_accessor :base_class, :inspect_proc, :interface_defined
|
10
|
+
|
7
11
|
def initialize
|
8
12
|
@interface_defined = false
|
9
13
|
@base_class = BasicObject
|
@@ -30,56 +34,17 @@ module Naught
|
|
30
34
|
end
|
31
35
|
|
32
36
|
def generate_conversions_module(null_class)
|
33
|
-
|
34
|
-
@conversions_module ||= Module.new do
|
35
|
-
define_method(:Null) do |object=:nothing_passed|
|
36
|
-
case object
|
37
|
-
when NullObjectTag then object
|
38
|
-
when :nothing_passed, *null_equivs
|
39
|
-
null_class.get(caller: caller(1))
|
40
|
-
else raise ArgumentError, "#{object.inspect} is not null!"
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
define_method(:Maybe) do |object=nil, &block|
|
45
|
-
object = block ? block.call : object
|
46
|
-
case object
|
47
|
-
when NullObjectTag then object
|
48
|
-
when *null_equivs
|
49
|
-
null_class.get(caller: caller(1))
|
50
|
-
else
|
51
|
-
object
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
define_method(:Just) do |object=nil, &block|
|
56
|
-
object = block ? block.call : object
|
57
|
-
case object
|
58
|
-
when NullObjectTag, *null_equivs
|
59
|
-
raise ArgumentError, "Null value: #{object.inspect}"
|
60
|
-
else
|
61
|
-
object
|
62
|
-
end
|
63
|
-
end
|
64
|
-
|
65
|
-
define_method(:Actual) do |object=nil, &block|
|
66
|
-
object = block ? block.call : object
|
67
|
-
case object
|
68
|
-
when NullObjectTag then nil
|
69
|
-
else
|
70
|
-
object
|
71
|
-
end
|
72
|
-
end
|
73
|
-
end
|
37
|
+
ConversionsModule.new(null_class, null_equivalents)
|
74
38
|
end
|
75
39
|
|
76
40
|
def generate_class
|
41
|
+
respond_to_any_message unless interface_defined?
|
77
42
|
generation_mod = Module.new
|
78
43
|
customization_mod = customization_module # get a local binding
|
79
44
|
builder = self
|
80
|
-
|
81
|
-
|
82
|
-
|
45
|
+
|
46
|
+
apply_operations(operations, generation_mod)
|
47
|
+
|
83
48
|
null_class = Class.new(@base_class) do
|
84
49
|
const_set :GeneratedMethods, generation_mod
|
85
50
|
const_set :Customizations, customization_mod
|
@@ -89,9 +54,9 @@ module Naught
|
|
89
54
|
include generation_mod
|
90
55
|
include customization_mod
|
91
56
|
end
|
92
|
-
|
93
|
-
|
94
|
-
|
57
|
+
|
58
|
+
apply_operations(class_operations, null_class)
|
59
|
+
|
95
60
|
null_class
|
96
61
|
end
|
97
62
|
|
@@ -105,9 +70,11 @@ module Naught
|
|
105
70
|
end
|
106
71
|
end
|
107
72
|
|
108
|
-
def respond_to_missing?(method_name,
|
73
|
+
def respond_to_missing?(method_name, include_private=false)
|
109
74
|
command_name = command_name_for_method(method_name)
|
110
75
|
Commands.const_defined?(command_name) || super
|
76
|
+
rescue NameError
|
77
|
+
super
|
111
78
|
end
|
112
79
|
|
113
80
|
############################################################################
|
@@ -115,37 +82,13 @@ module Naught
|
|
115
82
|
#
|
116
83
|
# See also the contents of lib/naught/null_class_builder/commands
|
117
84
|
############################################################################
|
118
|
-
def define_implicit_conversions
|
119
|
-
defer do |subject|
|
120
|
-
subject.module_eval do
|
121
|
-
def to_ary; []; end
|
122
|
-
def to_str; ''; end
|
123
|
-
end
|
124
|
-
end
|
125
|
-
end
|
126
|
-
|
127
|
-
def mimic(class_to_mimic, options={})
|
128
|
-
include_super = options.fetch(:include_super) { true }
|
129
|
-
@base_class = root_class_of(class_to_mimic)
|
130
|
-
@inspect_proc = -> { "<null:#{class_to_mimic}>" }
|
131
|
-
defer do |subject|
|
132
|
-
subject.module_eval do
|
133
|
-
methods = class_to_mimic.instance_methods(include_super) -
|
134
|
-
Object.instance_methods
|
135
|
-
methods.each do |method_name|
|
136
|
-
define_method(method_name) {|*| nil}
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
@interface_defined = true
|
141
|
-
end
|
142
85
|
|
143
86
|
def black_hole
|
144
87
|
@stub_strategy = :stub_method_returning_self
|
145
88
|
end
|
146
89
|
|
147
90
|
def respond_to_any_message
|
148
|
-
defer do |subject|
|
91
|
+
defer(prepend: true) do |subject|
|
149
92
|
subject.module_eval do
|
150
93
|
def respond_to?(*)
|
151
94
|
true
|
@@ -156,69 +99,43 @@ module Naught
|
|
156
99
|
@interface_defined = true
|
157
100
|
end
|
158
101
|
|
159
|
-
def
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
Object.instance_methods
|
166
|
-
methods.each do |method_name|
|
167
|
-
stub_method(subject, method_name)
|
168
|
-
end
|
102
|
+
def defer(options={}, &deferred_operation)
|
103
|
+
list = options[:class] ? class_operations : operations
|
104
|
+
if options[:prepend]
|
105
|
+
list.unshift(deferred_operation)
|
106
|
+
else
|
107
|
+
list << deferred_operation
|
169
108
|
end
|
170
|
-
@interface_defined = true
|
171
109
|
end
|
172
110
|
|
173
|
-
def
|
174
|
-
|
175
|
-
@base_class = class_to_impersonate
|
111
|
+
def stub_method(subject, name)
|
112
|
+
send(@stub_strategy, subject, name)
|
176
113
|
end
|
177
114
|
|
178
|
-
|
179
|
-
defer do |subject|
|
180
|
-
subject.module_eval do
|
181
|
-
attr_reader :__file__, :__line__
|
115
|
+
private
|
182
116
|
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
@__line__ = line.to_i
|
187
|
-
end
|
188
|
-
end
|
189
|
-
end
|
117
|
+
def define_basic_methods
|
118
|
+
define_basic_instance_methods
|
119
|
+
define_basic_class_methods
|
190
120
|
end
|
191
121
|
|
192
|
-
def
|
193
|
-
|
194
|
-
|
195
|
-
else
|
196
|
-
operations << deferred_operation
|
122
|
+
def apply_operations(operations, module_or_class)
|
123
|
+
operations.each do |operation|
|
124
|
+
operation.call(module_or_class)
|
197
125
|
end
|
198
126
|
end
|
199
127
|
|
200
|
-
def
|
201
|
-
defer(class: true) do |subject|
|
202
|
-
require 'singleton'
|
203
|
-
subject.module_eval do
|
204
|
-
include Singleton
|
205
|
-
def self.get(*)
|
206
|
-
instance
|
207
|
-
end
|
208
|
-
end
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
def define_basic_methods
|
128
|
+
def define_basic_instance_methods
|
213
129
|
defer do |subject|
|
214
|
-
|
215
|
-
inspect_proc = @inspect_proc
|
216
|
-
subject.module_eval do
|
130
|
+
subject.module_exec(@inspect_proc) do |inspect_proc|
|
217
131
|
define_method(:inspect, &inspect_proc)
|
218
132
|
def initialize(*)
|
219
133
|
end
|
220
134
|
end
|
221
135
|
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def define_basic_class_methods
|
222
139
|
defer(class: true) do |subject|
|
223
140
|
subject.module_eval do
|
224
141
|
class << self
|
@@ -230,8 +147,6 @@ module Naught
|
|
230
147
|
end
|
231
148
|
end
|
232
149
|
|
233
|
-
private
|
234
|
-
|
235
150
|
def class_operations
|
236
151
|
@class_operations ||= []
|
237
152
|
end
|
@@ -240,10 +155,6 @@ module Naught
|
|
240
155
|
@operations ||= []
|
241
156
|
end
|
242
157
|
|
243
|
-
def stub_method(subject, name)
|
244
|
-
send(@stub_strategy, subject, name)
|
245
|
-
end
|
246
|
-
|
247
158
|
def stub_method_returning_nil(subject, name)
|
248
159
|
subject.module_eval do
|
249
160
|
define_method(name) {|*| nil }
|
@@ -257,18 +168,7 @@ module Naught
|
|
257
168
|
end
|
258
169
|
|
259
170
|
def command_name_for_method(method_name)
|
260
|
-
|
261
|
-
gsub(/_(\w)/){ $1.upcase }.
|
262
|
-
gsub(/\A(\w)/){ $1.upcase }
|
263
|
-
end
|
264
|
-
|
265
|
-
def root_class_of(klass)
|
266
|
-
if klass.ancestors.include?(Object)
|
267
|
-
Object
|
268
|
-
else
|
269
|
-
BasicObject
|
270
|
-
end
|
171
|
+
method_name.to_s.gsub(/(?:^|_)([a-z])/) { $1.upcase }
|
271
172
|
end
|
272
|
-
|
273
173
|
end
|
274
174
|
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Naught
|
2
|
+
class NullClassBuilder
|
3
|
+
class Command
|
4
|
+
attr_reader :builder
|
5
|
+
|
6
|
+
def initialize(builder)
|
7
|
+
@builder = builder
|
8
|
+
end
|
9
|
+
|
10
|
+
def call
|
11
|
+
raise NotImplementedError,
|
12
|
+
"Method #call should be overriden in child classes"
|
13
|
+
end
|
14
|
+
|
15
|
+
def defer(options={}, &block)
|
16
|
+
@builder.defer(options, &block)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'naught/null_class_builder/commands/define_explicit_conversions'
|
2
|
+
require 'naught/null_class_builder/commands/define_implicit_conversions'
|
3
|
+
require 'naught/null_class_builder/commands/pebble'
|
4
|
+
require 'naught/null_class_builder/commands/predicates_return'
|
5
|
+
require 'naught/null_class_builder/commands/singleton'
|
6
|
+
require 'naught/null_class_builder/commands/traceable'
|
7
|
+
require 'naught/null_class_builder/commands/mimic'
|
8
|
+
require 'naught/null_class_builder/commands/impersonate'
|
@@ -1,28 +1,17 @@
|
|
1
|
+
require 'naught/null_class_builder/command'
|
1
2
|
|
2
|
-
module Naught
|
3
|
-
class NullClassBuilder
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
def to_i; 0; end
|
15
|
-
def to_f; 0.0; end
|
16
|
-
def to_c; 0.to_c; end
|
17
|
-
def to_r; 0.to_r; end
|
18
|
-
def to_a; []; end
|
19
|
-
def to_h; {}; end
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def defer(&block)
|
25
|
-
@builder.defer(&block)
|
3
|
+
module Naught::NullClassBuilder::Commands
|
4
|
+
class DefineExplicitConversions < ::Naught::NullClassBuilder::Command
|
5
|
+
def call
|
6
|
+
defer do |subject|
|
7
|
+
subject.module_eval do
|
8
|
+
def to_s; ""; end
|
9
|
+
def to_i; 0; end
|
10
|
+
def to_f; 0.0; end
|
11
|
+
def to_c; 0.to_c; end
|
12
|
+
def to_r; 0.to_r; end
|
13
|
+
def to_a; []; end
|
14
|
+
def to_h; {}; end
|
26
15
|
end
|
27
16
|
end
|
28
17
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'naught/null_class_builder/command'
|
2
|
+
|
3
|
+
module Naught::NullClassBuilder::Commands
|
4
|
+
class DefineImplicitConversions < ::Naught::NullClassBuilder::Command
|
5
|
+
def call
|
6
|
+
defer do |subject|
|
7
|
+
subject.module_eval do
|
8
|
+
def to_ary; []; end
|
9
|
+
def to_str; ''; end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'naught/null_class_builder/command'
|
2
|
+
|
3
|
+
module Naught::NullClassBuilder::Commands
|
4
|
+
class Mimic < Naught::NullClassBuilder::Command
|
5
|
+
attr_reader :class_to_mimic, :include_super
|
6
|
+
|
7
|
+
def initialize(builder, class_to_mimic, options={})
|
8
|
+
super(builder)
|
9
|
+
|
10
|
+
@class_to_mimic = class_to_mimic
|
11
|
+
@include_super = options.fetch(:include_super) { true }
|
12
|
+
|
13
|
+
builder.base_class = root_class_of(class_to_mimic)
|
14
|
+
builder.inspect_proc = -> { "<null:#{class_to_mimic}>" }
|
15
|
+
builder.interface_defined = true
|
16
|
+
end
|
17
|
+
|
18
|
+
def call
|
19
|
+
defer do |subject|
|
20
|
+
methods_to_stub.each do |method_name|
|
21
|
+
builder.stub_method(subject, method_name)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def root_class_of(klass)
|
29
|
+
if klass.ancestors.include?(Object)
|
30
|
+
Object
|
31
|
+
else
|
32
|
+
BasicObject
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def methods_to_stub
|
37
|
+
class_to_mimic.instance_methods(include_super) - Object.instance_methods
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'naught/null_class_builder/command'
|
2
|
+
|
3
|
+
module Naught
|
4
|
+
class NullClassBuilder
|
5
|
+
module Commands
|
6
|
+
class Pebble < ::Naught::NullClassBuilder::Command
|
7
|
+
|
8
|
+
def initialize(builder, output=$stdout)
|
9
|
+
@builder = builder
|
10
|
+
@output = output
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
defer do |subject|
|
15
|
+
subject.module_exec(@output) do |output|
|
16
|
+
|
17
|
+
define_method(:method_missing) do |method_name, *args, &block|
|
18
|
+
pretty_args = args.map(&:inspect).join(", ").gsub("\"", "'")
|
19
|
+
output.puts "#{method_name}(#{pretty_args}) from #{parse_caller}"
|
20
|
+
self
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def parse_caller
|
26
|
+
caller = Kernel.caller(2).first
|
27
|
+
method_name = caller.match(/\`([\w\s]+(\(\d+\s\w+\))?[\w\s]*)/)
|
28
|
+
method_name ? method_name[1] : caller
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|