naught 1.1.0 → 2.0.0
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 +5 -5
- data/LICENSE.txt +1 -1
- data/lib/naught/basic_object.rb +4 -14
- data/lib/naught/call_location.rb +131 -0
- data/lib/naught/caller_info.rb +128 -0
- data/lib/naught/chain_proxy.rb +51 -0
- data/lib/naught/conversions.rb +108 -34
- data/lib/naught/null_class_builder/command.rb +42 -4
- data/lib/naught/null_class_builder/commands/callstack.rb +89 -0
- data/lib/naught/null_class_builder/commands/define_explicit_conversions.rb +21 -9
- data/lib/naught/null_class_builder/commands/define_implicit_conversions.rb +19 -21
- data/lib/naught/null_class_builder/commands/impersonate.rb +13 -1
- data/lib/naught/null_class_builder/commands/mimic.rb +75 -31
- data/lib/naught/null_class_builder/commands/null_safe_proxy.rb +92 -0
- data/lib/naught/null_class_builder/commands/pebble.rb +21 -17
- data/lib/naught/null_class_builder/commands/predicates_return.rb +42 -24
- data/lib/naught/null_class_builder/commands/singleton.rb +14 -17
- data/lib/naught/null_class_builder/commands/traceable.rb +16 -11
- data/lib/naught/null_class_builder/commands.rb +10 -8
- data/lib/naught/null_class_builder.rb +213 -119
- data/lib/naught/stub_strategy.rb +30 -0
- data/lib/naught/version.rb +3 -1
- data/lib/naught.rb +31 -7
- metadata +34 -66
- data/.gitignore +0 -23
- data/.rspec +0 -2
- data/.rubocop.yml +0 -65
- data/.travis.yml +0 -24
- data/Changelog.md +0 -18
- data/Gemfile +0 -25
- data/Guardfile +0 -15
- data/README.markdown +0 -474
- data/Rakefile +0 -15
- data/naught.gemspec +0 -22
- data/spec/base_object_spec.rb +0 -46
- data/spec/basic_null_object_spec.rb +0 -34
- data/spec/blackhole_spec.rb +0 -14
- data/spec/explicit_conversions_spec.rb +0 -21
- data/spec/functions/actual_spec.rb +0 -22
- data/spec/functions/just_spec.rb +0 -22
- data/spec/functions/maybe_spec.rb +0 -35
- data/spec/functions/null_spec.rb +0 -33
- data/spec/implicit_conversions_spec.rb +0 -28
- data/spec/mimic_spec.rb +0 -123
- data/spec/naught/null_object_builder/command_spec.rb +0 -10
- data/spec/naught/null_object_builder_spec.rb +0 -31
- data/spec/naught_spec.rb +0 -93
- data/spec/pebble_spec.rb +0 -77
- data/spec/predicate_spec.rb +0 -79
- data/spec/singleton_null_object_spec.rb +0 -33
- data/spec/spec_helper.rb +0 -15
- data/spec/support/convertable_null.rb +0 -4
- data/spec/support/jruby.rb +0 -3
- data/spec/support/rubinius.rb +0 -3
- data/spec/support/ruby_18.rb +0 -3
|
@@ -1,17 +1,29 @@
|
|
|
1
|
-
require
|
|
2
|
-
require 'naught/null_class_builder/command'
|
|
1
|
+
require "naught/null_class_builder/command"
|
|
3
2
|
|
|
4
3
|
module Naught
|
|
5
4
|
class NullClassBuilder
|
|
6
5
|
module Commands
|
|
7
|
-
|
|
6
|
+
# Adds explicit conversion methods delegating to nil
|
|
7
|
+
#
|
|
8
|
+
# These methods return the same values that nil returns:
|
|
9
|
+
# - to_a => []
|
|
10
|
+
# - to_c => (0+0i)
|
|
11
|
+
# - to_f => 0.0
|
|
12
|
+
# - to_h => {}
|
|
13
|
+
# - to_i => 0
|
|
14
|
+
# - to_r => (0/1)
|
|
15
|
+
# - to_s => ""
|
|
16
|
+
#
|
|
17
|
+
# @api private
|
|
18
|
+
class DefineExplicitConversions < Command
|
|
19
|
+
METHODS = %i[to_a to_c to_f to_h to_i to_r to_s].freeze
|
|
20
|
+
private_constant :METHODS
|
|
21
|
+
|
|
22
|
+
# Install explicit conversion methods
|
|
23
|
+
# @return [void]
|
|
24
|
+
# @api private
|
|
8
25
|
def call
|
|
9
|
-
defer
|
|
10
|
-
subject.module_eval do
|
|
11
|
-
extend Forwardable
|
|
12
|
-
def_delegators :nil, :to_a, :to_c, :to_f, :to_h, :to_i, :to_r, :to_s
|
|
13
|
-
end
|
|
14
|
-
end
|
|
26
|
+
defer { |subject| METHODS.each { |name| subject.define_method(name) { nil.public_send(name) } } }
|
|
15
27
|
end
|
|
16
28
|
end
|
|
17
29
|
end
|
|
@@ -1,29 +1,27 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "naught/null_class_builder/command"
|
|
2
2
|
|
|
3
3
|
module Naught
|
|
4
4
|
class NullClassBuilder
|
|
5
5
|
module Commands
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
# Adds implicit conversion methods to the null class
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
class DefineImplicitConversions < Command
|
|
10
|
+
EMPTY_ARRAY = [] #: Array[untyped]
|
|
11
|
+
EMPTY_HASH = {} #: Hash[untyped, untyped]
|
|
12
|
+
RETURN_VALUES = {
|
|
13
|
+
to_ary: EMPTY_ARRAY.freeze,
|
|
14
|
+
to_hash: EMPTY_HASH.freeze,
|
|
15
|
+
to_int: 0,
|
|
16
|
+
to_str: "".freeze
|
|
17
|
+
}.freeze
|
|
18
|
+
private_constant :EMPTY_ARRAY, :EMPTY_HASH, :RETURN_VALUES
|
|
17
19
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
''
|
|
24
|
-
end
|
|
25
|
-
end
|
|
26
|
-
end
|
|
20
|
+
# Install implicit conversion methods
|
|
21
|
+
# @return [void]
|
|
22
|
+
# @api private
|
|
23
|
+
def call
|
|
24
|
+
defer { |subject| RETURN_VALUES.each { |name, value| subject.define_method(name) { value } } }
|
|
27
25
|
end
|
|
28
26
|
end
|
|
29
27
|
end
|
|
@@ -1,7 +1,19 @@
|
|
|
1
1
|
module Naught
|
|
2
2
|
class NullClassBuilder
|
|
3
3
|
module Commands
|
|
4
|
-
class
|
|
4
|
+
# Build a null class that impersonates a given class
|
|
5
|
+
#
|
|
6
|
+
# Unlike Mimic, Impersonate makes the null class inherit from the target,
|
|
7
|
+
# so `is_a?` checks will pass.
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
class Impersonate < Mimic
|
|
11
|
+
# Create an impersonate command for a class
|
|
12
|
+
#
|
|
13
|
+
# @param builder [NullClassBuilder]
|
|
14
|
+
# @param class_to_impersonate [Class]
|
|
15
|
+
# @param options [Hash]
|
|
16
|
+
# @api private
|
|
5
17
|
def initialize(builder, class_to_impersonate, options = {})
|
|
6
18
|
super
|
|
7
19
|
builder.base_class = class_to_impersonate
|
|
@@ -1,53 +1,97 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
1
|
+
require "naught/basic_object"
|
|
2
|
+
require "naught/null_class_builder/command"
|
|
3
3
|
|
|
4
4
|
module Naught
|
|
5
5
|
class NullClassBuilder
|
|
6
6
|
module Commands
|
|
7
|
-
class
|
|
8
|
-
|
|
7
|
+
# Build a null class that mimics an existing class or instance
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
class Mimic < Command
|
|
11
|
+
# Methods that should never be mimicked as they interfere with
|
|
12
|
+
# other Naught features like predicates_return
|
|
13
|
+
# @see https://github.com/avdi/naught/issues/55
|
|
14
|
+
METHODS_TO_SKIP = (%i[method_missing respond_to? respond_to_missing?] + Object.instance_methods).freeze
|
|
15
|
+
private_constant :METHODS_TO_SKIP
|
|
9
16
|
|
|
10
|
-
|
|
17
|
+
# Singleton class placeholder used when no instance is provided
|
|
18
|
+
NULL_SINGLETON_CLASS = Object.new.singleton_class.freeze
|
|
19
|
+
private_constant :NULL_SINGLETON_CLASS
|
|
11
20
|
|
|
21
|
+
# The class being mimicked by the null object
|
|
22
|
+
# @return [Class] class being mimicked
|
|
23
|
+
attr_reader :class_to_mimic
|
|
24
|
+
|
|
25
|
+
# Whether to include superclass methods when mimicking
|
|
26
|
+
# @return [Boolean] whether to include superclass methods
|
|
27
|
+
attr_reader :include_super
|
|
28
|
+
|
|
29
|
+
# The singleton class being mimicked (for instance-based mimicking)
|
|
30
|
+
# @return [Class] singleton class being mimicked
|
|
31
|
+
attr_reader :singleton_class
|
|
32
|
+
|
|
33
|
+
# Create a mimic command for a class or instance
|
|
34
|
+
#
|
|
35
|
+
# @param builder [NullClassBuilder]
|
|
36
|
+
# @param class_to_mimic_or_options [Class, Hash]
|
|
37
|
+
# @param options [Hash]
|
|
38
|
+
# @api private
|
|
12
39
|
def initialize(builder, class_to_mimic_or_options, options = {})
|
|
13
40
|
super(builder)
|
|
41
|
+
parse_arguments(class_to_mimic_or_options, options)
|
|
42
|
+
configure_builder
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Install stubbed methods from the target class or instance
|
|
46
|
+
#
|
|
47
|
+
# @return [void]
|
|
48
|
+
# @api private
|
|
49
|
+
def call
|
|
50
|
+
defer { |subject| methods_to_stub.each { |name| builder.stub_method(subject, name) } }
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
14
54
|
|
|
55
|
+
# Parse the arguments to determine what to mimic
|
|
56
|
+
#
|
|
57
|
+
# @param class_to_mimic_or_options [Class, Hash] class or options hash
|
|
58
|
+
# @param options [Hash] additional options
|
|
59
|
+
# @return [void]
|
|
60
|
+
def parse_arguments(class_to_mimic_or_options, options)
|
|
15
61
|
if class_to_mimic_or_options.is_a?(Hash)
|
|
16
|
-
options
|
|
17
|
-
instance
|
|
18
|
-
@singleton_class =
|
|
19
|
-
@class_to_mimic
|
|
62
|
+
options = class_to_mimic_or_options.merge(options)
|
|
63
|
+
instance = options.fetch(:example)
|
|
64
|
+
@singleton_class = instance.singleton_class
|
|
65
|
+
@class_to_mimic = instance.class
|
|
20
66
|
else
|
|
21
67
|
@singleton_class = NULL_SINGLETON_CLASS
|
|
22
|
-
@class_to_mimic
|
|
68
|
+
@class_to_mimic = class_to_mimic_or_options
|
|
23
69
|
end
|
|
24
|
-
@include_super = options.fetch(:include_super
|
|
25
|
-
|
|
26
|
-
builder.base_class = root_class_of(@class_to_mimic)
|
|
27
|
-
class_to_mimic = @class_to_mimic
|
|
28
|
-
builder.inspect_proc = lambda { "<null:#{class_to_mimic}>" }
|
|
29
|
-
builder.interface_defined = true
|
|
70
|
+
@include_super = options.fetch(:include_super, true)
|
|
30
71
|
end
|
|
31
72
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
73
|
+
# Configure the builder with the mimicked class's properties
|
|
74
|
+
#
|
|
75
|
+
# @return [void]
|
|
76
|
+
def configure_builder
|
|
77
|
+
builder.base_class = root_class_of(class_to_mimic)
|
|
78
|
+
klass = class_to_mimic
|
|
79
|
+
builder.inspect_proc = -> { "<null:#{klass}>" }
|
|
80
|
+
builder.interface_defined = true
|
|
38
81
|
end
|
|
39
82
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
83
|
+
# Determine the root class to inherit from
|
|
84
|
+
#
|
|
85
|
+
# @param klass [Class] the class to analyze
|
|
86
|
+
# @return [Class] Object or Naught::BasicObject
|
|
87
|
+
def root_class_of(klass) = klass.ancestors.include?(Object) ? Object : Naught::BasicObject
|
|
45
88
|
|
|
89
|
+
# Compute the list of methods to stub on the null object
|
|
90
|
+
#
|
|
91
|
+
# @return [Array<Symbol>] methods to stub
|
|
46
92
|
def methods_to_stub
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
singleton_class.instance_methods(false)
|
|
50
|
-
methods_to_mimic - Object.instance_methods
|
|
93
|
+
all_methods = class_to_mimic.instance_methods(include_super) | singleton_class.instance_methods(false)
|
|
94
|
+
all_methods - METHODS_TO_SKIP
|
|
51
95
|
end
|
|
52
96
|
end
|
|
53
97
|
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
require "naught/null_class_builder/command"
|
|
2
|
+
|
|
3
|
+
module Naught
|
|
4
|
+
class NullClassBuilder
|
|
5
|
+
module Commands
|
|
6
|
+
# Enables null-safe proxy wrapping via the NullSafe() conversion function
|
|
7
|
+
#
|
|
8
|
+
# When enabled, the generated null class gains a NullSafe() function that
|
|
9
|
+
# wraps any value in a proxy. The proxy intercepts all method calls and
|
|
10
|
+
# wraps return values, replacing nil with the null object.
|
|
11
|
+
#
|
|
12
|
+
# @example Enable null-safe proxy
|
|
13
|
+
# NullObject = Naught.build do |config|
|
|
14
|
+
# config.null_safe_proxy
|
|
15
|
+
# end
|
|
16
|
+
#
|
|
17
|
+
# include NullObject::Conversions
|
|
18
|
+
#
|
|
19
|
+
# user = nil
|
|
20
|
+
# NullSafe(user).name.upcase # => <null>
|
|
21
|
+
#
|
|
22
|
+
# user = OpenStruct.new(name: nil)
|
|
23
|
+
# NullSafe(user).name.upcase # => <null>
|
|
24
|
+
#
|
|
25
|
+
# user = OpenStruct.new(name: "Bob")
|
|
26
|
+
# NullSafe(user).name.upcase # => "BOB"
|
|
27
|
+
#
|
|
28
|
+
# @api private
|
|
29
|
+
class NullSafeProxy < Command
|
|
30
|
+
# Install the NullSafe conversion function
|
|
31
|
+
# @return [void]
|
|
32
|
+
# @api private
|
|
33
|
+
def call
|
|
34
|
+
null_equivs = builder.null_equivalents
|
|
35
|
+
defer_class do |null_class|
|
|
36
|
+
proxy_class = build_proxy_class(null_class, null_equivs)
|
|
37
|
+
null_class.const_set(:NullSafeProxy, proxy_class)
|
|
38
|
+
install_null_safe_conversion(null_class, proxy_class, null_equivs)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
# Build the proxy class that wraps objects for null-safe access
|
|
45
|
+
#
|
|
46
|
+
# @param null_class [Class] the null object class
|
|
47
|
+
# @param null_equivs [Array<Object>] values treated as null-equivalent
|
|
48
|
+
# @return [Class] the proxy class
|
|
49
|
+
# @api private
|
|
50
|
+
def build_proxy_class(null_class, null_equivs)
|
|
51
|
+
Class.new(::Naught::BasicObject) do
|
|
52
|
+
include ::Naught::NullSafeProxyTag
|
|
53
|
+
|
|
54
|
+
define_method(:initialize) { |target| @target = target }
|
|
55
|
+
define_method(:__target__) { @target }
|
|
56
|
+
define_method(:respond_to?) { |method_name, include_private = false| @target.respond_to?(method_name, include_private) }
|
|
57
|
+
define_method(:inspect) { "<null-safe-proxy(#{@target.inspect})>" }
|
|
58
|
+
|
|
59
|
+
define_method(:method_missing) do |method_name, *args, &block|
|
|
60
|
+
result = @target.__send__(method_name, *args, &block)
|
|
61
|
+
case result
|
|
62
|
+
when ::Naught::NullObjectTag then result
|
|
63
|
+
when *null_equivs then null_class.get
|
|
64
|
+
else self.class.new(result)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
klass = self
|
|
69
|
+
define_method(:class) { klass }
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# Install the NullSafe conversion method on the Conversions module
|
|
74
|
+
#
|
|
75
|
+
# @param null_class [Class] the null object class
|
|
76
|
+
# @param proxy_class [Class] the proxy class
|
|
77
|
+
# @param null_equivs [Array<Object>] values treated as null-equivalent
|
|
78
|
+
# @return [void]
|
|
79
|
+
# @api private
|
|
80
|
+
def install_null_safe_conversion(null_class, proxy_class, null_equivs)
|
|
81
|
+
null_class.const_get(:Conversions).define_method(:NullSafe) do |object|
|
|
82
|
+
case object
|
|
83
|
+
when ::Naught::NullObjectTag then object
|
|
84
|
+
when *null_equivs then null_class.get
|
|
85
|
+
else proxy_class.new(object)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -1,29 +1,33 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "naught/null_class_builder/command"
|
|
2
2
|
|
|
3
3
|
module Naught
|
|
4
4
|
class NullClassBuilder
|
|
5
5
|
module Commands
|
|
6
|
-
|
|
6
|
+
# Logs missing method calls and their call sites
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
class Pebble < Command
|
|
10
|
+
# Create a pebble command with optional output stream
|
|
11
|
+
#
|
|
12
|
+
# @param builder [NullClassBuilder]
|
|
13
|
+
# @param output [#puts] output stream for log lines
|
|
14
|
+
# @api private
|
|
7
15
|
def initialize(builder, output = $stdout)
|
|
8
|
-
|
|
16
|
+
super(builder)
|
|
9
17
|
@output = output
|
|
10
18
|
end
|
|
11
19
|
|
|
20
|
+
# Install the logging method_missing hook
|
|
21
|
+
# @return [void]
|
|
22
|
+
# @api private
|
|
12
23
|
def call
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
def parse_caller
|
|
22
|
-
caller = Kernel.caller(2).first
|
|
23
|
-
method_name = caller.match(/\`([\w\s]+(\(\d+\s\w+\))?[\w\s]*)/)
|
|
24
|
-
method_name ? method_name[1] : caller
|
|
25
|
-
end
|
|
26
|
-
private :parse_caller
|
|
24
|
+
output = @output
|
|
25
|
+
defer_prepend_module do
|
|
26
|
+
define_method(:method_missing) do |method_name, *args|
|
|
27
|
+
pretty_args = args.map(&:inspect).join(", ").tr('"', "'")
|
|
28
|
+
caller_desc = Naught::CallerInfo.format_caller_for_pebble(Kernel.caller(1))
|
|
29
|
+
output.puts "#{method_name}(#{pretty_args}) from #{caller_desc}"
|
|
30
|
+
self
|
|
27
31
|
end
|
|
28
32
|
end
|
|
29
33
|
end
|
|
@@ -1,45 +1,63 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "naught/null_class_builder/command"
|
|
2
2
|
|
|
3
3
|
module Naught
|
|
4
4
|
class NullClassBuilder
|
|
5
5
|
module Commands
|
|
6
|
-
|
|
6
|
+
# Overrides predicate methods to return a fixed value
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
class PredicatesReturn < Command
|
|
10
|
+
# Create a predicates_return command with the given value
|
|
11
|
+
#
|
|
12
|
+
# @param builder [NullClassBuilder]
|
|
13
|
+
# @param return_value [Object] value to return for predicate methods
|
|
14
|
+
# @api private
|
|
7
15
|
def initialize(builder, return_value)
|
|
8
16
|
super(builder)
|
|
9
|
-
@
|
|
17
|
+
@return_value = return_value
|
|
10
18
|
end
|
|
11
19
|
|
|
20
|
+
# Apply predicate overrides
|
|
21
|
+
# @return [void]
|
|
22
|
+
# @api private
|
|
12
23
|
def call
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
define_predicate_methods(subject)
|
|
16
|
-
end
|
|
24
|
+
install_method_missing_override
|
|
25
|
+
install_predicate_method_overrides
|
|
17
26
|
end
|
|
18
27
|
|
|
19
|
-
|
|
28
|
+
private
|
|
20
29
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
30
|
+
# Install method_missing override for predicate methods
|
|
31
|
+
# @return [void]
|
|
32
|
+
# @api private
|
|
33
|
+
def install_method_missing_override
|
|
34
|
+
return_value = @return_value
|
|
35
|
+
defer_prepend_module do
|
|
25
36
|
define_method(:method_missing) do |method_name, *args, &block|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
method_name.to_s.end_with?("?") ? return_value : super(method_name, *args, &block)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
define_method(:respond_to?) do |method_name, include_private = false|
|
|
41
|
+
method_name.to_s.end_with?("?") || super(method_name, include_private)
|
|
31
42
|
end
|
|
32
43
|
end
|
|
33
44
|
end
|
|
34
45
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
# Override existing predicate methods to return the configured value
|
|
47
|
+
# @return [void]
|
|
48
|
+
# @api private
|
|
49
|
+
def install_predicate_method_overrides
|
|
50
|
+
return_value = @return_value
|
|
51
|
+
defer do |subject|
|
|
52
|
+
predicate_methods = subject.instance_methods.select do |name|
|
|
53
|
+
name.to_s.end_with?("?") && name != :respond_to?
|
|
54
|
+
end
|
|
55
|
+
next if predicate_methods.empty?
|
|
56
|
+
|
|
57
|
+
predicate_mod = Module.new do
|
|
58
|
+
predicate_methods.each { |name| define_method(name) { |*| return_value } }
|
|
42
59
|
end
|
|
60
|
+
subject.prepend(predicate_mod)
|
|
43
61
|
end
|
|
44
62
|
end
|
|
45
63
|
end
|
|
@@ -1,25 +1,22 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "singleton"
|
|
2
|
+
require "naught/null_class_builder/command"
|
|
2
3
|
|
|
3
4
|
module Naught
|
|
4
5
|
class NullClassBuilder
|
|
5
6
|
module Commands
|
|
6
|
-
class
|
|
7
|
+
# Turns the null class into a Singleton
|
|
8
|
+
#
|
|
9
|
+
# @api private
|
|
10
|
+
class Singleton < Command
|
|
11
|
+
# Install Singleton behavior on the null class
|
|
12
|
+
# @return [void]
|
|
13
|
+
# @api private
|
|
7
14
|
def call
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
def self.get(*)
|
|
14
|
-
instance
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
%w(dup clone).each do |method_name|
|
|
18
|
-
define_method method_name do
|
|
19
|
-
self
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
15
|
+
defer_class do |klass|
|
|
16
|
+
klass.include(::Singleton)
|
|
17
|
+
klass.singleton_class.undef_method(:get)
|
|
18
|
+
klass.define_singleton_method(:get) { |*| instance }
|
|
19
|
+
%i[dup clone].each { |name| klass.define_method(name) { self } }
|
|
23
20
|
end
|
|
24
21
|
end
|
|
25
22
|
end
|
|
@@ -1,20 +1,25 @@
|
|
|
1
|
-
require
|
|
1
|
+
require "naught/null_class_builder/command"
|
|
2
2
|
|
|
3
3
|
module Naught
|
|
4
4
|
class NullClassBuilder
|
|
5
5
|
module Commands
|
|
6
|
-
|
|
6
|
+
# Records the source location where a null object was created
|
|
7
|
+
#
|
|
8
|
+
# @api private
|
|
9
|
+
class Traceable < Command
|
|
10
|
+
# Install the traceable initializer
|
|
11
|
+
# @return [void]
|
|
12
|
+
# @api private
|
|
7
13
|
def call
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
attr_reader :__file__, :__line__
|
|
14
|
+
defer_prepend_module do
|
|
15
|
+
attr_reader :__file__, :__line__
|
|
11
16
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
17
|
+
define_method(:initialize) do |options = {}|
|
|
18
|
+
backtrace = options.fetch(:caller) { Kernel.caller(3) }
|
|
19
|
+
caller_data = Naught::CallerInfo.parse(backtrace[0])
|
|
20
|
+
@__file__ = caller_data[:path]
|
|
21
|
+
@__line__ = caller_data[:lineno]
|
|
22
|
+
super(options)
|
|
18
23
|
end
|
|
19
24
|
end
|
|
20
25
|
end
|
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
require
|
|
2
|
-
require
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
7
|
-
require
|
|
8
|
-
require
|
|
1
|
+
require "naught/null_class_builder/commands/callstack"
|
|
2
|
+
require "naught/null_class_builder/commands/define_explicit_conversions"
|
|
3
|
+
require "naught/null_class_builder/commands/define_implicit_conversions"
|
|
4
|
+
require "naught/null_class_builder/commands/null_safe_proxy"
|
|
5
|
+
require "naught/null_class_builder/commands/pebble"
|
|
6
|
+
require "naught/null_class_builder/commands/predicates_return"
|
|
7
|
+
require "naught/null_class_builder/commands/singleton"
|
|
8
|
+
require "naught/null_class_builder/commands/traceable"
|
|
9
|
+
require "naught/null_class_builder/commands/mimic"
|
|
10
|
+
require "naught/null_class_builder/commands/impersonate"
|