naught 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|