Skip to content

Commit

Permalink
reduce feature envy on Filter
Browse files Browse the repository at this point in the history
  • Loading branch information
dchelimsky committed Nov 5, 2011
1 parent 765d8c7 commit 799d2bd
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 66 deletions.
69 changes: 25 additions & 44 deletions lib/rspec/core/configuration.rb
Expand Up @@ -386,7 +386,7 @@ def get_files_to_run(files)
else
if file =~ /^(.*?)((?:\:\d+)+)$/
path, lines = $1, $2[1..-1].split(":").map{|n| n.to_i}
add_location path, lines
@filter.add_location path, lines
path
else
file
Expand Down Expand Up @@ -451,29 +451,31 @@ def alias_it_should_behave_like_to(new_name, report_label = '')
# to true and `args` includes any symbols that are not part of a hash,
# each symbol is treated as a key in the hash with the value `true`.
#
# ### Note
#
# Filters set using this method can be overridden from the command line
# or config files (e.g. `.rspec`).
#
# @example
# filter_run_including :x => 'y'
#
# # with treat_symbols_as_metadata_keys_with_true_values = true
# filter_run_including :foo # results in {:foo => true}
def filter_run_including(*args)
filter = build_metadata_hash_from(args)
if already_set_standalone_filter?
warn_already_set_standalone_filter(filter)
elsif contains_standalone_filter?(filter)
@filter.inclusions.replace(filter)
else
@filter.include :late, filter
end
@filter.include :low_priority, build_metadata_hash_from(args)
end

alias_method :filter_run, :filter_run_including

# Clears and reassigns the `inclusion_filter`. Set to `nil` if you don't
# want any inclusion filter at all.
#
# ### Warning
#
# This overrides any inclusion filters/tags set on the command line or in
# configuration files.
def inclusion_filter=(filter)
filter = build_metadata_hash_from([filter])
filter.empty? ? inclusion_filter.clear : inclusion_filter.replace(filter)
@filter.include :replace, build_metadata_hash_from([filter])
end

alias_method :filter=, :inclusion_filter=
Expand All @@ -491,20 +493,29 @@ def inclusion_filter
# to true and `args` excludes any symbols that are not part of a hash,
# each symbol is treated as a key in the hash with the value `true`.
#
# ### Note
#
# Filters set using this method can be overridden from the command line
# or config files (e.g. `.rspec`).
#
# @example
# filter_run_excluding :x => 'y'
#
# # with treat_symbols_as_metadata_keys_with_true_values = true
# filter_run_excluding :foo # results in {:foo => true}
def filter_run_excluding(*args)
@filter.exclude :late, build_metadata_hash_from(args)
@filter.exclude :low_priority, build_metadata_hash_from(args)
end

# Clears and reassigns the `exclusion_filter`. Set to `nil` if you don't
# want any exclusion filter at all.
#
# ### Warning
#
# This overrides any exclusion filters/tags set on the command line or in
# configuration files.
def exclusion_filter=(filter)
filter = build_metadata_hash_from([filter])
filter.empty? ? exclusion_filter.clear : exclusion_filter.replace(filter)
@filter.exclude :replace, build_metadata_hash_from([filter])
end

# Returns the `exclusion_filter`. If none has been set, returns an empty
Expand All @@ -513,18 +524,6 @@ def exclusion_filter
@filter.exclusions
end

STANDALONE_FILTERS = [:line_numbers, :full_description]

# @api private
def already_set_standalone_filter?
contains_standalone_filter?(inclusion_filter)
end

# @api private
def contains_standalone_filter?(filter)
STANDALONE_FILTERS.any? {|key| filter.has_key?(key)}
end

def include(mod, *args)
filters = build_metadata_hash_from(args)
include_or_extend_modules << [:include, mod, filters]
Expand Down Expand Up @@ -590,24 +589,6 @@ def value_for(key, default=nil)
@preferred_options.has_key?(key) ? @preferred_options[key] : default
end

def add_location(file_path, line_numbers)
# filter_locations is a hash of expanded paths to arrays of line
# numbers to match against. e.g.
# { "path/to/file.rb" => [37, 42] }
filter_locations = @filter.inclusions[:locations] ||= Hash.new {|h,k| h[k] = []}
@preferred_options.delete(:exclusion_filter)
@preferred_options.delete(:inclusion_filter)
exclusion_filter.clear
inclusion_filter.clear
filter_locations[File.expand_path(file_path)].push(*line_numbers)
filter_run(:locations => filter_locations)
end

def warn_already_set_standalone_filter(options)
warn "Filtering by #{options.inspect} is not possible since " \
"you are already filtering by #{inclusion_filter.inspect}"
end

def assert_no_example_groups_defined(config_option)
if RSpec.world.example_groups.any?
raise MustBeConfiguredBeforeExampleGroupsError.new(
Expand Down
35 changes: 31 additions & 4 deletions lib/rspec/core/filter.rb
Expand Up @@ -6,6 +6,8 @@ class Filter
:unless => lambda { |value| value }
}

STANDALONE_FILTERS = [:locations, :line_numbers, :full_description]

module Describable
PROC_HEX_NUMBER = /0x[0-9a-f]+@/
PROJECT_DIR = File.expand_path('.')
Expand Down Expand Up @@ -52,6 +54,17 @@ def initialize
extend(BackwardCompatibility)
end

def add_location(file_path, line_numbers)
# filter_locations is a hash of expanded paths to arrays of line
# numbers to match against. e.g.
# { "path/to/file.rb" => [37, 42] }
filter_locations = @inclusions[:locations] ||= Hash.new {|h,k| h[k] = []}
@exclusions.clear
@inclusions.clear
filter_locations[File.expand_path(file_path)].push(*line_numbers)
include :locations => filter_locations
end

def empty?
inclusions.empty? && exclusions.empty_without_conditional_filters?
end
Expand All @@ -71,22 +84,36 @@ def include?(example)
end

def exclude(*args)
@exclusions = update(@exclusions, @inclusions, *args)
update(@exclusions, @inclusions, *args)
end

def include(*args)
@inclusions = update(@inclusions, @exclusions, *args)
return if already_set_standalone_filter?

is_standalone_filter?(args.last) ? @inclusions.replace(args.last) : update(@inclusions, @exclusions, *args)
end

def update(orig, opposite, *updates)
if updates.length == 2
updated = updates.last.merge(orig)
opposite.each_key {|k| updated.delete(k)}
if updates[0] == :replace
updated = updates.last
else
updated = updates.last.merge(orig)
opposite.each_key {|k| updated.delete(k)}
end
orig.replace(updated)
else
orig.merge!(updates.last).each_key {|k| opposite.delete(k)}
end
end

def already_set_standalone_filter?
is_standalone_filter?(inclusions)
end

def is_standalone_filter?(filter)
STANDALONE_FILTERS.any? {|key| filter.has_key?(key)}
end
end
end
end
19 changes: 1 addition & 18 deletions spec/rspec/core/configuration_spec.rb
Expand Up @@ -656,23 +656,6 @@ def metadata_hash(*args)
config.inclusion_filter.should eq({})
end

it "warns if :line_numbers is already a filter" do
config.filter_run_including :line_numbers => [100]
config.should_receive(:warn).with(
"Filtering by {:foo=>true} is not possible since you " \
"are already filtering by {:line_numbers=>[100]}"
)
config.filter_run_including :foo => true
end

it "warns if :full_description is already a filter" do
config.filter_run_including :full_description => 'bar'
config.should_receive(:warn).with(
"Filtering by {:foo=>true} is not possible since you " \
"are already filtering by {:full_description=>\"bar\"}"
)
config.filter_run_including :foo => true
end
end

describe "#filter_run_excluding" do
Expand Down Expand Up @@ -773,7 +756,7 @@ def metadata_hash(*args)
end

describe "line_numbers=" do
before { config.stub(:warn) }
before { config.instance_variable_get("@filter").stub(:warn) }

it "sets the line numbers" do
config.line_numbers = ['37']
Expand Down
24 changes: 24 additions & 0 deletions spec/rspec/core/filter_spec.rb
Expand Up @@ -136,5 +136,29 @@ module RSpec::Core
filter.exclusions.description.should eq({ :unless => :custom_filter }.inspect)
end
end

it "clears the inclusion filter on include :line_numbers" do
filter = Filter.new
filter.stub(:warn)
filter.include :foo => :bar
filter.include :line_numbers => [100]
filter.inclusions.should eq(:line_numbers => [100])
end

it "clears the inclusion filter on include :locations" do
filter = Filter.new
# filter.stub(:warn)
filter.include :foo => :bar
filter.include :locations => { "path/to/file.rb" => [37] }
filter.inclusions.should eq(:locations => { "path/to/file.rb" => [37] })
end

it "clears the inclusion filter on include :full_description" do
filter = Filter.new
filter.stub(:warn)
filter.include :foo => :bar
filter.include :full_description => "this and that"
filter.inclusions.should eq(:full_description => "this and that")
end
end
end

0 comments on commit 799d2bd

Please sign in to comment.