Skip to content

Commit

Permalink
[core] Merge pull request rspec/rspec-core#2851 from rspec/remove-exa…
Browse files Browse the repository at this point in the history
…mple_group-subhash

Remove deprecated `:example_group` example metadata sub-hash

---
This commit was imported from rspec/rspec-core@0ea90bb.
  • Loading branch information
pirj authored Feb 10, 2021
2 parents 8f181d6 + 28c4ca1 commit 282e20a
Show file tree
Hide file tree
Showing 8 changed files with 35 additions and 370 deletions.
2 changes: 2 additions & 0 deletions rspec-core/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 rspec-core/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 rspec-core/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 rspec-core/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 rspec-core/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 rspec-core/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

0 comments on commit 282e20a

Please sign in to comment.