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.
Files changed (57) hide show
  1. data/CHANGES +5 -0
  2. data/Gemfile +6 -0
  3. data/README.rdoc +28 -2
  4. data/VERSION.yml +3 -3
  5. data/lib/rr.rb +23 -17
  6. data/lib/rr/adapters/rr_methods.rb +43 -17
  7. data/lib/rr/blank_slate.rb +2 -2
  8. data/lib/rr/class_instance_method_defined.rb +9 -0
  9. data/lib/rr/double.rb +2 -2
  10. data/lib/rr/double_definitions/double_definition.rb +29 -16
  11. data/lib/rr/double_definitions/double_definition_create.rb +52 -86
  12. data/lib/rr/double_definitions/double_injections/any_instance_of.rb +28 -0
  13. data/lib/rr/double_definitions/double_injections/instance.rb +16 -0
  14. data/lib/rr/double_definitions/double_injections/new_instance_of.rb +53 -0
  15. data/lib/rr/double_definitions/strategies/double_injection/any_instance_of.rb +31 -0
  16. data/lib/rr/double_definitions/strategies/double_injection/double_injection_strategy.rb +10 -0
  17. data/lib/rr/double_definitions/strategies/double_injection/instance.rb +17 -0
  18. data/lib/rr/double_definitions/strategies/double_injection/new_instance_of.rb +37 -0
  19. data/lib/rr/double_definitions/strategies/implementation/implementation_strategy.rb +0 -5
  20. data/lib/rr/double_definitions/strategies/implementation/proxy.rb +0 -2
  21. data/lib/rr/double_definitions/strategies/implementation/strongly_typed_reimplementation.rb +0 -2
  22. data/lib/rr/double_definitions/strategies/strategy.rb +0 -27
  23. data/lib/rr/double_definitions/strategies/strategy_methods.rb +53 -0
  24. data/lib/rr/double_definitions/strategies/verification/dont_allow.rb +0 -2
  25. data/lib/rr/double_definitions/strategies/verification/mock.rb +0 -2
  26. data/lib/rr/double_definitions/strategies/verification/stub.rb +0 -2
  27. data/lib/rr/double_definitions/strategies/verification/verification_strategy.rb +0 -5
  28. data/lib/rr/hash_with_object_id_key.rb +4 -0
  29. data/lib/rr/injections/double_injection.rb +94 -71
  30. data/lib/rr/injections/injection.rb +8 -10
  31. data/lib/rr/injections/method_missing_injection.rb +13 -20
  32. data/lib/rr/injections/singleton_method_added_injection.rb +19 -17
  33. data/lib/rr/method_dispatches/base_method_dispatch.rb +1 -1
  34. data/lib/rr/method_dispatches/method_dispatch.rb +4 -4
  35. data/lib/rr/method_dispatches/method_missing_dispatch.rb +17 -14
  36. data/lib/rr/recorded_calls.rb +1 -1
  37. data/lib/rr/space.rb +6 -6
  38. data/lib/rr/times_called_matchers/times_called_matcher.rb +2 -2
  39. data/scratch.rb +118 -0
  40. data/spec/api/any_instance_of/all_instances_of_spec.rb +14 -0
  41. data/spec/api/any_instance_of/any_instance_of_spec.rb +47 -0
  42. data/spec/api/mock/mock_spec.rb +2 -2
  43. data/spec/api/new_instance_of/instance_of_spec.rb +15 -0
  44. data/spec/api/new_instance_of/new_instance_of_spec.rb +61 -0
  45. data/spec/environment_fixture_setup.rb +3 -2
  46. data/spec/rr/adapters/rr_methods_space_spec.rb +8 -10
  47. data/spec/rr/double_definitions/child_double_definition_creator_spec.rb +1 -1
  48. data/spec/rr/double_definitions/double_definition_create_blank_slate_spec.rb +6 -2
  49. data/spec/rr/double_definitions/double_definition_create_spec.rb +1 -52
  50. data/spec/rr/double_injection/double_injection_verify_spec.rb +1 -1
  51. data/spec/rr/rspec/rspec_adapter_spec.rb +5 -5
  52. data/spec/rr/space/space_spec.rb +58 -67
  53. data/spec/spec_helper.rb +2 -2
  54. metadata +33 -9
  55. data/lib/rr/double_definitions/strategies/scope/instance.rb +0 -15
  56. data/lib/rr/double_definitions/strategies/scope/instance_of_class.rb +0 -50
  57. 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
- class << self
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
- @subject.methods.detect {|method_name| method_name.to_sym == method_name_in_question.to_sym} ||
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
- subject_respond_to_method?(original_method_alias_name)
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) || @subject.respond_to?(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
- class << self
5
- def create(subject)
6
- instances[subject] ||= begin
7
- new(subject).bind
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
- def initialize(subject)
17
- @subject = subject
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 subject.respond_to?(original_method_alias_name)
23
- unless subject.respond_to?(:method_missing)
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
- returns_method = <<-METHOD
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
- RR::Injections::MethodMissingInjection.create(self).dispatch_method(method_name, args, block)
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
- class << self
5
- def create(subject)
6
- instances[subject] ||= begin
7
- new(subject).bind
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
- def initialize(subject)
17
- @subject = subject
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 subject.respond_to?(original_method_alias_name)
23
- unless subject.respond_to?(:singleton_method_added)
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?(memoized_subject, method_name_arg)
37
- Injections::DoubleInjection.create(memoized_subject, method_name_arg).send(:deferred_bind_method)
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
- send(memoized_original_method_alias_name, method_name_arg)
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?, :subject, :method_name, :original_method_alias_name
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
- class << self
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?(subject, method_name)
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
- double_injection.bypass_bound_method do
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
- nil
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.create(subject, method_name)
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
@@ -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.exists?(spy_verification.subject, spy_verification.method_name)
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" <<
@@ -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 |subject, injection|
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
- class << self
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
 
@@ -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