rr 0.10.11 → 1.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.
- data/CHANGES +5 -0
- data/Gemfile +6 -0
- data/README.rdoc +28 -2
- data/VERSION.yml +3 -3
- data/lib/rr.rb +23 -17
- data/lib/rr/adapters/rr_methods.rb +43 -17
- data/lib/rr/blank_slate.rb +2 -2
- data/lib/rr/class_instance_method_defined.rb +9 -0
- data/lib/rr/double.rb +2 -2
- data/lib/rr/double_definitions/double_definition.rb +29 -16
- data/lib/rr/double_definitions/double_definition_create.rb +52 -86
- data/lib/rr/double_definitions/double_injections/any_instance_of.rb +28 -0
- data/lib/rr/double_definitions/double_injections/instance.rb +16 -0
- data/lib/rr/double_definitions/double_injections/new_instance_of.rb +53 -0
- data/lib/rr/double_definitions/strategies/double_injection/any_instance_of.rb +31 -0
- data/lib/rr/double_definitions/strategies/double_injection/double_injection_strategy.rb +10 -0
- data/lib/rr/double_definitions/strategies/double_injection/instance.rb +17 -0
- data/lib/rr/double_definitions/strategies/double_injection/new_instance_of.rb +37 -0
- data/lib/rr/double_definitions/strategies/implementation/implementation_strategy.rb +0 -5
- data/lib/rr/double_definitions/strategies/implementation/proxy.rb +0 -2
- data/lib/rr/double_definitions/strategies/implementation/strongly_typed_reimplementation.rb +0 -2
- data/lib/rr/double_definitions/strategies/strategy.rb +0 -27
- data/lib/rr/double_definitions/strategies/strategy_methods.rb +53 -0
- data/lib/rr/double_definitions/strategies/verification/dont_allow.rb +0 -2
- data/lib/rr/double_definitions/strategies/verification/mock.rb +0 -2
- data/lib/rr/double_definitions/strategies/verification/stub.rb +0 -2
- data/lib/rr/double_definitions/strategies/verification/verification_strategy.rb +0 -5
- data/lib/rr/hash_with_object_id_key.rb +4 -0
- data/lib/rr/injections/double_injection.rb +94 -71
- data/lib/rr/injections/injection.rb +8 -10
- data/lib/rr/injections/method_missing_injection.rb +13 -20
- data/lib/rr/injections/singleton_method_added_injection.rb +19 -17
- data/lib/rr/method_dispatches/base_method_dispatch.rb +1 -1
- data/lib/rr/method_dispatches/method_dispatch.rb +4 -4
- data/lib/rr/method_dispatches/method_missing_dispatch.rb +17 -14
- data/lib/rr/recorded_calls.rb +1 -1
- data/lib/rr/space.rb +6 -6
- data/lib/rr/times_called_matchers/times_called_matcher.rb +2 -2
- data/scratch.rb +118 -0
- data/spec/api/any_instance_of/all_instances_of_spec.rb +14 -0
- data/spec/api/any_instance_of/any_instance_of_spec.rb +47 -0
- data/spec/api/mock/mock_spec.rb +2 -2
- data/spec/api/new_instance_of/instance_of_spec.rb +15 -0
- data/spec/api/new_instance_of/new_instance_of_spec.rb +61 -0
- data/spec/environment_fixture_setup.rb +3 -2
- data/spec/rr/adapters/rr_methods_space_spec.rb +8 -10
- data/spec/rr/double_definitions/child_double_definition_creator_spec.rb +1 -1
- data/spec/rr/double_definitions/double_definition_create_blank_slate_spec.rb +6 -2
- data/spec/rr/double_definitions/double_definition_create_spec.rb +1 -52
- data/spec/rr/double_injection/double_injection_verify_spec.rb +1 -1
- data/spec/rr/rspec/rspec_adapter_spec.rb +5 -5
- data/spec/rr/space/space_spec.rb +58 -67
- data/spec/spec_helper.rb +2 -2
- metadata +33 -9
- data/lib/rr/double_definitions/strategies/scope/instance.rb +0 -15
- data/lib/rr/double_definitions/strategies/scope/instance_of_class.rb +0 -50
- data/lib/rr/double_definitions/strategies/scope/scope_strategy.rb +0 -15
@@ -1,29 +1,27 @@
|
|
1
1
|
module RR
|
2
2
|
module Injections
|
3
3
|
class Injection
|
4
|
-
|
4
|
+
extend(Module.new do
|
5
5
|
def instances
|
6
6
|
@instances ||= HashWithObjectIdKey.new
|
7
7
|
end
|
8
|
-
end
|
8
|
+
end)
|
9
9
|
|
10
10
|
include Space::Reader
|
11
11
|
|
12
|
-
attr_reader :subject
|
13
|
-
|
14
12
|
def subject_has_method_defined?(method_name_in_question)
|
15
|
-
|
16
|
-
@subject.protected_methods.detect {|method_name| method_name.to_sym == method_name_in_question.to_sym} ||
|
17
|
-
@subject.private_methods.detect {|method_name| method_name.to_sym == method_name_in_question.to_sym}
|
13
|
+
ClassInstanceMethodDefined.call(subject_class, method_name_in_question)
|
18
14
|
end
|
19
15
|
|
20
16
|
def subject_has_original_method?
|
21
|
-
|
17
|
+
subject_has_method_defined?(original_method_alias_name)
|
22
18
|
end
|
23
19
|
|
24
20
|
protected
|
25
|
-
def subject_respond_to_method?(method_name)
|
26
|
-
subject_has_method_defined?(method_name) ||
|
21
|
+
def subject_respond_to_method?(subject, method_name)
|
22
|
+
subject_has_method_defined?(method_name) ||
|
23
|
+
ClassInstanceMethodDefined.call(subject_class, :respond_to?) &&
|
24
|
+
subject.respond_to?(method_name)
|
27
25
|
end
|
28
26
|
end
|
29
27
|
end
|
@@ -1,26 +1,27 @@
|
|
1
1
|
module RR
|
2
2
|
module Injections
|
3
3
|
class MethodMissingInjection < Injection
|
4
|
-
|
5
|
-
def
|
6
|
-
instances[
|
7
|
-
new(
|
4
|
+
extend(Module.new do
|
5
|
+
def find_or_create(subject_class)
|
6
|
+
instances[subject_class] ||= begin
|
7
|
+
new(subject_class).bind
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
11
|
def exists?(subject)
|
12
12
|
instances.include?(subject)
|
13
13
|
end
|
14
|
-
end
|
14
|
+
end)
|
15
15
|
|
16
|
-
|
17
|
-
|
16
|
+
attr_reader :subject_class
|
17
|
+
def initialize(subject_class)
|
18
|
+
@subject_class = subject_class
|
18
19
|
@placeholder_method_defined = false
|
19
20
|
end
|
20
21
|
|
21
22
|
def bind
|
22
|
-
unless
|
23
|
-
unless
|
23
|
+
unless ClassInstanceMethodDefined.call(subject_class, original_method_alias_name)
|
24
|
+
unless ClassInstanceMethodDefined.call(subject_class, :method_missing)
|
24
25
|
@placeholder_method_defined = true
|
25
26
|
subject_class.class_eval do
|
26
27
|
def method_missing(method_name, *args, &block)
|
@@ -48,22 +49,14 @@ module RR
|
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
51
|
-
def dispatch_method(method_name, args, block)
|
52
|
-
MethodDispatches::MethodMissingDispatch.new(subject, method_name, args, block).call
|
53
|
-
end
|
54
|
-
|
55
52
|
protected
|
56
|
-
def subject_class
|
57
|
-
class << subject; self; end
|
58
|
-
end
|
59
|
-
|
60
53
|
def bind_method
|
61
|
-
|
54
|
+
subject_class_object_id = subject_class.object_id
|
55
|
+
subject_class.class_eval((<<-METHOD), __FILE__, __LINE__ + 1)
|
62
56
|
def method_missing(method_name, *args, &block)
|
63
|
-
|
57
|
+
MethodDispatches::MethodMissingDispatch.new(self, ObjectSpace._id2ref(#{subject_class_object_id}), method_name, args, block).call
|
64
58
|
end
|
65
59
|
METHOD
|
66
|
-
subject_class.class_eval(returns_method, __FILE__, __LINE__ - 4)
|
67
60
|
end
|
68
61
|
|
69
62
|
def original_method_alias_name
|
@@ -1,26 +1,31 @@
|
|
1
1
|
module RR
|
2
2
|
module Injections
|
3
3
|
class SingletonMethodAddedInjection < Injection
|
4
|
-
|
5
|
-
def
|
6
|
-
instances[
|
7
|
-
new(
|
4
|
+
extend(Module.new do
|
5
|
+
def find_or_create(subject_class)
|
6
|
+
instances[subject_class] ||= begin
|
7
|
+
new(subject_class).bind
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
+
def find(subject)
|
12
|
+
instances[subject]
|
13
|
+
end
|
14
|
+
|
11
15
|
def exists?(subject)
|
12
16
|
instances.include?(subject)
|
13
17
|
end
|
14
|
-
end
|
18
|
+
end)
|
15
19
|
|
16
|
-
|
17
|
-
|
20
|
+
attr_reader :subject_class
|
21
|
+
def initialize(subject_class)
|
22
|
+
@subject_class = subject_class
|
18
23
|
@placeholder_method_defined = false
|
19
24
|
end
|
20
25
|
|
21
26
|
def bind
|
22
|
-
unless
|
23
|
-
unless
|
27
|
+
unless ClassInstanceMethodDefined.call(subject_class, original_method_alias_name, false)
|
28
|
+
unless ClassInstanceMethodDefined.call(subject_class, :singleton_method_added, false)
|
24
29
|
@placeholder_method_defined = true
|
25
30
|
subject_class.class_eval do
|
26
31
|
def singleton_method_added(method_name)
|
@@ -29,14 +34,15 @@ module RR
|
|
29
34
|
end
|
30
35
|
end
|
31
36
|
|
32
|
-
memoized_subject = subject
|
33
37
|
memoized_original_method_alias_name = original_method_alias_name
|
34
38
|
subject_class.__send__(:alias_method, original_method_alias_name, :singleton_method_added)
|
39
|
+
memoized_subject_class = subject_class
|
40
|
+
memoized_original_method_alias_name = original_method_alias_name
|
35
41
|
subject_class.__send__(:define_method, :singleton_method_added) do |method_name_arg|
|
36
|
-
if Injections::DoubleInjection.exists?(
|
37
|
-
Injections::DoubleInjection.
|
42
|
+
if Injections::DoubleInjection.exists?(memoized_subject_class, method_name_arg)
|
43
|
+
Injections::DoubleInjection.find_or_create(memoized_subject_class, method_name_arg).send(:deferred_bind_method)
|
38
44
|
end
|
39
|
-
|
45
|
+
__send__(memoized_original_method_alias_name, method_name_arg)
|
40
46
|
end
|
41
47
|
end
|
42
48
|
self
|
@@ -57,10 +63,6 @@ module RR
|
|
57
63
|
end
|
58
64
|
|
59
65
|
protected
|
60
|
-
def subject_class
|
61
|
-
class << subject; self; end
|
62
|
-
end
|
63
|
-
|
64
66
|
def original_method_alias_name
|
65
67
|
"__rr__original_singleton_method_added"
|
66
68
|
end
|
@@ -48,7 +48,7 @@ module RR
|
|
48
48
|
end
|
49
49
|
|
50
50
|
def call_original_method_missing
|
51
|
-
subject.__send__(original_method_missing_alias_name, method_name, *args, &block)
|
51
|
+
subject.__send__(MethodMissingDispatch.original_method_missing_alias_name, method_name, *args, &block)
|
52
52
|
end
|
53
53
|
|
54
54
|
def implementation_is_original_method?
|
@@ -1,9 +1,9 @@
|
|
1
1
|
module RR
|
2
2
|
module MethodDispatches
|
3
3
|
class MethodDispatch < BaseMethodDispatch
|
4
|
-
attr_reader :double_injection
|
5
|
-
def initialize(double_injection, args, block)
|
6
|
-
@double_injection, @args, @block = double_injection, args, block
|
4
|
+
attr_reader :double_injection, :subject
|
5
|
+
def initialize(double_injection, subject, args, block)
|
6
|
+
@double_injection, @subject, @args, @block = double_injection, subject, args, block
|
7
7
|
@double = find_double_to_attempt
|
8
8
|
end
|
9
9
|
|
@@ -53,7 +53,7 @@ module RR
|
|
53
53
|
end
|
54
54
|
|
55
55
|
def_delegators :definition, :implementation
|
56
|
-
def_delegators :double_injection, :subject_has_original_method?, :subject_has_original_method_missing?, :
|
56
|
+
def_delegators :double_injection, :subject_has_original_method?, :subject_has_original_method_missing?, :method_name, :original_method_alias_name
|
57
57
|
end
|
58
58
|
end
|
59
59
|
end
|
@@ -1,24 +1,21 @@
|
|
1
1
|
module RR
|
2
2
|
module MethodDispatches
|
3
3
|
class MethodMissingDispatch < BaseMethodDispatch
|
4
|
-
|
4
|
+
extend(Module.new do
|
5
5
|
def original_method_missing_alias_name
|
6
6
|
"__rr__original_method_missing"
|
7
7
|
end
|
8
|
-
end
|
8
|
+
end)
|
9
9
|
|
10
|
-
attr_reader :subject, :method_name
|
11
|
-
def initialize(subject, method_name, args, block)
|
12
|
-
@subject, @method_name, @args, @block = subject, method_name, args, block
|
10
|
+
attr_reader :subject, :subject_class, :method_name
|
11
|
+
def initialize(subject, subject_class, method_name, args, block)
|
12
|
+
@subject, @subject_class, @method_name, @args, @block = subject, subject_class, method_name, args, block
|
13
13
|
end
|
14
14
|
|
15
15
|
def call
|
16
|
-
if Injections::DoubleInjection.exists?(
|
17
|
-
space.record_call(subject, method_name, args, block)
|
16
|
+
if Injections::DoubleInjection.exists?(subject_class, method_name)
|
18
17
|
@double = find_double_to_attempt
|
19
|
-
|
20
18
|
if double
|
21
|
-
double.method_call(args)
|
22
19
|
call_yields
|
23
20
|
return_value = extract_subject_from_return_value(call_implementation)
|
24
21
|
if after_call_proc
|
@@ -35,7 +32,7 @@ module RR
|
|
35
32
|
end
|
36
33
|
|
37
34
|
def call_original_method
|
38
|
-
|
35
|
+
Injections::DoubleInjection.find_or_create(subject_class, method_name).dispatch_method_delegates_to_dispatch_original_method do
|
39
36
|
call_original_method_missing
|
40
37
|
end
|
41
38
|
end
|
@@ -43,17 +40,23 @@ module RR
|
|
43
40
|
protected
|
44
41
|
def call_implementation
|
45
42
|
if implementation_is_original_method?
|
43
|
+
space.record_call(subject, method_name, args, block)
|
44
|
+
double.method_call(args)
|
46
45
|
call_original_method
|
47
46
|
else
|
48
|
-
|
47
|
+
if double_injection = Injections::DoubleInjection.find(subject_class, method_name)
|
48
|
+
double_injection.bind_method
|
49
|
+
# The DoubleInjection takes care of calling double.method_call
|
50
|
+
subject.__send__(method_name, *args, &block)
|
51
|
+
else
|
52
|
+
nil
|
53
|
+
end
|
49
54
|
end
|
50
55
|
end
|
51
56
|
|
52
57
|
def double_injection
|
53
|
-
Injections::DoubleInjection.
|
58
|
+
Injections::DoubleInjection.find_or_create(subject_class, method_name)
|
54
59
|
end
|
55
|
-
|
56
|
-
def_delegators 'self.class', :original_method_missing_alias_name
|
57
60
|
end
|
58
61
|
end
|
59
62
|
end
|
data/lib/rr/recorded_calls.rb
CHANGED
@@ -40,7 +40,7 @@ module RR
|
|
40
40
|
attr_accessor :ordered_index
|
41
41
|
|
42
42
|
def double_injection_exists_error(spy_verification)
|
43
|
-
unless Injections::DoubleInjection.
|
43
|
+
unless Injections::DoubleInjection.exists_by_subject?(spy_verification.subject, spy_verification.method_name)
|
44
44
|
RR::Errors::SpyVerificationErrors::DoubleInjectionNotFoundError.new(
|
45
45
|
"A Double Injection for the subject and method call:\n" <<
|
46
46
|
"#{spy_verification.subject.inspect}\n" <<
|
data/lib/rr/space.rb
CHANGED
@@ -67,12 +67,12 @@ module RR
|
|
67
67
|
|
68
68
|
# Verifies the DoubleInjection for the passed in subject and method_name.
|
69
69
|
def verify_double(subject, method_name)
|
70
|
-
Injections::DoubleInjection.verify_double(subject, method_name)
|
70
|
+
Injections::DoubleInjection.verify_double(class << subject; self; end, method_name)
|
71
71
|
end
|
72
72
|
|
73
73
|
# Resets the DoubleInjection for the passed in subject and method_name.
|
74
74
|
def reset_double(subject, method_name)
|
75
|
-
Injections::DoubleInjection.reset_double(subject, method_name)
|
75
|
+
Injections::DoubleInjection.reset_double(class << subject; self; end, method_name)
|
76
76
|
end
|
77
77
|
|
78
78
|
def record_call(subject, method_name, arguments, block)
|
@@ -81,7 +81,7 @@ module RR
|
|
81
81
|
|
82
82
|
def blank_slate_whitelist
|
83
83
|
@blank_slate_whitelist ||= [
|
84
|
-
"object_id", "respond_to?", "method_missing", "instance_eval", "instance_exec", "class_eval"
|
84
|
+
"object_id", "respond_to?", "respond_to_missing?", "method_missing", "instance_eval", "instance_exec", "class_eval"
|
85
85
|
]
|
86
86
|
end
|
87
87
|
|
@@ -92,7 +92,7 @@ module RR
|
|
92
92
|
end
|
93
93
|
|
94
94
|
def reset_method_missing_injections
|
95
|
-
Injections::MethodMissingInjection.instances.each do |
|
95
|
+
Injections::MethodMissingInjection.instances.each do |subject_class, injection|
|
96
96
|
injection.reset
|
97
97
|
end
|
98
98
|
Injections::MethodMissingInjection.instances.clear
|
@@ -104,9 +104,9 @@ module RR
|
|
104
104
|
end
|
105
105
|
Injections::SingletonMethodAddedInjection.instances.clear
|
106
106
|
end
|
107
|
-
|
107
|
+
|
108
108
|
def reset_recorded_calls
|
109
109
|
@recorded_calls.clear
|
110
110
|
end
|
111
111
|
end
|
112
|
-
end
|
112
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
module RR
|
2
2
|
module TimesCalledMatchers
|
3
3
|
class TimesCalledMatcher #:nodoc:
|
4
|
-
|
4
|
+
extend(Module.new do
|
5
5
|
def create(value)
|
6
6
|
return value if value.is_a?(TimesCalledMatcher)
|
7
7
|
return IntegerMatcher.new(value) if value.is_a?(Integer)
|
@@ -9,7 +9,7 @@ module RR
|
|
9
9
|
return ProcMatcher.new(value) if value.is_a?(Proc)
|
10
10
|
raise ArgumentError, "There is no TimesCalledMatcher for #{value.inspect}."
|
11
11
|
end
|
12
|
-
end
|
12
|
+
end)
|
13
13
|
|
14
14
|
attr_reader :times
|
15
15
|
|
data/scratch.rb
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
o = Object.new
|
2
|
+
#p (class << o; self; end).singleton_methods
|
3
|
+
#p (class << o; self; end).superclass
|
4
|
+
|
5
|
+
klass = Class.new
|
6
|
+
p klass
|
7
|
+
p klass.superclass
|
8
|
+
p (class << klass; self; end).superclass
|
9
|
+
p (class << Class; self; end)
|
10
|
+
|
11
|
+
p Class.new
|
12
|
+
|
13
|
+
##any_instance_of(Person, :id => 111, :to_s => "Joe Smith")
|
14
|
+
##
|
15
|
+
##any_instance_of(Person) do |person|
|
16
|
+
## person.id {111}
|
17
|
+
## person.to_s {"Joe Smith"}
|
18
|
+
##end
|
19
|
+
##
|
20
|
+
##new_instance_of(Person, :id => 111, :to_s => "Joe Smith")
|
21
|
+
##new_instance_of(Person) do |person|
|
22
|
+
## person.id {111}
|
23
|
+
## person.to_s {"Joe Smith"}
|
24
|
+
##end
|
25
|
+
#
|
26
|
+
#class Sut
|
27
|
+
# def existing_method
|
28
|
+
# "existing_method"
|
29
|
+
# end
|
30
|
+
#end
|
31
|
+
#
|
32
|
+
#class Dispatcher
|
33
|
+
# def self.call(subject, method_name, *args, &block)
|
34
|
+
# new(subject, method_name, *args, &block).call
|
35
|
+
# end
|
36
|
+
#
|
37
|
+
# attr_reader :method_name
|
38
|
+
# def initialize(subject, method_name, *args, &block)
|
39
|
+
# @subject, @method_name, @args, @block = subject, method_name, args, block
|
40
|
+
# end
|
41
|
+
#
|
42
|
+
# def call
|
43
|
+
#
|
44
|
+
# end
|
45
|
+
#end
|
46
|
+
#
|
47
|
+
#Sut.class_eval do
|
48
|
+
# alias_method :existing_method_without_rr, :existing_method
|
49
|
+
#
|
50
|
+
# def existing_method(*args, &block)
|
51
|
+
# Dispatcher.call(self, :existing_method, *args, &block)
|
52
|
+
# end
|
53
|
+
#end
|
54
|
+
|
55
|
+
#class SubjectClass
|
56
|
+
# def foo
|
57
|
+
# :foo_original
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# def _dump(arg)
|
61
|
+
# ""
|
62
|
+
# end
|
63
|
+
#end
|
64
|
+
#subject = SubjectClass.new
|
65
|
+
#
|
66
|
+
#class << subject
|
67
|
+
# p self.class
|
68
|
+
# class << self
|
69
|
+
# def foobar
|
70
|
+
# :baz
|
71
|
+
# end
|
72
|
+
# end
|
73
|
+
# p foobar
|
74
|
+
#end
|
75
|
+
#
|
76
|
+
#Foo = Module.new do
|
77
|
+
# def foo
|
78
|
+
# :foo_module
|
79
|
+
# end
|
80
|
+
#end
|
81
|
+
#Bar = Module.new do
|
82
|
+
# def foo
|
83
|
+
# :bar_module
|
84
|
+
# end
|
85
|
+
#end
|
86
|
+
##module Foo
|
87
|
+
## def foo
|
88
|
+
## end
|
89
|
+
##end
|
90
|
+
#
|
91
|
+
#subject.extend Foo
|
92
|
+
#end
|
93
|
+
#p subject.foo
|
94
|
+
#p Marshal.dump(subject)
|
95
|
+
|
96
|
+
|
97
|
+
class Super
|
98
|
+
|
99
|
+
end
|
100
|
+
class Sub < Super
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
def Super.foo
|
105
|
+
:bar
|
106
|
+
end
|
107
|
+
|
108
|
+
def Sub.foo
|
109
|
+
super
|
110
|
+
end
|
111
|
+
|
112
|
+
s = Super.new
|
113
|
+
p s.object_id
|
114
|
+
p (class << s; self; end).object_id
|
115
|
+
p s.class
|
116
|
+
#subject.extend Bar
|
117
|
+
#def subject.foo
|
118
|
+
# :singleton_foo
|