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

Remove deprecated :example_group example metadata sub-hash #2851

Merged
merged 1 commit into from
Feb 10, 2021
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ Breaking Changes:
* Raise on usage of metadata on suite-level scopes. (Phil Pirozhkov, #2849)
* Raise an error when `fail_fast` is configured with
an unsupported value. (Phil Pirozhkov, #2849)
* Remove deprecated access to an example group's metadata through the example.
(Phil Pirozhkov, #2851)

Enhancements:

Expand Down
2 changes: 1 addition & 1 deletion benchmarks/module_inclusion_filtering.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ def initialize(*args)
end

def include(mod, *filters)
meta = RSpec::Core::Metadata.build_hash_from(filters, :warn_about_example_group_filtering)
meta = RSpec::Core::Metadata.build_hash_from(filters)
@include_extend_or_prepend_modules << [:include, mod, meta]
super
end
Expand Down
14 changes: 7 additions & 7 deletions lib/rspec/core/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1163,7 +1163,7 @@ def alias_it_behaves_like_to(new_name, report_label='')
#
# filter_run_including :foo # same as filter_run_including :foo => true
def filter_run_including(*args)
meta = Metadata.build_hash_from(args, :warn_about_example_group_filtering)
meta = Metadata.build_hash_from(args)
filter_manager.include_with_low_priority meta
static_config_filter_manager.include_with_low_priority Metadata.deep_hash_dup(meta)
end
Expand Down Expand Up @@ -1192,7 +1192,7 @@ def filter_run_when_matching(*args)
# This overrides any inclusion filters/tags set on the command line or in
# configuration files.
def inclusion_filter=(filter)
meta = Metadata.build_hash_from([filter], :warn_about_example_group_filtering)
meta = Metadata.build_hash_from([filter])
filter_manager.include_only meta
end

Expand Down Expand Up @@ -1237,7 +1237,7 @@ def inclusion_filter
#
# filter_run_excluding :foo # same as filter_run_excluding :foo => true
def filter_run_excluding(*args)
meta = Metadata.build_hash_from(args, :warn_about_example_group_filtering)
meta = Metadata.build_hash_from(args)
filter_manager.exclude_with_low_priority meta
static_config_filter_manager.exclude_with_low_priority Metadata.deep_hash_dup(meta)
end
Expand All @@ -1250,7 +1250,7 @@ def filter_run_excluding(*args)
# This overrides any exclusion filters/tags set on the command line or in
# configuration files.
def exclusion_filter=(filter)
meta = Metadata.build_hash_from([filter], :warn_about_example_group_filtering)
meta = Metadata.build_hash_from([filter])
filter_manager.exclude_only meta
end

Expand Down Expand Up @@ -1732,7 +1732,7 @@ def raise_errors_for_deprecations!
# end
# end
def define_derived_metadata(*filters, &block)
meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering)
meta = Metadata.build_hash_from(filters)
@derived_metadata_blocks.append(block, meta)
end

Expand All @@ -1755,7 +1755,7 @@ def define_derived_metadata(*filters, &block)
# end
# end
def when_first_matching_example_defined(*filters)
specified_meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering)
specified_meta = Metadata.build_hash_from(filters)

callback = lambda do |example_or_group_meta|
# Example groups do not have `:example_group` metadata
Expand Down Expand Up @@ -2194,7 +2194,7 @@ def define_mixed_in_module(mod, filters, mod_list, config_method, &block)
raise TypeError, "`RSpec.configuration.#{config_method}` expects a module but got: #{mod.inspect}"
end

meta = Metadata.build_hash_from(filters, :warn_about_example_group_filtering)
meta = Metadata.build_hash_from(filters)
mod_list.append(mod, meta)
on_existing_matching_groups(meta, &block)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/rspec/core/hooks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -571,7 +571,7 @@ def process(host, parent_groups, globals, position, scope)
def scope_and_options_from(*args)
return :suite if args.first == :suite
scope = extract_scope_from(args)
meta = Metadata.build_hash_from(args, :warn_about_example_group_filtering)
meta = Metadata.build_hash_from(args)
return scope, meta
end

Expand Down
116 changes: 7 additions & 109 deletions lib/rspec/core/metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,16 +77,11 @@ def self.ascend(metadata)
# Symbols are converted into hash keys with a value of `true`.
# This is done to support simple tagging using a symbol, rather
# than needing to do `:symbol => true`.
def self.build_hash_from(args, warn_about_example_group_filtering=false)
def self.build_hash_from(args)
hash = args.last.is_a?(Hash) ? args.pop : {}

hash[args.pop] = true while args.last.is_a?(Symbol)

if warn_about_example_group_filtering && hash.key?(:example_group)
RSpec.deprecate("Filtering by an `:example_group` subhash",
:replacement => "the subhash to filter directly")
end

hash
end

Expand Down Expand Up @@ -213,11 +208,6 @@ def ensure_valid_user_keys
class ExampleHash < HashPopulator
def self.create(group_metadata, user_metadata, index_provider, description, block)
example_metadata = group_metadata.dup
group_metadata = Hash.new(&ExampleGroupHash.backwards_compatibility_default_proc do |hash|
hash[:parent_example_group]
end)
group_metadata.update(example_metadata)

example_metadata[:execution_result] = Example::ExecutionResult.new
example_metadata[:example_group] = group_metadata
example_metadata[:shared_group_inclusion_backtrace] = SharedExampleGroupInclusionStackFrame.current_backtrace
Expand Down Expand Up @@ -246,59 +236,18 @@ def full_description
# @private
class ExampleGroupHash < HashPopulator
def self.create(parent_group_metadata, user_metadata, example_group_index, *args, &block)
group_metadata = hash_with_backwards_compatibility_default_proc

if parent_group_metadata
group_metadata.update(parent_group_metadata)
group_metadata[:parent_example_group] = parent_group_metadata
end
group_metadata =
if parent_group_metadata
{ **parent_group_metadata, :parent_example_group => parent_group_metadata }
else
{}
end

hash = new(group_metadata, user_metadata, example_group_index, args, block)
hash.populate
hash.metadata
end

def self.hash_with_backwards_compatibility_default_proc
Hash.new(&backwards_compatibility_default_proc { |hash| hash })
end

def self.backwards_compatibility_default_proc(&example_group_selector)
Proc.new do |hash, key|
case key
when :example_group
# We commonly get here when rspec-core is applying a previously
# configured filter rule, such as when a gem configures:
#
# RSpec.configure do |c|
# c.include MyGemHelpers, :example_group => { :file_path => /spec\/my_gem_specs/ }
# end
#
# It's confusing for a user to get a deprecation at this point in
# the code, so instead we issue a deprecation from the config APIs
# that take a metadata hash, and MetadataFilter sets this thread
# local to silence the warning here since it would be so
# confusing.
unless RSpec::Support.thread_local_data[:silence_metadata_example_group_deprecations]
RSpec.deprecate("The `:example_group` key in an example group's metadata hash",
:replacement => "the example group's hash directly for the " \
"computed keys and `:parent_example_group` to access the parent " \
"example group metadata")
end

group_hash = example_group_selector.call(hash)
LegacyExampleGroupHash.new(group_hash) if group_hash
when :example_group_block
RSpec.deprecate("`metadata[:example_group_block]`",
:replacement => "`metadata[:block]`")
hash[:block]
when :describes
RSpec.deprecate("`metadata[:describes]`",
:replacement => "`metadata[:described_class]`")
hash[:described_class]
end
end
end

private

def described_class
Expand Down Expand Up @@ -443,56 +392,5 @@ def attr_accessor(*names)
end
end
end

# @private
# Together with the example group metadata hash default block,
# provides backwards compatibility for the old `:example_group`
# key. In RSpec 2.x, the computed keys of a group's metadata
# were exposed from a nested subhash keyed by `[:example_group]`, and
# then the parent group's metadata was exposed by sub-subhash
# keyed by `[:example_group][:example_group]`.
#
# In RSpec 3, we reorganized this to that the computed keys are
# exposed directly of the group metadata hash (no nesting), and
# `:parent_example_group` returns the parent group's metadata.
#
# Maintaining backwards compatibility was difficult: we wanted
# `:example_group` to return an object that:
#
# * Exposes the top-level metadata keys that used to be nested
# under `:example_group`.
# * Supports mutation (rspec-rails, for example, assigns
# `metadata[:example_group][:described_class]` when you use
# anonymous controller specs) such that changes are written
# back to the top-level metadata hash.
# * Exposes the parent group metadata as
# `[:example_group][:example_group]`.
class LegacyExampleGroupHash
include HashImitatable

def initialize(metadata)
@metadata = metadata
parent_group_metadata = metadata.fetch(:parent_example_group) { {} }[:example_group]
self[:example_group] = parent_group_metadata if parent_group_metadata
end

def to_h
super.merge(@metadata)
end

private

def directly_supports_attribute?(name)
name != :example_group
end

def get_value(name)
@metadata[name]
end

def set_value(name, value)
@metadata[name] = value
end
end
end
end
45 changes: 12 additions & 33 deletions lib/rspec/core/metadata_filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,27 +14,17 @@ def apply?(predicate, filters, metadata)

# @private
def filter_applies?(key, filter_value, metadata)
silence_metadata_example_group_deprecations do
return location_filter_applies?(filter_value, metadata) if key == :locations
return id_filter_applies?(filter_value, metadata) if key == :ids
return filters_apply?(key, filter_value, metadata) if Hash === filter_value
return location_filter_applies?(filter_value, metadata) if key == :locations
return id_filter_applies?(filter_value, metadata) if key == :ids
return filters_apply?(key, filter_value, metadata) if Hash === filter_value

meta_value = metadata.fetch(key) { return false }
meta_value = metadata.fetch(key) { return false }

return true if TrueClass === filter_value && meta_value
return proc_filter_applies?(key, filter_value, metadata) if Proc === filter_value
return filter_applies_to_any_value?(key, filter_value, metadata) if Array === meta_value
return true if TrueClass === filter_value && meta_value
return proc_filter_applies?(key, filter_value, metadata) if Proc === filter_value
return filter_applies_to_any_value?(key, filter_value, metadata) if Array === meta_value

filter_value === meta_value || filter_value.to_s == meta_value.to_s
end
end

# @private
def silence_metadata_example_group_deprecations
RSpec::Support.thread_local_data[:silence_metadata_example_group_deprecations] = true
yield
ensure
RSpec::Support.thread_local_data.delete(:silence_metadata_example_group_deprecations)
filter_value === meta_value || filter_value.to_s == meta_value.to_s
end

private
Expand Down Expand Up @@ -72,7 +62,7 @@ def proc_filter_applies?(key, proc, metadata)

def filters_apply?(key, value, metadata)
subhash = metadata[key]
return false unless Hash === subhash || HashImitatable === subhash
return false unless Hash === subhash
value.all? { |k, v| filter_applies?(k, v, subhash) }
end
end
Expand Down Expand Up @@ -201,21 +191,10 @@ def handle_mutation(metadata)
@memoized_lookups.clear
end

# Ruby 2.3 and 2.4 do not have `Hash#slice`
def applicable_metadata_from(metadata)
MetadataFilter.silence_metadata_example_group_deprecations do
@applicable_keys.inject({}) do |hash, key|
# :example_group is treated special here because...
# - In RSpec 2, example groups had an `:example_group` key
# - In RSpec 3, that key is deprecated (it was confusing!).
# - The key is not technically present in an example group metadata hash
# (and thus would fail the `metadata.key?(key)` check) but a value
# is provided when accessed via the hash's `default_proc`
# - Thus, for backwards compatibility, we have to explicitly check
# for `:example_group` here if it is one of the keys being used to
# filter.
hash[key] = metadata[key] if metadata.key?(key) || key == :example_group
hash
end
@applicable_keys.each_with_object({}) do |key, hash|
hash[key] = metadata[key] if metadata.key?(key)
end
end

Expand Down
Loading