Skip to content
This repository
Browse code

Begin to refactor any_instance framework a bit as I wrap my head arou…

…nd it.
  • Loading branch information...
commit c1046268f4e9b9c7143449af27305bb147558817 1 parent 6d93edd
David Chelimsky dchelimsky authored
3  lib/rspec/mocks/any_instance.rb
... ... @@ -1,7 +1,4 @@
1 1 require 'rspec/mocks/any_instance/chain'
2   -require 'rspec/mocks/any_instance/stub_chain'
3   -require 'rspec/mocks/any_instance/stub_chain_chain'
4   -require 'rspec/mocks/any_instance/expectation_chain'
5 2 require 'rspec/mocks/any_instance/message_chains'
6 3 require 'rspec/mocks/any_instance/recorder'
7 4
75 lib/rspec/mocks/any_instance/chain.rb
@@ -15,12 +15,14 @@ def #{method_name}(*args, &block)
15 15 EOM
16 16 end
17 17
  18 + # @api private
18 19 def playback!(instance)
19 20 messages.inject(instance) do |_instance, message|
20 21 _instance.__send__(*message.first, &message.last)
21 22 end
22 23 end
23 24
  25 + # @api private
24 26 def constrained_to_any_of?(*constraints)
25 27 constraints.any? do |constraint|
26 28 messages.any? do |message|
@@ -29,7 +31,12 @@ def constrained_to_any_of?(*constraints)
29 31 end
30 32 end
31 33
  34 + def expectation_fulfilled!
  35 + @expectation_fulfilled = true
  36 + end
  37 +
32 38 private
  39 +
33 40 def messages
34 41 @messages ||= []
35 42 end
@@ -44,6 +51,74 @@ def record(rspec_method_name, *args, &block)
44 51 self
45 52 end
46 53 end
  54 +
  55 + class ExpectationChain < Chain
  56 + def initialize(*args, &block)
  57 + record(:should_receive, *args, &block)
  58 + @expectation_fulfilled = false
  59 + end
  60 +
  61 + def invocation_order
  62 + @invocation_order ||= {
  63 + :should_receive => [nil],
  64 + :with => [:should_receive],
  65 + :and_return => [:with, :should_receive],
  66 + :and_raise => [:with, :should_receive]
  67 + }
  68 + end
  69 +
  70 + def expectation_fulfilled?
  71 + @expectation_fulfilled || constrained_to_any_of?(:never, :any_number_of_times)
  72 + end
  73 +
  74 + private
  75 +
  76 + def verify_invocation_order(rspec_method_name, *args, &block)
  77 + end
  78 + end
  79 +
  80 + class StubChain < Chain
  81 + def initialize(*args, &block)
  82 + record(:stub, *args, &block)
  83 + end
  84 +
  85 + def invocation_order
  86 + @invocation_order ||= {
  87 + :stub => [nil],
  88 + :with => [:stub],
  89 + :and_return => [:with, :stub],
  90 + :and_raise => [:with, :stub],
  91 + :and_yield => [:with, :stub]
  92 + }
  93 + end
  94 +
  95 + def expectation_fulfilled?
  96 + true
  97 + end
  98 +
  99 + private
  100 +
  101 + def verify_invocation_order(rspec_method_name, *args, &block)
  102 + unless invocation_order[rspec_method_name].include?(last_message)
  103 + raise(NoMethodError, "Undefined method #{rspec_method_name}")
  104 + end
  105 + end
  106 + end
  107 +
  108 + class StubChainChain < StubChain
  109 + def initialize(*args, &block)
  110 + record(:stub_chain, *args, &block)
  111 + end
  112 +
  113 + def invocation_order
  114 + @invocation_order ||= {
  115 + :stub_chain => [nil],
  116 + :and_return => [:stub_chain],
  117 + :and_raise => [:stub_chain],
  118 + :and_yield => [:stub_chain]
  119 + }
  120 + end
  121 + end
47 122 end
48 123 end
49 124 end
33 lib/rspec/mocks/any_instance/expectation_chain.rb
... ... @@ -1,33 +0,0 @@
1   -module RSpec
2   - module Mocks
3   - module AnyInstance
4   - class ExpectationChain < Chain
5   - def initialize(*args, &block)
6   - record(:should_receive, *args, &block)
7   - @expectation_fulfilled = false
8   - end
9   -
10   - def invocation_order
11   - @invocation_order ||= {
12   - :should_receive => [nil],
13   - :with => [:should_receive],
14   - :and_return => [:with, :should_receive],
15   - :and_raise => [:with, :should_receive]
16   - }
17   - end
18   -
19   - def expectation_fulfilled!
20   - @expectation_fulfilled = true
21   - end
22   -
23   - def expectation_fulfilled?
24   - @expectation_fulfilled || constrained_to_any_of?(:never, :any_number_of_times)
25   - end
26   -
27   - private
28   - def verify_invocation_order(rspec_method_name, *args, &block)
29   - end
30   - end
31   - end
32   - end
33   -end
50 lib/rspec/mocks/any_instance/message_chains.rb
@@ -2,47 +2,51 @@ module RSpec
2 2 module Mocks
3 3 module AnyInstance
4 4 class MessageChains < Hash
  5 + def initialize
  6 + super {|h,k| h[k] = []}
  7 + end
  8 +
5 9 def add(method_name, chain)
6   - (self[method_name] ||= []) << chain
  10 + self[method_name] << chain
  11 + chain
7 12 end
8   -
  13 +
9 14 def remove_stub_chains_for!(method_name)
10   - chains = self[method_name]
11   - chains.reject! { |chain| chain.is_a?(StubChain) || chain.is_a?(StubChainChain) }
  15 + self[method_name].reject! {|chain| chain.is_a?(StubChain)}
12 16 end
13   -
  17 +
14 18 def has_expectation?(method_name)
15   - !!self[method_name].find{|chain| chain.is_a?(ExpectationChain)}
  19 + self[method_name].find {|chain| chain.is_a?(ExpectationChain)}
16 20 end
17   -
18   - def each_expectation_fulfilled?
19   - self.all? do |method_name, chains|
20   - chains.all? { |chain| chain.expectation_fulfilled? }
21   - end
  21 +
  22 + def all_expectations_fulfilled?
  23 + all? {|method_name, chains| chains.all? {|chain| chain.expectation_fulfilled?}}
22 24 end
23 25
24 26 def unfulfilled_expectations
25   - self.map do |method_name, chains|
  27 + map do |method_name, chains|
26 28 method_name.to_s if chains.last.is_a?(ExpectationChain) unless chains.last.expectation_fulfilled?
27 29 end.compact
28 30 end
29 31
30 32 def received_expected_message!(method_name)
31   - self[method_name].each do |chain|
32   - chain.expectation_fulfilled!
33   - end
  33 + self[method_name].each {|chain| chain.expectation_fulfilled!}
34 34 end
35   -
  35 +
36 36 def playback!(instance, method_name)
37   - self[method_name].each do |chain|
38   - @instance_with_expectation = instance if instance.is_a?(ExpectationChain) && !@instance_with_expectation
39   - if instance.is_a?(ExpectationChain) && !@instance_with_expectation.equal?(instance)
40   - raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}"
41   - end
42   - chain.playback!(instance)
  37 + raise_if_second_instance_to_receive_message(instance)
  38 + self[method_name].each {|chain| chain.playback!(instance)}
  39 + end
  40 +
  41 + private
  42 +
  43 + def raise_if_second_instance_to_receive_message(instance)
  44 + @instance_with_expectation ||= instance if instance.is_a?(ExpectationChain)
  45 + if instance.is_a?(ExpectationChain) && !@instance_with_expectation.equal?(instance)
  46 + raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}"
43 47 end
44 48 end
45 49 end
46 50 end
47 51 end
48   -end
  52 +end
90 lib/rspec/mocks/any_instance/recorder.rb
@@ -10,7 +10,31 @@ def initialize(klass)
10 10 @klass = klass
11 11 @expectation_set = false
12 12 end
13   -
  13 +
  14 + def stub(method_name_or_method_map, &block)
  15 + if method_name_or_method_map.is_a?(Hash)
  16 + method_name_or_method_map.each do |method_name, return_value|
  17 + stub(method_name).and_return(return_value)
  18 + end
  19 + else
  20 + observe!(method_name_or_method_map)
  21 + message_chains.add(method_name_or_method_map, StubChain.new(method_name_or_method_map, &block))
  22 + end
  23 + end
  24 +
  25 + def stub_chain(*args, &block)
  26 + normalize_chain(*args) do |method_name, args|
  27 + observe!(method_name)
  28 + message_chains.add(method_name, StubChainChain.new(*args, &block))
  29 + end
  30 + end
  31 +
  32 + def should_receive(method_name, &block)
  33 + @expectation_set = true
  34 + observe!(method_name)
  35 + message_chains.add(method_name, ExpectationChain.new(method_name, &block))
  36 + end
  37 +
14 38 def unstub(method_name)
15 39 unless @observed_methods.include?(method_name.to_sym)
16 40 raise RSpec::Mocks::MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed"
@@ -18,21 +42,10 @@ def unstub(method_name)
18 42 message_chains.remove_stub_chains_for!(method_name)
19 43 stop_observing!(method_name) unless message_chains.has_expectation?(method_name)
20 44 end
21   -
22   - def stub(method_name_or_method_map, *args, &block)
23   - if method_name_or_method_map.is_a?(Hash)
24   - method_map = method_name_or_method_map
25   - method_map.each do |method_name, return_value|
26   - observe!(method_name)
27   - message_chains.add(method_name, chain = StubChain.new(method_name))
28   - chain.and_return(return_value)
29   - end
30   - method_map
31   - else
32   - method_name = method_name_or_method_map
33   - observe!(method_name)
34   - message_chains.add(method_name, chain = StubChain.new(method_name, *args, &block))
35   - chain
  45 +
  46 + def verify
  47 + if @expectation_set && !message_chains.all_expectations_fulfilled?
  48 + raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}"
36 49 end
37 50 end
38 51
@@ -40,30 +53,12 @@ def stub!(*)
40 53 raise "stub! is not supported on any_instance. Use stub instead."
41 54 end
42 55
43   - def stub_chain(method_name_or_string_chain, *args, &block)
44   - if period_separated_method_chain?(method_name_or_string_chain)
45   - first_method_name = method_name_or_string_chain.split('.').first.to_sym
46   - else
47   - first_method_name = method_name_or_string_chain
48   - end
49   - observe!(first_method_name)
50   - message_chains.add(first_method_name, chain = StubChainChain.new(method_name_or_string_chain, *args, &block))
51   - chain
52   - end
53   -
54   - def should_receive(method_name, *args, &block)
55   - observe!(method_name)
56   - @expectation_set = true
57   - message_chains.add(method_name, chain = ExpectationChain.new(method_name, *args, &block))
58   - chain
59   - end
60   -
  56 + # @api private
61 57 def stop_all_observation!
62   - @observed_methods.each do |method_name|
63   - restore_method!(method_name)
64   - end
  58 + @observed_methods.each {|method_name| restore_method!(method_name)}
65 59 end
66 60
  61 + # @api private
67 62 def playback!(instance, method_name)
68 63 RSpec::Mocks::space.add(instance)
69 64 message_chains.playback!(instance, method_name)
@@ -71,27 +66,24 @@ def playback!(instance, method_name)
71 66 received_expected_message!(method_name) if message_chains.has_expectation?(method_name)
72 67 end
73 68
  69 + # @api private
74 70 def instance_that_received(method_name)
75 71 @played_methods[method_name]
76 72 end
77 73
78   - def verify
79   - if @expectation_set && !message_chains.each_expectation_fulfilled?
80   - raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}"
81   - end
82   - end
83   -
84 74 private
85   - def period_separated_method_chain?(method_name)
86   - method_name.is_a?(String) && method_name.include?('.')
  75 +
  76 + def normalize_chain(*args)
  77 + args.shift.to_s.split('.').map {|s| s.to_sym}.reverse.each {|a| args.unshift a}
  78 + yield args.first, args
87 79 end
88   -
  80 +
89 81 def received_expected_message!(method_name)
90 82 message_chains.received_expected_message!(method_name)
91 83 restore_method!(method_name)
92 84 mark_invoked!(method_name)
93 85 end
94   -
  86 +
95 87 def restore_method!(method_name)
96 88 if public_protected_or_private_method_defined?(build_alias_method_name(method_name))
97 89 restore_original_method!(method_name)
@@ -125,11 +117,11 @@ def backup_method!(method_name)
125 117 alias_method alias_method_name, method_name
126 118 end if public_protected_or_private_method_defined?(method_name)
127 119 end
128   -
  120 +
129 121 def public_protected_or_private_method_defined?(method_name)
130 122 @klass.method_defined?(method_name) || @klass.private_method_defined?(method_name)
131 123 end
132   -
  124 +
133 125 def stop_observing!(method_name)
134 126 restore_method!(method_name)
135 127 @observed_methods.delete(method_name)
35 lib/rspec/mocks/any_instance/stub_chain.rb
... ... @@ -1,35 +0,0 @@
1   -module RSpec
2   - module Mocks
3   - module AnyInstance
4   - class StubChain < Chain
5   - def initialize(*args, &block)
6   - record(:stub, *args, &block)
7   - end
8   -
9   - def invocation_order
10   - @invocation_order ||= {
11   - :stub => [nil],
12   - :with => [:stub],
13   - :and_return => [:with, :stub],
14   - :and_raise => [:with, :stub],
15   - :and_yield => [:with, :stub]
16   - }
17   - end
18   -
19   - def expectation_fulfilled?
20   - true
21   - end
22   -
23   - def expectation_fulfilled!
24   - end
25   -
26   - private
27   - def verify_invocation_order(rspec_method_name, *args, &block)
28   - unless invocation_order[rspec_method_name].include?(last_message)
29   - raise(NoMethodError, "Undefined method #{rspec_method_name}")
30   - end
31   - end
32   - end
33   - end
34   - end
35   -end
34 lib/rspec/mocks/any_instance/stub_chain_chain.rb
... ... @@ -1,34 +0,0 @@
1   -module RSpec
2   - module Mocks
3   - module AnyInstance
4   - class StubChainChain < Chain
5   - def initialize(*args, &block)
6   - record(:stub_chain, *args, &block)
7   - end
8   -
9   - def invocation_order
10   - @invocation_order ||= {
11   - :stub_chain => [nil],
12   - :and_return => [:stub_chain],
13   - :and_raise => [:stub_chain],
14   - :and_yield => [:stub_chain]
15   - }
16   - end
17   -
18   - def expectation_fulfilled?
19   - true
20   - end
21   -
22   - def expectation_fulfilled!
23   - end
24   -
25   - private
26   - def verify_invocation_order(rspec_method_name, *args, &block)
27   - unless invocation_order[rspec_method_name].include?(last_message)
28   - raise(NoMethodError, "Undefined method #{rspec_method_name}")
29   - end
30   - end
31   - end
32   - end
33   - end
34   -end

0 comments on commit c104626

Please sign in to comment.
Something went wrong with that request. Please try again.