Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add caller_filter from rspec-core. #310

Merged
merged 2 commits into from Aug 23, 2013
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
55 changes: 55 additions & 0 deletions lib/rspec/caller_filter.rb
@@ -0,0 +1,55 @@
module RSpec
# Consistent implementation for "cleaning" the caller method to strip out
# non-rspec lines. This enables errors to be reported at the call site in
# the code using the library, which is far more useful than the particular
# internal method that raised an error.
class CallerFilter

# This list is an unfortunate dependency on other RSpec core libraries.
# It would be nice if this was not needed.
RSPEC_LIBS = %w[
core
mocks
expectations
matchers
rails
]

LIB_REGEX = %r{/lib/rspec/(#{RSPEC_LIBS.join('|')})(\.rb|/)}

if RUBY_VERSION >= '2.0.0'
def self.first_non_rspec_line
# `caller` is an expensive method that scales linearly with the size of
# the stack. The performance hit for fetching it in chunks is small,
# and since the target line is probably near the top of the stack, the
# overall improvement of a chunked search like this is significant.
#
# See benchmarks/caller.rb for measurements.

# Initial value here is mostly arbitrary, but is chosen to give good
# performance on the common case of creating a double.
increment = 5
i = 1
line = nil

while !line
stack = caller(i, increment)
raise "No non-lib lines in stack" unless stack

line = stack.find { |l| l !~ LIB_REGEX }

i += increment
increment *= 2 # The choice of two here is arbitrary.
end

line
end
else
# Earlier rubies do not support the two argument form of `caller`. This
# fallback is logically the same, but slower.
def self.first_non_rspec_line
caller.find { |line| line !~ LIB_REGEX }
end
end
end
end
4 changes: 3 additions & 1 deletion lib/rspec/expectations/deprecation.rb
@@ -1,3 +1,5 @@
require 'rspec/caller_filter'

module RSpec
module Expectations
module Deprecation
Expand All @@ -7,7 +9,7 @@ module Deprecation
def deprecate(deprecated, options={})
message = "DEPRECATION: #{deprecated} is deprecated."
message << " Use #{options[:replacement]} instead." if options[:replacement]
message << " Called from #{caller(0)[2]}."
message << " Called from #{CallerFilter.first_non_rspec_line}."
warn message
end
end
Expand Down
18 changes: 18 additions & 0 deletions spec/rspec/expectations_spec.rb
@@ -0,0 +1,18 @@
module RSpec
describe Expectations do
def file_contents_for(lib, filename)
# http://rubular.com/r/HYpUMftlG2
path = $LOAD_PATH.find { |p| p.match(/\/rspec-#{lib}(-[a-f0-9]+)?\/lib/) }
file = File.join(path, filename)
File.read(file)
end

it 'has an up-to-date rspec/caller_filter file' do
expectations = file_contents_for("expectations", "rspec/caller_filter.rb")
core = file_contents_for("core", "rspec/caller_filter.rb")

expect(expectations).to eq(core)
end
end
end