Skip to content

Commit

Permalink
[support] Merge pull request rspec/rspec-support#436 from rspec/drop-…
Browse files Browse the repository at this point in the history
…old-rubies

Drop Ruby < 2.3 support

---
This commit was imported from rspec/rspec-support@1a26b09.
  • Loading branch information
JonRowe authored Dec 15, 2020
2 parents 019dc41 + d8e8eb4 commit 0e2f958
Show file tree
Hide file tree
Showing 40 changed files with 961 additions and 1,677 deletions.
6 changes: 6 additions & 0 deletions rspec-support/Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
### Development (unreleased)

Breaking Changes:

* Ruby < 2.3 is no longer supported. (Phil Pirozhkov, #436)

### 3.10.0 / 2020-10-30

No changes. Released to support other RSpec releases.
Expand Down
36 changes: 3 additions & 33 deletions rspec-support/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,51 +13,21 @@ branch = File.read(File.expand_path("../maintenance-branch", __FILE__)).chomp
end
end

if RUBY_VERSION < '1.9.3'
gem 'rake', '< 11.0.0' # rake 11 requires Ruby 1.9.3 or later
elsif RUBY_VERSION < '2.0.0'
gem 'rake', '< 12.0.0' # rake 12 requires Ruby 2.0.0 or later
else
gem 'rake', '>= 12.3.3'
end

if ENV['DIFF_LCS_VERSION']
gem 'diff-lcs', ENV['DIFF_LCS_VERSION']
else
gem 'diff-lcs', '~> 1.4', '>= 1.4.3'
end

if RUBY_VERSION < '2.3.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/)
gem "childprocess", "< 1.0.0"
elsif RUBY_VERSION < '2.3.0'
gem "childprocess", "< 3.0.0"
else
gem "childprocess", ">= 3.0.0"
end
gem "childprocess", ">= 3.0.0"
gem 'ffi', '~> 1.12.0'

### dep for ci/coverage
gem 'simplecov', '~> 0.8'

if RUBY_VERSION < '2.0.0' || RUBY_ENGINE == 'java'
gem 'json', '< 2.0.0' # is a dependency of simplecov
else
gem 'json', '> 2.3.0'
end

if RUBY_VERSION < '2.2.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/)
gem 'ffi', '< 1.10'
elsif RUBY_VERSION < '2.0'
# ffi dropped Ruby 1.8 support in 1.9.19 and Ruby 1.9 support in 1.11.0
gem 'ffi', '< 1.9.19'
elsif RUBY_VERSION < '2.3.0'
gem 'ffi', '~> 1.12.0'
else
gem 'ffi', '~> 1.13.0'
end

# No need to run rubocop on earlier versions
if RUBY_VERSION >= '2.4' && RUBY_ENGINE == 'ruby'
gem "rubocop", "~> 0.52.1"
end

eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom')
eval_gemfile 'Gemfile-custom' if File.exist?('Gemfile-custom')
49 changes: 11 additions & 38 deletions rspec-support/lib/rspec/support.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,13 @@ module Support
# hand, does a linear O(N) search over the dirs in the $LOAD_PATH until
# it can resolve the file relative to one of the dirs.
def self.define_optimized_require_for_rspec(lib, &require_relative)
name = "require_rspec_#{lib}"

if Kernel.respond_to?(:require_relative)
(class << self; self; end).__send__(:define_method, name) do |f|
require_relative.call("#{lib}/#{f}")
end
else
(class << self; self; end).__send__(:define_method, name) do |f|
require "rspec/#{lib}/#{f}"
end
define_singleton_method("require_rspec_#{lib}") do |f|
require_relative.call("#{lib}/#{f}")
end
end

define_optimized_require_for_rspec(:support) { |f| require_relative(f) }
require_rspec_support "version"
require_rspec_support "ruby_features"

# @api private
KERNEL_METHOD_METHOD = ::Kernel.instance_method(:method)
Expand All @@ -43,33 +34,15 @@ def self.define_optimized_require_for_rspec(lib, &require_relative)
# - BasicObject subclasses that mixin a Kernel dup (e.g. SimpleDelegator)
# - Objects that undefine method and delegate everything to another
# object (e.g. Mongoid association objects)
if RubyFeatures.supports_rebinding_module_methods?
def self.method_handle_for(object, method_name)
KERNEL_METHOD_METHOD.bind(object).call(method_name)
rescue NameError => original
begin
handle = object.method(method_name)
raise original unless handle.is_a? Method
handle
rescue Support::AllExceptionsExceptOnesWeMustNotRescue
raise original
end
end
else
def self.method_handle_for(object, method_name)
if ::Kernel === object
KERNEL_METHOD_METHOD.bind(object).call(method_name)
else
object.method(method_name)
end
rescue NameError => original
begin
handle = object.method(method_name)
raise original unless handle.is_a? Method
handle
rescue Support::AllExceptionsExceptOnesWeMustNotRescue
raise original
end
def self.method_handle_for(object, method_name)
KERNEL_METHOD_METHOD.bind(object).call(method_name)
rescue NameError => original
begin
handle = object.method(method_name)
raise original unless handle.is_a? Method
handle
rescue Support::AllExceptionsExceptOnesWeMustNotRescue
raise original
end
end

Expand Down
84 changes: 37 additions & 47 deletions rspec-support/lib/rspec/support/caller_filter.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
RSpec::Support.require_rspec_support "ruby_features"

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
Expand Down Expand Up @@ -27,56 +25,48 @@ class CallerFilter
# with this complexity in our `RSpec.deprecate` calls, so we ignore it here.
IGNORE_REGEX = Regexp.union(LIB_REGEX, "rubygems/core_ext/kernel_require.rb")

if RSpec::Support::RubyFeatures.caller_locations_supported?
# This supports args because it's more efficient when the caller specifies
# these. It allows us to skip frames the caller knows are part of RSpec,
# and to decrease the increment size if the caller is confident the line will
# be found in a small number of stack frames from `skip_frames`.
#
# Note that there is a risk to passing a `skip_frames` value that is too high:
# If it skippped the first non-rspec line, then this method would return the
# 2nd or 3rd (or whatever) non-rspec line. Thus, you generally shouldn't pass
# values for these parameters, particularly since most places that use this are
# not hot spots (generally it gets used for deprecation warnings). However,
# if you do have a hot spot that calls this, passing `skip_frames` can make
# a significant difference. Just make sure that that particular use is tested
# so that if the provided `skip_frames` changes to no longer be accurate in
# such a way that would return the wrong stack frame, a test will fail to tell you.
#
# See benchmarks/skip_frames_for_caller_filter.rb for measurements.
def self.first_non_rspec_line(skip_frames=3, increment=5)
# Why a default `skip_frames` of 3?
# By the time `caller_locations` is called below, the first 3 frames are:
# lib/rspec/support/caller_filter.rb:63:in `block in first_non_rspec_line'
# lib/rspec/support/caller_filter.rb:62:in `loop'
# lib/rspec/support/caller_filter.rb:62:in `first_non_rspec_line'
# This supports args because it's more efficient when the caller specifies
# these. It allows us to skip frames the caller knows are part of RSpec,
# and to decrease the increment size if the caller is confident the line will
# be found in a small number of stack frames from `skip_frames`.
#
# Note that there is a risk to passing a `skip_frames` value that is too high:
# If it skippped the first non-rspec line, then this method would return the
# 2nd or 3rd (or whatever) non-rspec line. Thus, you generally shouldn't pass
# values for these parameters, particularly since most places that use this are
# not hot spots (generally it gets used for deprecation warnings). However,
# if you do have a hot spot that calls this, passing `skip_frames` can make
# a significant difference. Just make sure that that particular use is tested
# so that if the provided `skip_frames` changes to no longer be accurate in
# such a way that would return the wrong stack frame, a test will fail to tell you.
#
# See benchmarks/skip_frames_for_caller_filter.rb for measurements.
def self.first_non_rspec_line(skip_frames=3, increment=5)
# Why a default `skip_frames` of 3?
# By the time `caller_locations` is called below, the first 3 frames are:
# lib/rspec/support/caller_filter.rb:63:in `block in first_non_rspec_line'
# lib/rspec/support/caller_filter.rb:62:in `loop'
# lib/rspec/support/caller_filter.rb:62:in `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.
# `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.

# The default increment of 5 for this method are mostly arbitrary, but
# is chosen to give good performance on the common case of creating a double.
# The default increment of 5 for this method are mostly arbitrary, but
# is chosen to give good performance on the common case of creating a double.

loop do
stack = caller_locations(skip_frames, increment)
raise "No non-lib lines in stack" unless stack
loop do
stack = caller_locations(skip_frames, increment)
raise "No non-lib lines in stack" unless stack

line = stack.find { |l| l.path !~ IGNORE_REGEX }
return line.to_s if line
line = stack.find { |l| l.path !~ IGNORE_REGEX }
return line.to_s if line

skip_frames += increment
increment *= 2 # The choice of two here is arbitrary.
end
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 !~ IGNORE_REGEX }
skip_frames += increment
increment *= 2 # The choice of two here is arbitrary.
end
end
end
Expand Down
10 changes: 2 additions & 8 deletions rspec-support/lib/rspec/support/differ.rb
Original file line number Diff line number Diff line change
Expand Up @@ -102,14 +102,8 @@ def diffably_stringify(array)
end
end

if String.method_defined?(:encoding)
def multiline?(string)
string.include?("\n".encode(string.encoding))
end
else
def multiline?(string)
string.include?("\n")
end
def multiline?(string)
string.include?("\n".encode(string.encoding))
end

def build_hunks(actual, expected)
Expand Down
2 changes: 0 additions & 2 deletions rspec-support/lib/rspec/support/directory_maker.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
RSpec::Support.require_rspec_support 'ruby_features'

module RSpec
module Support
# @api private
Expand Down
Loading

0 comments on commit 0e2f958

Please sign in to comment.