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

Commit

Permalink
Add config.define_derived_metadata.
Browse files Browse the repository at this point in the history
Fixes #969 and supercedes #1089.
  • Loading branch information
myronmarston committed Apr 22, 2014
1 parent 6b67068 commit ebf9c80
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,9 @@ Enhancements:
* Add `disable_monkey_patching!` config option that disables all monkey
patching from whatever pieces of RSpec you use. (Alexey Fedorov)
* Add `Pathname` support for setting all output streams. (Aaron Kromer)
* Add `config.define_derived_metadata`, which can be used to apply
additional metadata to all groups or examples that match a given
filter. (Myron Marston)

Bug Fixes:

Expand Down
27 changes: 27 additions & 0 deletions lib/rspec/core/configuration.rb
Original file line number Diff line number Diff line change
Expand Up @@ -300,6 +300,7 @@ def initialize
@profile_examples = false
@requires = []
@libs = []
@derived_metadata_blocks = []
end

# @private
Expand Down Expand Up @@ -1209,6 +1210,32 @@ def disable_monkey_patching!
# @private
attr_accessor :disable_monkey_patching

# Defines a callback that can assign derived metadata values.
#
# @param filters [Array<Symbol>, Hash] metadata filters that determine which example
# or group metadata hashes the callback will be triggered for. If none are given,
# the callback will be run against the metadata hashes of all groups and examples.
# @yieldparam metadata [Hash] original metadata hash from an example or group. Mutate this in
# your block as needed.
#
# @example
# RSpec.configure do |config|
# # Tag all groups and examples in the spec/unit directory with :type => :unit
# config.define_derived_metadata(:file_path => %r{/spec/unit/}) do |metadata|
# metadata[:type] = :unit
# end
# end
def define_derived_metadata(*filters, &block)
@derived_metadata_blocks << [Metadata.build_hash_from(filters), block]
end

# @private
def apply_derived_metadata_to(metadata)
@derived_metadata_blocks.each do |filter, block|
block.call(metadata) if filter.empty? || MetadataFilter.any_apply?(filter, metadata)
end
end

private

def get_files_to_run(paths)
Expand Down
1 change: 1 addition & 0 deletions lib/rspec/core/metadata.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def populate

populate_location_attributes
metadata.update(user_metadata)
RSpec.configuration.apply_derived_metadata_to(metadata)
end

private
Expand Down
101 changes: 101 additions & 0 deletions spec/rspec/core/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -1077,6 +1077,107 @@ def metadata_hash(*args)
end
end

describe "#define_derived_metadata" do
it 'allows the provided block to mutate example group metadata' do
RSpec.configuration.define_derived_metadata do |metadata|
metadata[:reverse_description] = metadata[:description].reverse
end

group = RSpec.describe("My group")
expect(group.metadata).to include(:description => "My group", :reverse_description => "puorg yM")
end

it 'allows the provided block to mutate example metadata' do
RSpec.configuration.define_derived_metadata do |metadata|
metadata[:reverse_description] = metadata[:description].reverse
end

ex = RSpec.describe("My group").example("foo")
expect(ex.metadata).to include(:description => "foo", :reverse_description => "oof")
end

it 'allows multiple configured blocks to be applied, in order of definition' do
RSpec.configure do |c|
c.define_derived_metadata { |m| m[:b1_desc] = m[:description] + " (block 1)" }
c.define_derived_metadata { |m| m[:b2_desc] = m[:b1_desc] + " (block 2)" }
end

group = RSpec.describe("bar")
expect(group.metadata).to include(:b1_desc => "bar (block 1)", :b2_desc => "bar (block 1) (block 2)")
end

it "derives metadata before the group or example blocks are eval'd so their logic can depend on the derived metadata" do
RSpec.configure do |c|
c.define_derived_metadata(:foo) do |metadata|
metadata[:bar] = "bar"
end
end

group_bar_value = example_bar_value = nil

RSpec.describe "Group", :foo do
group_bar_value = metadata[:bar]
example_bar_value = example("ex", :foo).metadata[:bar]
end

expect(group_bar_value).to eq("bar")
expect(example_bar_value).to eq("bar")
end

context "when passed a metadata filter" do
it 'only applies to the groups and examples that match that filter' do
RSpec.configure do |c|
c.define_derived_metadata(:apply => true) do |metadata|
metadata[:reverse_description] = metadata[:description].reverse
end
end

g1 = RSpec.describe("G1", :apply)
g2 = RSpec.describe("G2")
e1 = g1.example("E1")
e2 = g2.example("E2", :apply)
e3 = g2.example("E3")

expect(g1.metadata).to include(:reverse_description => "1G")
expect(g2.metadata).not_to include(:reverse_description)

expect(e1.metadata).to include(:reverse_description => "1E")
expect(e2.metadata).to include(:reverse_description => "2E")
expect(e3.metadata).not_to include(:reverse_description)
end

it 'applies if any of multiple filters apply (to align with module inclusion semantics)' do
RSpec.configure do |c|
c.define_derived_metadata(:a => 1, :b => 2) do |metadata|
metadata[:reverse_description] = metadata[:description].reverse
end
end

g1 = RSpec.describe("G1", :a => 1)
g2 = RSpec.describe("G2", :b => 2)
g3 = RSpec.describe("G3", :c => 3)

expect(g1.metadata).to include(:reverse_description => "1G")
expect(g2.metadata).to include(:reverse_description => "2G")
expect(g3.metadata).not_to include(:reverse_description)
end

it 'allows a metadata filter to be passed as a raw symbol' do
RSpec.configure do |c|
c.define_derived_metadata(:apply) do |metadata|
metadata[:reverse_description] = metadata[:description].reverse
end
end

g1 = RSpec.describe("G1", :apply)
g2 = RSpec.describe("G2")

expect(g1.metadata).to include(:reverse_description => "1G")
expect(g2.metadata).not_to include(:reverse_description)
end
end
end

describe "#add_setting" do
describe "with no modifiers" do
context "with no additional options" do
Expand Down

0 comments on commit ebf9c80

Please sign in to comment.