naught 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.travis.yml +2 -0
  4. data/Changelog.md +6 -0
  5. data/Gemfile +1 -0
  6. data/Guardfile +10 -0
  7. data/README.markdown +413 -0
  8. data/Rakefile +5 -0
  9. data/lib/naught.rb +1 -4
  10. data/lib/naught/null_class_builder.rb +37 -137
  11. data/lib/naught/null_class_builder/command.rb +20 -0
  12. data/lib/naught/null_class_builder/commands.rb +8 -0
  13. data/lib/naught/null_class_builder/commands/define_explicit_conversions.rb +13 -24
  14. data/lib/naught/null_class_builder/commands/define_implicit_conversions.rb +14 -0
  15. data/lib/naught/null_class_builder/commands/impersonate.rb +9 -0
  16. data/lib/naught/null_class_builder/commands/mimic.rb +40 -0
  17. data/lib/naught/null_class_builder/commands/pebble.rb +36 -0
  18. data/lib/naught/null_class_builder/commands/predicates_return.rb +47 -0
  19. data/lib/naught/null_class_builder/commands/singleton.rb +24 -0
  20. data/lib/naught/null_class_builder/commands/traceable.rb +19 -0
  21. data/lib/naught/null_class_builder/conversions_module.rb +57 -0
  22. data/lib/naught/version.rb +1 -1
  23. data/naught.gemspec +2 -1
  24. data/spec/base_object_spec.rb +47 -0
  25. data/spec/basic_null_object_spec.rb +35 -0
  26. data/spec/blackhole_spec.rb +16 -0
  27. data/spec/conversions_spec.rb +20 -0
  28. data/spec/functions/actual_spec.rb +22 -0
  29. data/spec/functions/just_spec.rb +22 -0
  30. data/spec/functions/maybe_spec.rb +35 -0
  31. data/spec/functions/null_spec.rb +34 -0
  32. data/spec/implicit_conversions_spec.rb +25 -0
  33. data/spec/mimic_spec.rb +122 -0
  34. data/spec/naught/null_object_builder/command_spec.rb +10 -0
  35. data/spec/naught/null_object_builder_spec.rb +31 -0
  36. data/spec/naught_spec.rb +77 -411
  37. data/spec/pebble_spec.rb +75 -0
  38. data/spec/predicate_spec.rb +80 -0
  39. data/spec/singleton_null_object_spec.rb +35 -0
  40. data/spec/spec_helper.rb +13 -1
  41. data/spec/support/convertable_null.rb +4 -0
  42. metadata +76 -32
  43. data/.rspec +0 -1
  44. data/README.org +0 -340
data/Rakefile CHANGED
@@ -1 +1,6 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -1,14 +1,11 @@
1
1
  require "naught/version"
2
2
  require 'naught/null_class_builder'
3
- require 'naught/null_class_builder/commands/define_explicit_conversions'
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
- null_equivs = null_equivalents # get a local binding
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
- @operations.each do |operation|
81
- operation.call(generation_mod)
82
- end
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
- class_operations.each do |operation|
93
- operation.call(null_class)
94
- end
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, *args)
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 mimic(class_to_mimic, options={})
160
- include_super = options.fetch(:include_super) { true }
161
- @base_class = root_class_of(class_to_mimic)
162
- @inspect_proc = -> { "<null:#{class_to_mimic}>" }
163
- defer do |subject|
164
- methods = class_to_mimic.instance_methods(include_super) -
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 impersonate(class_to_impersonate, options={})
174
- mimic(class_to_impersonate, options)
175
- @base_class = class_to_impersonate
111
+ def stub_method(subject, name)
112
+ send(@stub_strategy, subject, name)
176
113
  end
177
114
 
178
- def traceable
179
- defer do |subject|
180
- subject.module_eval do
181
- attr_reader :__file__, :__line__
115
+ private
182
116
 
183
- def initialize(options={})
184
- backtrace = options.fetch(:caller) { Kernel.caller(4) }
185
- @__file__, line, _ = backtrace[0].split(':')
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 defer(options={}, &deferred_operation)
193
- if options[:class]
194
- class_operations << deferred_operation
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 singleton
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
- # make local variable to be accessible to Class.new block
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
- command_name = method_name.to_s.
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
- module Commands
5
- class DefineExplicitConversions
6
- def initialize(builder)
7
- @builder = builder
8
- end
9
-
10
- def call
11
- defer do |subject|
12
- subject.module_eval do
13
- def to_s; ""; end
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,9 @@
1
+ module Naught::NullClassBuilder::Commands
2
+ class Impersonate < Naught::NullClassBuilder::Commands::Mimic
3
+ def initialize(builder, class_to_impersonate, options={})
4
+ super
5
+
6
+ builder.base_class = class_to_impersonate
7
+ end
8
+ end
9
+ 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