Permalink
Browse files

Prevent SystemStackError on 1.9 when users include RSpec::Matchers in…

… an example group.


Closes #63.
  • Loading branch information...
myronmarston committed Feb 27, 2011
1 parent a496a73 commit 80e53009f18846e7feea3202f79c49b959242e43
@@ -1,12 +1,35 @@
module RSpec
module Matchers
-
private
- def method_missing(method, *args, &block) # :nodoc:
+ # Ruby 1.9 has a weird bug where you can get infinite recursion and a SystemStackError
+ # under the following conditions:
+ # * You have a module that defines a method that uses super
+ # * You include that module in a subclass
+ # * You include that module in the subclass's superclass _after_ it has already
+ # been included in the subclass.
+ # See https://gist.github.com/845896 for a demonstration of this bug.
+ #
+ # This manifested itself in RSpec with the method_missing hook below, because
+ # rspec-core includes RSpec::Matchers in RSpec::Core::ExampleGroup right before
+ # running all the examples (but after all the examples have been defined), so that
+ # users can configure RSpec to use a different expectation/assertion framework if
+ # they wish. If users included RSpec::Matchers in an example group, undefined method
+ # calls would trigger this bug.
+ #
+ # Our work around is to use alias_method_chain rather than super. It's not as
+ # elegant, but it fixes the issue.
+ def self.included(base)
+ base.class_eval do
+ alias_method :method_missing_without_rspec_matchers, :method_missing
+ alias_method :method_missing, :method_missing_with_rspec_matchers
+ end
+ end
+
+ def method_missing_with_rspec_matchers(method, *args, &block) # :nodoc:
return Matchers::BePredicate.new(method, *args, &block) if method.to_s =~ /^be_/
return Matchers::Has.new(method, *args, &block) if method.to_s =~ /^have_/
- super
+ method_missing_without_rspec_matchers(method, *args, &block)
end
end
end
@@ -1,10 +1,6 @@
require 'spec_helper'
describe "should be_predicate" do
- it "allows other undefined methods to raise errors as normal" do
- expect { some_undefined_method }.to raise_error(NameError)
- end
-
it "passes when actual returns true for :predicate?" do
actual = stub("actual", :happy? => true)
actual.should be_happy
@@ -0,0 +1,21 @@
+require 'spec_helper'
+
+describe "method_missing" do
+ shared_examples_for "a well-behaved method_missing hook" do
+ it "allows undefined methods to raise errors as normal" do
+ expect { some_undefined_method }.to raise_error(NameError)
+ end
+ end
+
+ it_behaves_like "a well-behaved method_missing hook"
+
+ context "when RSpec::Matchers has been included in an example group" do
+ include RSpec::Matchers
+ it_behaves_like "a well-behaved method_missing hook"
+ end
+
+ context "when a module that includes RSpec::Matchers has been included in an example group" do
+ include Module.new { include RSpec::Matchers }
+ it_behaves_like "a well-behaved method_missing hook"
+ end
+end

0 comments on commit 80e5300

Please sign in to comment.