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