Permalink
Browse files

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

…nd it.
  • Loading branch information...
1 parent 6d93edd commit c1046268f4e9b9c7143449af27305bb147558817 @dchelimsky dchelimsky committed Nov 27, 2011
View
3 lib/rspec/mocks/any_instance.rb
@@ -1,7 +1,4 @@
require 'rspec/mocks/any_instance/chain'
-require 'rspec/mocks/any_instance/stub_chain'
-require 'rspec/mocks/any_instance/stub_chain_chain'
-require 'rspec/mocks/any_instance/expectation_chain'
require 'rspec/mocks/any_instance/message_chains'
require 'rspec/mocks/any_instance/recorder'
View
75 lib/rspec/mocks/any_instance/chain.rb
@@ -15,12 +15,14 @@ def #{method_name}(*args, &block)
EOM
end
+ # @api private
def playback!(instance)
messages.inject(instance) do |_instance, message|
_instance.__send__(*message.first, &message.last)
end
end
+ # @api private
def constrained_to_any_of?(*constraints)
constraints.any? do |constraint|
messages.any? do |message|
@@ -29,7 +31,12 @@ def constrained_to_any_of?(*constraints)
end
end
+ def expectation_fulfilled!
+ @expectation_fulfilled = true
+ end
+
private
+
def messages
@messages ||= []
end
@@ -44,6 +51,74 @@ def record(rspec_method_name, *args, &block)
self
end
end
+
+ class ExpectationChain < Chain
+ def initialize(*args, &block)
+ record(:should_receive, *args, &block)
+ @expectation_fulfilled = false
+ end
+
+ def invocation_order
+ @invocation_order ||= {
+ :should_receive => [nil],
+ :with => [:should_receive],
+ :and_return => [:with, :should_receive],
+ :and_raise => [:with, :should_receive]
+ }
+ end
+
+ def expectation_fulfilled?
+ @expectation_fulfilled || constrained_to_any_of?(:never, :any_number_of_times)
+ end
+
+ private
+
+ def verify_invocation_order(rspec_method_name, *args, &block)
+ end
+ end
+
+ class StubChain < Chain
+ def initialize(*args, &block)
+ record(:stub, *args, &block)
+ end
+
+ def invocation_order
+ @invocation_order ||= {
+ :stub => [nil],
+ :with => [:stub],
+ :and_return => [:with, :stub],
+ :and_raise => [:with, :stub],
+ :and_yield => [:with, :stub]
+ }
+ end
+
+ def expectation_fulfilled?
+ true
+ end
+
+ private
+
+ def verify_invocation_order(rspec_method_name, *args, &block)
+ unless invocation_order[rspec_method_name].include?(last_message)
+ raise(NoMethodError, "Undefined method #{rspec_method_name}")
+ end
+ end
+ end
+
+ class StubChainChain < StubChain
+ def initialize(*args, &block)
+ record(:stub_chain, *args, &block)
+ end
+
+ def invocation_order
+ @invocation_order ||= {
+ :stub_chain => [nil],
+ :and_return => [:stub_chain],
+ :and_raise => [:stub_chain],
+ :and_yield => [:stub_chain]
+ }
+ end
+ end
end
end
end
View
33 lib/rspec/mocks/any_instance/expectation_chain.rb
@@ -1,33 +0,0 @@
-module RSpec
- module Mocks
- module AnyInstance
- class ExpectationChain < Chain
- def initialize(*args, &block)
- record(:should_receive, *args, &block)
- @expectation_fulfilled = false
- end
-
- def invocation_order
- @invocation_order ||= {
- :should_receive => [nil],
- :with => [:should_receive],
- :and_return => [:with, :should_receive],
- :and_raise => [:with, :should_receive]
- }
- end
-
- def expectation_fulfilled!
- @expectation_fulfilled = true
- end
-
- def expectation_fulfilled?
- @expectation_fulfilled || constrained_to_any_of?(:never, :any_number_of_times)
- end
-
- private
- def verify_invocation_order(rspec_method_name, *args, &block)
- end
- end
- end
- end
-end
View
50 lib/rspec/mocks/any_instance/message_chains.rb
@@ -2,47 +2,51 @@ module RSpec
module Mocks
module AnyInstance
class MessageChains < Hash
+ def initialize
+ super {|h,k| h[k] = []}
+ end
+
def add(method_name, chain)
- (self[method_name] ||= []) << chain
+ self[method_name] << chain
+ chain
end
-
+
def remove_stub_chains_for!(method_name)
- chains = self[method_name]
- chains.reject! { |chain| chain.is_a?(StubChain) || chain.is_a?(StubChainChain) }
+ self[method_name].reject! {|chain| chain.is_a?(StubChain)}
end
-
+
def has_expectation?(method_name)
- !!self[method_name].find{|chain| chain.is_a?(ExpectationChain)}
+ self[method_name].find {|chain| chain.is_a?(ExpectationChain)}
end
-
- def each_expectation_fulfilled?
- self.all? do |method_name, chains|
- chains.all? { |chain| chain.expectation_fulfilled? }
- end
+
+ def all_expectations_fulfilled?
+ all? {|method_name, chains| chains.all? {|chain| chain.expectation_fulfilled?}}
end
def unfulfilled_expectations
- self.map do |method_name, chains|
+ map do |method_name, chains|
method_name.to_s if chains.last.is_a?(ExpectationChain) unless chains.last.expectation_fulfilled?
end.compact
end
def received_expected_message!(method_name)
- self[method_name].each do |chain|
- chain.expectation_fulfilled!
- end
+ self[method_name].each {|chain| chain.expectation_fulfilled!}
end
-
+
def playback!(instance, method_name)
- self[method_name].each do |chain|
- @instance_with_expectation = instance if instance.is_a?(ExpectationChain) && !@instance_with_expectation
- if instance.is_a?(ExpectationChain) && !@instance_with_expectation.equal?(instance)
- raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}"
- end
- chain.playback!(instance)
+ raise_if_second_instance_to_receive_message(instance)
+ self[method_name].each {|chain| chain.playback!(instance)}
+ end
+
+ private
+
+ def raise_if_second_instance_to_receive_message(instance)
+ @instance_with_expectation ||= instance if instance.is_a?(ExpectationChain)
+ if instance.is_a?(ExpectationChain) && !@instance_with_expectation.equal?(instance)
+ raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{unfulfilled_expectations.sort.join(', ')}"
end
end
end
end
end
-end
+end
View
90 lib/rspec/mocks/any_instance/recorder.rb
@@ -10,88 +10,80 @@ def initialize(klass)
@klass = klass
@expectation_set = false
end
-
+
+ def stub(method_name_or_method_map, &block)
+ if method_name_or_method_map.is_a?(Hash)
+ method_name_or_method_map.each do |method_name, return_value|
+ stub(method_name).and_return(return_value)
+ end
+ else
+ observe!(method_name_or_method_map)
+ message_chains.add(method_name_or_method_map, StubChain.new(method_name_or_method_map, &block))
+ end
+ end
+
+ def stub_chain(*args, &block)
+ normalize_chain(*args) do |method_name, args|
+ observe!(method_name)
+ message_chains.add(method_name, StubChainChain.new(*args, &block))
+ end
+ end
+
+ def should_receive(method_name, &block)
+ @expectation_set = true
+ observe!(method_name)
+ message_chains.add(method_name, ExpectationChain.new(method_name, &block))
+ end
+
def unstub(method_name)
unless @observed_methods.include?(method_name.to_sym)
raise RSpec::Mocks::MockExpectationError, "The method `#{method_name}` was not stubbed or was already unstubbed"
end
message_chains.remove_stub_chains_for!(method_name)
stop_observing!(method_name) unless message_chains.has_expectation?(method_name)
end
-
- def stub(method_name_or_method_map, *args, &block)
- if method_name_or_method_map.is_a?(Hash)
- method_map = method_name_or_method_map
- method_map.each do |method_name, return_value|
- observe!(method_name)
- message_chains.add(method_name, chain = StubChain.new(method_name))
- chain.and_return(return_value)
- end
- method_map
- else
- method_name = method_name_or_method_map
- observe!(method_name)
- message_chains.add(method_name, chain = StubChain.new(method_name, *args, &block))
- chain
+
+ def verify
+ if @expectation_set && !message_chains.all_expectations_fulfilled?
+ raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}"
end
end
def stub!(*)
raise "stub! is not supported on any_instance. Use stub instead."
end
- def stub_chain(method_name_or_string_chain, *args, &block)
- if period_separated_method_chain?(method_name_or_string_chain)
- first_method_name = method_name_or_string_chain.split('.').first.to_sym
- else
- first_method_name = method_name_or_string_chain
- end
- observe!(first_method_name)
- message_chains.add(first_method_name, chain = StubChainChain.new(method_name_or_string_chain, *args, &block))
- chain
- end
-
- def should_receive(method_name, *args, &block)
- observe!(method_name)
- @expectation_set = true
- message_chains.add(method_name, chain = ExpectationChain.new(method_name, *args, &block))
- chain
- end
-
+ # @api private
def stop_all_observation!
- @observed_methods.each do |method_name|
- restore_method!(method_name)
- end
+ @observed_methods.each {|method_name| restore_method!(method_name)}
end
+ # @api private
def playback!(instance, method_name)
RSpec::Mocks::space.add(instance)
message_chains.playback!(instance, method_name)
@played_methods[method_name] = instance
received_expected_message!(method_name) if message_chains.has_expectation?(method_name)
end
+ # @api private
def instance_that_received(method_name)
@played_methods[method_name]
end
- def verify
- if @expectation_set && !message_chains.each_expectation_fulfilled?
- raise RSpec::Mocks::MockExpectationError, "Exactly one instance should have received the following message(s) but didn't: #{message_chains.unfulfilled_expectations.sort.join(', ')}"
- end
- end
-
private
- def period_separated_method_chain?(method_name)
- method_name.is_a?(String) && method_name.include?('.')
+
+ def normalize_chain(*args)
+ args.shift.to_s.split('.').map {|s| s.to_sym}.reverse.each {|a| args.unshift a}
+ yield args.first, args
end
-
+
def received_expected_message!(method_name)
message_chains.received_expected_message!(method_name)
restore_method!(method_name)
mark_invoked!(method_name)
end
-
+
def restore_method!(method_name)
if public_protected_or_private_method_defined?(build_alias_method_name(method_name))
restore_original_method!(method_name)
@@ -125,11 +117,11 @@ def backup_method!(method_name)
alias_method alias_method_name, method_name
end if public_protected_or_private_method_defined?(method_name)
end
-
+
def public_protected_or_private_method_defined?(method_name)
@klass.method_defined?(method_name) || @klass.private_method_defined?(method_name)
end
-
+
def stop_observing!(method_name)
restore_method!(method_name)
@observed_methods.delete(method_name)
View
35 lib/rspec/mocks/any_instance/stub_chain.rb
@@ -1,35 +0,0 @@
-module RSpec
- module Mocks
- module AnyInstance
- class StubChain < Chain
- def initialize(*args, &block)
- record(:stub, *args, &block)
- end
-
- def invocation_order
- @invocation_order ||= {
- :stub => [nil],
- :with => [:stub],
- :and_return => [:with, :stub],
- :and_raise => [:with, :stub],
- :and_yield => [:with, :stub]
- }
- end
-
- def expectation_fulfilled?
- true
- end
-
- def expectation_fulfilled!
- end
-
- private
- def verify_invocation_order(rspec_method_name, *args, &block)
- unless invocation_order[rspec_method_name].include?(last_message)
- raise(NoMethodError, "Undefined method #{rspec_method_name}")
- end
- end
- end
- end
- end
-end
View
34 lib/rspec/mocks/any_instance/stub_chain_chain.rb
@@ -1,34 +0,0 @@
-module RSpec
- module Mocks
- module AnyInstance
- class StubChainChain < Chain
- def initialize(*args, &block)
- record(:stub_chain, *args, &block)
- end
-
- def invocation_order
- @invocation_order ||= {
- :stub_chain => [nil],
- :and_return => [:stub_chain],
- :and_raise => [:stub_chain],
- :and_yield => [:stub_chain]
- }
- end
-
- def expectation_fulfilled?
- true
- end
-
- def expectation_fulfilled!
- end
-
- private
- def verify_invocation_order(rspec_method_name, *args, &block)
- unless invocation_order[rspec_method_name].include?(last_message)
- raise(NoMethodError, "Undefined method #{rspec_method_name}")
- end
- end
- end
- end
- end
-end

0 comments on commit c104626

Please sign in to comment.