Skip to content

Commit

Permalink
Implement MatcherDelegator using the BlankSlate pattern
Browse files Browse the repository at this point in the history
This allows to minimize the amount of method inherited by
the delegator object, and protects RSpec from new methods
being introduced by future Ruby versions or monkey patched
in by user code or libraries.

On modern Rubies there is `BasicObject` for this purpose,
that's what `delegate.rb` uses for instance, and is considered
best practice for delegators and other proxy objects. However
since RSpec still support Ruby 1.8 so it's not an option.

Additionally `BasicObject` doesn't inherit from `Object` so
all constant resolutions in a `BasicObject` descendant must
be fully qualified which is tedious, but more importantly
changing this now would probably break third party code that
inherit from `MatcherDelegator`.
  • Loading branch information
byroot committed Nov 8, 2023
1 parent 562d327 commit 3d97c7e
Showing 1 changed file with 27 additions and 1 deletion.
28 changes: 27 additions & 1 deletion lib/rspec/matchers/matcher_delegator.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,34 @@
module RSpec
module Matchers
# Provides a base class with as little methods as possible, so that
# most methods can be delegated via `method_missing`.
#
# On Ruby 2.0+ BasicObject could be used for this purpose, but it
# introduce some extra complexity with constant resolution, so the
# BlankSlate pattern was prefered.
# @private
class BaseDelegator
kept_methods = [
# Methods that raise warnings if removed.
:__id__, :__send__, :object_id,

# Methods that are explicitly undefined in some subclasses.
:==, :===,

# Methods we keep on purpose.
:class, :respond_to?, :__method__, :method, :dup,
:clone, :initialize_dup, :initialize_copy, :initialize_clone,
]
instance_methods.each do |method|
unless kept_methods.include?(method.to_sym)
undef_method(method)
end
end
end

# Provides the necessary plumbing to wrap a matcher with a decorator.
# @private
class MatcherDelegator
class MatcherDelegator < BaseDelegator
include Composable
attr_reader :base_matcher

Expand Down

0 comments on commit 3d97c7e

Please sign in to comment.