Skip to content
This repository has been archived by the owner on Nov 30, 2024. It is now read-only.

Drop Ruby < 2.3 support #1349

Merged
merged 26 commits into from
Dec 15, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
5b35444
Remove usages of module_prepends_supported?
pirj Nov 12, 2020
a5f22d9
Update required Ruby version gemspec constraint
pirj Nov 15, 2020
74ff06d
Remove explicit gem version constraints
pirj Nov 15, 2020
0bf0581
Remove feature support checks from specs
pirj Nov 15, 2020
5e064b8
Move require to where it's used
pirj Nov 15, 2020
8372c1d
Remove old Ruby specific workarounds
pirj Nov 15, 2020
3c5f54b
Get rid of old Ruby workarounds
pirj Nov 15, 2020
27e80fb
Remove unused require
pirj Nov 15, 2020
400ddcd
Get rid of old Ruby workarounds
pirj Nov 15, 2020
3635e9a
Add Changelog entry
pirj Nov 15, 2020
9a9430d
Remove 1.8, 1.9 workarounds
pirj Nov 15, 2020
f56b1a6
Remove tricky 1.9.2 workaround
pirj Nov 15, 2020
c834a7d
Remove 2.0 workarounds
pirj Nov 15, 2020
1338a68
Embrace 2.3+ workarounds
pirj Nov 15, 2020
7548ec5
Remove JRuby and 1.8 workarounds
pirj Nov 15, 2020
dc463ab
Use Mutex from RSpec Support
pirj Nov 12, 2020
a4370fc
Add clarification about JRuby 9.1 workaround
pirj Nov 23, 2020
4cd0faf
Remove appveyor workaround
pirj Nov 23, 2020
3af7d3a
Remove JRuby workaround
pirj Nov 23, 2020
54e44a9
Remove handle_restoration_failures
pirj Nov 23, 2020
3bc9780
Remove expect_output_warning_on_ruby_lt_2
pirj Nov 23, 2020
ea500ad
Relax, BasicObject is defined
pirj Nov 23, 2020
d968590
Prefer alias_method inside DSL
pirj Nov 27, 2020
b78355f
Reduce minumum coverage
pirj Dec 8, 2020
d02dd01
Pin ffi due to their usage of Gem in 1.13.0/1.13.1
pirj Dec 12, 2020
bae42c7
Update lib/rspec/mocks/method_double.rb
pirj Dec 14, 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, #1349)

### 3.10.0 / 2020-10-30
[Full Changelog](http://github.com/rspec/rspec-mocks/compare/v3.9.1...v3.10.0)

Expand Down
48 changes: 4 additions & 44 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,46 +12,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.2'
end

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

gem 'ffi', '~> 1.12.0'

gem 'yard', '~> 0.9.24', :require => false

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

# allow gems to be installed on older rubies and/or windows
if RUBY_VERSION < '2.2.0' && !!(RbConfig::CONFIG['host_os'] =~ /cygwin|mswin|mingw|bccwin|wince|emx/)
gem 'ffi', '< 1.10'
elsif RUBY_VERSION < '1.9'
gem 'ffi', '< 1.9.19' # ffi dropped Ruby 1.8 support in 1.9.19
elsif RUBY_VERSION < '2.0'
gem 'ffi', '< 1.11.0' # ffi dropped Ruby 1.9 support in 1.11.0
else
gem 'ffi', '> 1.9.24' # prevent Github security vulnerability warning
end

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

if RUBY_VERSION < '1.9.2'
gem 'contracts', '~> 0.15.0' # is a dependency of aruba
end

# Version 5.12 of minitest requires Ruby 2.4
if RUBY_VERSION < '2.4.0'
gem 'minitest', '< 5.12.0'
Expand All @@ -65,21 +40,6 @@ end

gem 'simplecov', '~> 0.8'

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

platforms :jruby do
if RUBY_VERSION < '1.9.0'
# Pin jruby-openssl on older J Ruby
gem "jruby-openssl", "< 0.10.0"
# Pin child-process on older J Ruby
gem "childprocess", "< 1.0.0"
else
gem "jruby-openssl"
end
end
gem "jruby-openssl", platforms: [:jruby]

eval File.read('Gemfile-custom') if File.exist?('Gemfile-custom')
eval_gemfile 'Gemfile-custom' if File.exist?('Gemfile-custom')
9 changes: 1 addition & 8 deletions Gemfile-custom.sample
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,10 @@ group :development do
gem 'relish', '~> 0.6.0'
gem 'guard-rspec', '~> 1.2.1'
gem 'growl', '1.0.3'
gem 'spork', '0.9.0'

platform :mri do
gem 'rb-fsevent', '~> 0.9.0'
gem 'ruby-prof', '~> 0.10.0'

case RUBY_VERSION
when /^1.8/
gem 'ruby-debug'
when /^1.9/
gem 'debugger'
end
gem 'pry-byebug'
end
end
1 change: 0 additions & 1 deletion lib/rspec/mocks.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
require 'rspec/support'
RSpec::Support.require_rspec_support 'caller_filter'
RSpec::Support.require_rspec_support 'warnings'
RSpec::Support.require_rspec_support 'ruby_features'
JonRowe marked this conversation as resolved.
Show resolved Hide resolved

RSpec::Support.define_optimized_require_for_rspec(:mocks) { |f| require_relative f }

Expand Down
10 changes: 2 additions & 8 deletions lib/rspec/mocks/any_instance/proxy.rb
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,8 @@ def initialize(targets)
@targets = targets
end

if RUBY_VERSION.to_f > 1.8
def respond_to_missing?(method_name, include_private=false)
super || @targets.first.respond_to?(method_name, include_private)
end
else
def respond_to?(method_name, include_private=false)
super || @targets.first.respond_to?(method_name, include_private)
end
def respond_to_missing?(method_name, include_private=false)
super || @targets.first.respond_to?(method_name, include_private)
end

def method_missing(*args, &block)
Expand Down
33 changes: 11 additions & 22 deletions lib/rspec/mocks/any_instance/recorder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -200,24 +200,17 @@ def restore_original_method!(method_name)
remove_method method_name

# A @klass can have methods implemented (see Method#owner) in @klass
# or inherited from a superclass. In ruby 2.2 and earlier, we can copy
# a method regardless of the 'owner' and restore it to @klass after
# because a call to 'super' from @klass's copied method would end up
# calling the original class's superclass's method.
# or inherited from a superclass.
#
# With the commit below, available starting in 2.3.0, ruby changed
# this behavior and a call to 'super' from the method copied to @klass
# A call to 'super' from the method copied to @klass
# will call @klass's superclass method, which is the original
# implementer of this method! This leads to very strange errors
# if @klass's copied method calls 'super', since it would end up
# calling itself, the original method implemented in @klass's
# superclass.
#
# For ruby 2.3 and above, we need to only restore methods that
# @klass originally owned.
#
# https://github.com/ruby/ruby/commit/c8854d2ca4be9ee6946e6d17b0e17d9ef130ee81
if RUBY_VERSION < "2.3" || backed_up_method_owner[method_name.to_sym] == self
# We need to only restore methods that @klass originally owned.
if backed_up_method_owner[method_name.to_sym] == self
alias_method method_name, alias_method_name
end
remove_method alias_method_name
Expand Down Expand Up @@ -257,6 +250,7 @@ def observe!(method_name)
@observed_methods << method_name
backup_method!(method_name)
recorder = self
# In Ruby 2.4 and earlier, `define_method` is private
@klass.__send__(:define_method, method_name) do |*args, &blk|
recorder.playback!(self, method_name)
__send__(method_name, *args, &blk)
Expand All @@ -266,6 +260,7 @@ def observe!(method_name)
def mark_invoked!(method_name)
backup_method!(method_name)
recorder = self
# In Ruby 2.4 and earlier, `define_method` is private
@klass.__send__(:define_method, method_name) do |*_args, &_blk|
invoked_instance = recorder.instance_that_received(method_name)
inspect = "#<#{self.class}:#{object_id} #{instance_variables.map { |name| "#{name}=#{instance_variable_get name}" }.join(', ')}>"
Expand All @@ -275,18 +270,12 @@ def mark_invoked!(method_name)
end
end

if Support::RubyFeatures.module_prepends_supported?
def allow_no_prepended_module_definition_of(method_name)
prepended_modules = RSpec::Mocks::Proxy.prepended_modules_of(@klass)
problem_mod = prepended_modules.find { |mod| mod.method_defined?(method_name) }
return unless problem_mod
def allow_no_prepended_module_definition_of(method_name)
prepended_modules = RSpec::Mocks::Proxy.prepended_modules_of(@klass)
problem_mod = prepended_modules.find { |mod| mod.method_defined?(method_name) }
return unless problem_mod

AnyInstance.error_generator.raise_not_supported_with_prepend_error(method_name, problem_mod)
end
else
def allow_no_prepended_module_definition_of(_method_name)
# nothing to do; prepends aren't supported on this version of ruby
end
AnyInstance.error_generator.raise_not_supported_with_prepend_error(method_name, problem_mod)
end
end
end
Expand Down
1 change: 1 addition & 0 deletions lib/rspec/mocks/error_generator.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
RSpec::Support.require_rspec_support "object_formatter"
RSpec::Support.require_rspec_support 'ruby_features'
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See other comment, now if we need this again elsewhere we'll have to remember to require it / move it back.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's what I did, went through all our files and added/removed it to only be required where used.


module RSpec
module Mocks
Expand Down
121 changes: 22 additions & 99 deletions lib/rspec/mocks/instance_method_stasher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,93 +5,41 @@ class InstanceMethodStasher
def initialize(object, method)
@object = object
@method = method
# We don't want to create singleton class if it doesn't exist,
# so we don't use `object.singleton_class`.
@klass = (class << object; self; end)

@original_method = nil
@method_is_stashed = false
end

attr_reader :original_method

if RUBY_VERSION.to_f < 1.9
# @private
def method_is_stashed?
@method_is_stashed
end

# @private
def stash
return if !method_defined_directly_on_klass? || @method_is_stashed

@klass.__send__(:alias_method, stashed_method_name, @method)
@method_is_stashed = true
end

# @private
def stashed_method_name
"obfuscated_by_rspec_mocks__#{@method}"
end

# @private
def restore
return unless @method_is_stashed
# @private
def method_is_stashed?
!!@original_method
end

if @klass.__send__(:method_defined?, @method)
@klass.__send__(:undef_method, @method)
end
@klass.__send__(:alias_method, @method, stashed_method_name)
@klass.__send__(:remove_method, stashed_method_name)
@method_is_stashed = false
end
else
# @private
def stash
return unless method_defined_directly_on_klass?
@original_method ||= ::RSpec::Support.method_handle_for(@object, @method)
# In Ruby 2.4 and earlier, `undef_method` is private
@klass.__send__(:undef_method, @method)
end

# @private
def method_is_stashed?
!!@original_method
end
# @private
def restore
return unless @original_method

# @private
def stash
return unless method_defined_directly_on_klass?
@original_method ||= ::RSpec::Support.method_handle_for(@object, @method)
if @klass.method_defined?(@method)
# In Ruby 2.4 and earlier, `undef_method` is private
@klass.__send__(:undef_method, @method)
end

# @private
def restore
return unless @original_method
# In Ruby 2.4 and earlier, `define_method` is private
@klass.__send__(:define_method, @method, @original_method)

if @klass.__send__(:method_defined?, @method)
@klass.__send__(:undef_method, @method)
end

handle_restoration_failures do
@klass.__send__(:define_method, @method, @original_method)
end

@original_method = nil
end
end

if RUBY_DESCRIPTION.include?('2.0.0p247') || RUBY_DESCRIPTION.include?('2.0.0p195')
# ruby 2.0.0-p247 and 2.0.0-p195 both have a bug that we can't work around :(.
# https://bugs.ruby-lang.org/issues/8686
def handle_restoration_failures
yield
rescue TypeError
RSpec.warn_with(
"RSpec failed to properly restore a partial double (#{@object.inspect}) " \
"to its original state due to a known bug in MRI 2.0.0-p195 & p247 " \
"(https://bugs.ruby-lang.org/issues/8686). This object may remain " \
"screwed up for the rest of this process. Please upgrade to 2.0.0-p353 or above.",
:call_site => nil, :use_spec_location_as_call_site => true
)
end
else
def handle_restoration_failures
# No known reasons for restoration to fail on other rubies.
yield
end
@original_method = nil
end

private
Expand All @@ -109,36 +57,11 @@ def method_defined_on_klass?(klass=@klass)
def method_owned_by_klass?
owner = @klass.instance_method(@method).owner

# On Ruby 2.0.0+ the owner of a method on a class which has been
# The owner of a method on a class which has been
# `prepend`ed may actually be an instance, e.g.
# `#<MyClass:0x007fbb94e3cd10>`, rather than the expected `MyClass`.
owner = owner.class unless Module === owner

# On some 1.9s (e.g. rubinius) aliased methods
# can report the wrong owner. Example:
# class MyClass
# class << self
# alias alternate_new new
# end
# end
#
# MyClass.owner(:alternate_new) returns `Class` when incorrect,
# but we need to consider the owner to be `MyClass` because
# it is not actually available on `Class` but is on `MyClass`.
# Hence, we verify that the owner actually has the method defined.
# If the given owner does not have the method defined, we assume
# that the method is actually owned by @klass.
#
# On 1.8, aliased methods can also report the wrong owner. Example:
# module M
# def a; end
# module_function :a
# alias b a
# module_function :b
# end
# The owner of M.b is the raw Module object, instead of the expected
# singleton class of the module
return true if RUBY_VERSION < '1.9' && owner == @object
owner == @klass || !(method_defined_on_klass?(owner))
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rspec/mocks/message_expectation.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
RSpec::Support.require_rspec_support 'mutex'
pirj marked this conversation as resolved.
Show resolved Hide resolved
RSpec::Support.require_rspec_support 'reentrant_mutex'

module RSpec
module Mocks
Expand Down
Loading