module Rspec module Core class Metadata < Hash def self.process(superclass_metadata, *args) new(superclass_metadata) do |metadata| metadata.process(*args) end end attr_reader :superclass_metadata def initialize(superclass_metadata=nil) @superclass_metadata = superclass_metadata update(@superclass_metadata) if @superclass_metadata store(:example_group, {}) store(:behaviour, self[:example_group]) yield self if block_given? end def process(*args) extra_metadata = args.last.is_a?(Hash) ? args.pop : {} extra_metadata.delete(:example_group) # Remove it when present to prevent it clobbering the one we setup extra_metadata.delete(:behaviour) # Remove it when present to prevent it clobbering the one we setup self[:example_group][:describes] = args.shift unless args.first.is_a?(String) self[:example_group][:describes] ||= self.superclass_metadata && self.superclass_metadata[:example_group][:describes] self[:example_group][:description] = args.shift || '' self[:example_group][:name] = determine_name self[:example_group][:block] = extra_metadata.delete(:example_group_block) self[:example_group][:caller] = extra_metadata.delete(:caller) self[:example_group][:file_path] = file_path_from(self[:example_group], extra_metadata.delete(:file_path)) self[:example_group][:line_number] = line_number_from(self[:example_group], extra_metadata.delete(:line_number)) self[:example_group][:location] = location_from(self[:example_group]) update(extra_metadata) end def for_example(description, options) dup.configure_for_example(description,options) end def configure_for_example(description, options) store(:description, description.to_s) store(:full_description, "#{self[:example_group][:name]} #{self[:description]}") store(:execution_result, {}) store(:caller, options.delete(:caller)) if self[:caller] store(:file_path, file_path_from(self)) store(:line_number, line_number_from(self)) end self[:location] = location_from(self) update(options) self end def apply_condition(filter_on, filter, metadata=nil) metadata ||= self case filter when Hash filter.all? { |k, v| apply_condition(k, v, metadata[filter_on]) } when Regexp metadata[filter_on] =~ filter when Proc filter.call(metadata[filter_on]) rescue false when Fixnum if filter_on == :line_number [metadata[:line_number],metadata[:example_group][:line_number]].include?(filter) else metadata[filter_on] == filter end else metadata[filter_on] == filter end end def all_apply?(filters) filters.all? do |filter_on, filter| apply_condition(filter_on, filter) end end private def file_path_from(metadata, given_file_path=nil) given_file_path || file_and_line_number(metadata)[0].strip end def line_number_from(metadata, given_line_number=nil) given_line_number || file_and_line_number(metadata)[1].to_i end def location_from(metadata) "#{metadata[:file_path]}:#{metadata[:line_number]}" end def file_and_line_number(metadata) candidate_entries_from_caller(metadata).first.split(':') end def candidate_entries_from_caller(metadata) metadata[:caller].grep(/\_spec\.rb:/i) end def determine_name if superclass_metadata && superclass_metadata[:example_group][:name] self[:example_group][:name] = "#{superclass_metadata[:example_group][:name]} #{self[:example_group][:description]}".strip else self[:example_group][:name] = "#{self[:example_group][:describes]} #{self[:example_group][:description]}".strip end end end end end