naught 0.0.1
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/.gitignore +19 -0
- data/.rspec +1 -0
- data/Gemfile +8 -0
- data/Guardfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.org +340 -0
- data/Rakefile +1 -0
- data/bin/autospec +16 -0
- data/bin/coderay +16 -0
- data/bin/guard +16 -0
- data/bin/htmldiff +16 -0
- data/bin/ldiff +16 -0
- data/bin/pry +16 -0
- data/bin/rake +16 -0
- data/bin/rspec +16 -0
- data/bin/thor +16 -0
- data/lib/naught.rb +16 -0
- data/lib/naught/null_class_builder.rb +274 -0
- data/lib/naught/null_class_builder/commands/define_explicit_conversions.rb +30 -0
- data/lib/naught/version.rb +3 -0
- data/naught.gemspec +26 -0
- data/spec/naught_spec.rb +429 -0
- data/spec/spec_helper.rb +1 -0
- metadata +161 -0
data/bin/htmldiff
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'htmldiff' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('diff-lcs', 'htmldiff')
|
data/bin/ldiff
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'ldiff' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('diff-lcs', 'ldiff')
|
data/bin/pry
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'pry' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('pry', 'pry')
|
data/bin/rake
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rake' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rake', 'rake')
|
data/bin/rspec
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'rspec' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('rspec-core', 'rspec')
|
data/bin/thor
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#
|
3
|
+
# This file was generated by Bundler.
|
4
|
+
#
|
5
|
+
# The application 'thor' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'pathname'
|
10
|
+
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
|
11
|
+
Pathname.new(__FILE__).realpath)
|
12
|
+
|
13
|
+
require 'rubygems'
|
14
|
+
require 'bundler/setup'
|
15
|
+
|
16
|
+
load Gem.bin_path('thor', 'thor')
|
data/lib/naught.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "naught/version"
|
2
|
+
require 'naught/null_class_builder'
|
3
|
+
require 'naught/null_class_builder/commands/define_explicit_conversions'
|
4
|
+
|
5
|
+
module Naught
|
6
|
+
def self.build(&customization_block)
|
7
|
+
builder = NullClassBuilder.new
|
8
|
+
builder.customize(&customization_block)
|
9
|
+
unless builder.interface_defined?
|
10
|
+
builder.respond_to_any_message
|
11
|
+
end
|
12
|
+
builder.generate_class
|
13
|
+
end
|
14
|
+
module NullObjectTag
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,274 @@
|
|
1
|
+
module Naught
|
2
|
+
class NullClassBuilder
|
3
|
+
# make sure this module exists
|
4
|
+
module Commands
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@interface_defined = false
|
9
|
+
@base_class = BasicObject
|
10
|
+
@inspect_proc = ->{ "<null>" }
|
11
|
+
@stub_strategy = :stub_method_returning_nil
|
12
|
+
define_basic_methods
|
13
|
+
end
|
14
|
+
|
15
|
+
def interface_defined?
|
16
|
+
@interface_defined
|
17
|
+
end
|
18
|
+
|
19
|
+
def customize(&customization_block)
|
20
|
+
return unless customization_block
|
21
|
+
customization_module.module_exec(self, &customization_block)
|
22
|
+
end
|
23
|
+
|
24
|
+
def customization_module
|
25
|
+
@customization_module ||= Module.new
|
26
|
+
end
|
27
|
+
|
28
|
+
def null_equivalents
|
29
|
+
@null_equivalents ||= [nil]
|
30
|
+
end
|
31
|
+
|
32
|
+
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
|
74
|
+
end
|
75
|
+
|
76
|
+
def generate_class
|
77
|
+
generation_mod = Module.new
|
78
|
+
customization_mod = customization_module # get a local binding
|
79
|
+
builder = self
|
80
|
+
@operations.each do |operation|
|
81
|
+
operation.call(generation_mod)
|
82
|
+
end
|
83
|
+
null_class = Class.new(@base_class) do
|
84
|
+
const_set :GeneratedMethods, generation_mod
|
85
|
+
const_set :Customizations, customization_mod
|
86
|
+
const_set :Conversions, builder.generate_conversions_module(self)
|
87
|
+
|
88
|
+
include NullObjectTag
|
89
|
+
include generation_mod
|
90
|
+
include customization_mod
|
91
|
+
end
|
92
|
+
class_operations.each do |operation|
|
93
|
+
operation.call(null_class)
|
94
|
+
end
|
95
|
+
null_class
|
96
|
+
end
|
97
|
+
|
98
|
+
def method_missing(method_name, *args, &block)
|
99
|
+
command_name = command_name_for_method(method_name)
|
100
|
+
if Commands.const_defined?(command_name)
|
101
|
+
command_class = Commands.const_get(command_name)
|
102
|
+
command_class.new(self, *args, &block).call
|
103
|
+
else
|
104
|
+
super
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def respond_to_missing?(method_name, *args)
|
109
|
+
command_name = command_name_for_method(method_name)
|
110
|
+
Commands.const_defined?(command_name) || super
|
111
|
+
end
|
112
|
+
|
113
|
+
############################################################################
|
114
|
+
# Builder API
|
115
|
+
#
|
116
|
+
# See also the contents of lib/naught/null_class_builder/commands
|
117
|
+
############################################################################
|
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
|
+
|
143
|
+
def black_hole
|
144
|
+
@stub_strategy = :stub_method_returning_self
|
145
|
+
end
|
146
|
+
|
147
|
+
def respond_to_any_message
|
148
|
+
defer do |subject|
|
149
|
+
subject.module_eval do
|
150
|
+
def respond_to?(*)
|
151
|
+
true
|
152
|
+
end
|
153
|
+
end
|
154
|
+
stub_method(subject, :method_missing)
|
155
|
+
end
|
156
|
+
@interface_defined = true
|
157
|
+
end
|
158
|
+
|
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
|
169
|
+
end
|
170
|
+
@interface_defined = true
|
171
|
+
end
|
172
|
+
|
173
|
+
def impersonate(class_to_impersonate, options={})
|
174
|
+
mimic(class_to_impersonate, options)
|
175
|
+
@base_class = class_to_impersonate
|
176
|
+
end
|
177
|
+
|
178
|
+
def traceable
|
179
|
+
defer do |subject|
|
180
|
+
subject.module_eval do
|
181
|
+
attr_reader :__file__, :__line__
|
182
|
+
|
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
|
190
|
+
end
|
191
|
+
|
192
|
+
def defer(options={}, &deferred_operation)
|
193
|
+
if options[:class]
|
194
|
+
class_operations << deferred_operation
|
195
|
+
else
|
196
|
+
operations << deferred_operation
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
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
|
213
|
+
defer do |subject|
|
214
|
+
# make local variable to be accessible to Class.new block
|
215
|
+
inspect_proc = @inspect_proc
|
216
|
+
subject.module_eval do
|
217
|
+
define_method(:inspect, &inspect_proc)
|
218
|
+
def initialize(*)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
defer(class: true) do |subject|
|
223
|
+
subject.module_eval do
|
224
|
+
class << self
|
225
|
+
alias get new
|
226
|
+
end
|
227
|
+
klass = self
|
228
|
+
define_method(:class) { klass }
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
private
|
234
|
+
|
235
|
+
def class_operations
|
236
|
+
@class_operations ||= []
|
237
|
+
end
|
238
|
+
|
239
|
+
def operations
|
240
|
+
@operations ||= []
|
241
|
+
end
|
242
|
+
|
243
|
+
def stub_method(subject, name)
|
244
|
+
send(@stub_strategy, subject, name)
|
245
|
+
end
|
246
|
+
|
247
|
+
def stub_method_returning_nil(subject, name)
|
248
|
+
subject.module_eval do
|
249
|
+
define_method(name) {|*| nil }
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
def stub_method_returning_self(subject, name)
|
254
|
+
subject.module_eval do
|
255
|
+
define_method(name) {|*| self }
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
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
|
271
|
+
end
|
272
|
+
|
273
|
+
end
|
274
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
|
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)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/naught.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'naught/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "naught"
|
8
|
+
spec.version = Naught::VERSION
|
9
|
+
spec.authors = ["Avdi Grimm"]
|
10
|
+
spec.email = ["avdi@avdi.org"]
|
11
|
+
spec.description = %q{Naught is a toolkit for building Null Objects}
|
12
|
+
spec.summary = %q{Naught is a toolkit for building Null Objects}
|
13
|
+
spec.homepage = "https://github.com/avdi/naught"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "rspec"
|
24
|
+
spec.add_development_dependency "guard"
|
25
|
+
spec.add_development_dependency "guard-rspec"
|
26
|
+
end
|