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

Drop Ruby < 2.3 support #436

Merged
merged 39 commits into from
Dec 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
39 commits
Select commit Hold shift + click to select a range
75149f3
Simplify fork_supported?
pirj Nov 12, 2020
7c2c610
Simplify ripper_supported?
pirj Nov 12, 2020
2f3489e
Remove redundant jruby_9000?
pirj Nov 12, 2020
3842f17
Remove kw_args_supported?
pirj Nov 12, 2020
26ca803
Remove supports_rebinding_module_methods?
pirj Nov 12, 2020
514ce68
Remove required_kw_args_supported?
pirj Nov 12, 2020
87dca93
Always use require_relative
pirj Nov 12, 2020
8109750
Remove optional_and_splat_args_supported?
pirj Nov 12, 2020
ba17eca
Remove caller_locations_supported?
pirj Nov 12, 2020
a372762
Remove supports_exception_cause?
pirj Nov 12, 2020
67f560d
Remove unused module_refinement_supported?
pirj Nov 12, 2020
c6257e9
Remove JRuby arity check workaround
pirj Nov 12, 2020
2a9c58a
Remove 1.8.7-specific workaround
pirj Nov 12, 2020
cd8b41e
Remove JRuby in 1.8.7 compat mode workaround
pirj Nov 12, 2020
ad44a2e
Remove 1.9 Open3 workaround
pirj Nov 12, 2020
ffd4518
Remove module_prepends_supported?
pirj Nov 12, 2020
1d1270d
Remove String#encoding workarounds
pirj Nov 12, 2020
3496910
Remove Time#nsec workaround
pirj Nov 12, 2020
81e4713
Remove 1.9.2-specific workaround
pirj Nov 12, 2020
e91cc38
Simplify a macro definition
pirj Nov 12, 2020
7e36bd3
Update required Ruby version gemspec constraint
pirj Nov 12, 2020
e072219
Remove explicit gem version constraints
pirj Nov 12, 2020
9939949
Remove assorted older rubies workarounds
pirj Nov 12, 2020
b9c5674
Explicitly require ruby_features where needed
pirj Nov 12, 2020
69288f3
Remove const_defined? workaround
pirj Nov 12, 2020
1b2467f
Remove version-specific details from a doc
pirj Nov 12, 2020
b66b7c8
Remove old JRuby workaround
pirj Nov 12, 2020
571a692
Remove usages of jruby_9000
pirj Nov 12, 2020
0c3c74e
Remove non_mri?
pirj Nov 12, 2020
a92dc58
Add Changelog entry
pirj Nov 14, 2020
b69244f
Get rid of evals
pirj Nov 15, 2020
fdd801c
Fix offences
pirj Nov 15, 2020
d181a28
Use Mutex from core, prevent it from being stubbed
pirj Nov 18, 2020
dd56e6f
Remove StdErrSplitter workarounds
pirj Nov 18, 2020
38885cf
Remove redundant boilerplate
pirj Nov 22, 2020
a9ab522
Remove BasicObject checks
pirj Nov 23, 2020
048eedd
Remove filtering for unsupported rubies
pirj Dec 8, 2020
a787fa2
Fix code doc
pirj Dec 8, 2020
0d41dfa
Downgrade ffi due to their usage of Gem
pirj Dec 12, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions 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 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 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
pirj marked this conversation as resolved.
Show resolved Hide resolved

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 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 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 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