rspec-core 2.6.0 → 2.7.0.rc1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. data/README.md +1 -1
  2. data/features/Upgrade.md +11 -0
  3. data/features/command_line/exit_status.feature +20 -3
  4. data/features/command_line/format_option.feature +8 -0
  5. data/features/command_line/line_number_appended_to_path.feature +35 -1
  6. data/features/command_line/line_number_option.feature +16 -3
  7. data/features/command_line/pattern_option.feature +31 -0
  8. data/features/command_line/rake_task.feature +1 -1
  9. data/features/command_line/ruby.feature +22 -0
  10. data/features/configuration/default_path.feature +38 -0
  11. data/features/example_groups/{shared_example_group.feature → shared_examples.feature} +49 -26
  12. data/features/expectation_framework_integration/configure_expectation_framework.feature +1 -1
  13. data/features/filtering/inclusion_filters.feature +4 -5
  14. data/features/formatters/text_formatter.feature +16 -13
  15. data/features/helper_methods/let.feature +4 -4
  16. data/features/hooks/around_hooks.feature +1 -1
  17. data/features/hooks/before_and_after_hooks.feature +3 -3
  18. data/features/hooks/filtering.feature +13 -6
  19. data/features/metadata/user_defined.feature +12 -10
  20. data/features/pending/pending_examples.feature +21 -8
  21. data/features/step_definitions/additional_cli_steps.rb +1 -1
  22. data/features/subject/attribute_of_subject.feature +2 -2
  23. data/features/support/env.rb +1 -2
  24. data/lib/rspec/core/backward_compatibility.rb +1 -1
  25. data/lib/rspec/core/configuration.rb +71 -16
  26. data/lib/rspec/core/configuration_options.rb +46 -16
  27. data/lib/rspec/core/{extensions/object.rb → dsl.rb} +2 -4
  28. data/lib/rspec/core/example.rb +10 -7
  29. data/lib/rspec/core/example_group.rb +34 -18
  30. data/lib/rspec/core/expecting/with_rspec.rb +0 -2
  31. data/lib/rspec/core/extensions.rb +0 -1
  32. data/lib/rspec/core/formatters/base_formatter.rb +8 -4
  33. data/lib/rspec/core/formatters/base_text_formatter.rb +38 -18
  34. data/lib/rspec/core/formatters/html_formatter.rb +3 -1
  35. data/lib/rspec/core/formatters/snippet_extractor.rb +9 -3
  36. data/lib/rspec/core/formatters/text_mate_formatter.rb +20 -6
  37. data/lib/rspec/core/hooks.rb +7 -6
  38. data/lib/rspec/core/let.rb +5 -5
  39. data/lib/rspec/core/metadata.rb +136 -94
  40. data/lib/rspec/core/metadata_hash_builder.rb +1 -1
  41. data/lib/rspec/core/option_parser.rb +11 -7
  42. data/lib/rspec/core/pending.rb +2 -1
  43. data/lib/rspec/core/rake_task.rb +26 -15
  44. data/lib/rspec/core/reporter.rb +5 -6
  45. data/lib/rspec/core/runner.rb +1 -1
  46. data/lib/rspec/core/shared_example_group.rb +4 -4
  47. data/lib/rspec/core/subject.rb +7 -7
  48. data/lib/rspec/core/version.rb +1 -1
  49. data/lib/rspec/core/world.rb +9 -13
  50. data/lib/rspec/core.rb +14 -21
  51. data/spec/autotest/discover_spec.rb +2 -2
  52. data/spec/autotest/failed_results_re_spec.rb +29 -21
  53. data/spec/autotest/rspec_spec.rb +3 -3
  54. data/spec/rspec/core/command_line_spec.rb +1 -6
  55. data/spec/rspec/core/configuration_options_spec.rb +111 -27
  56. data/spec/rspec/core/configuration_spec.rb +161 -37
  57. data/spec/rspec/core/deprecations_spec.rb +2 -2
  58. data/spec/rspec/core/drb_command_line_spec.rb +6 -6
  59. data/spec/rspec/core/example_group_spec.rb +197 -61
  60. data/spec/rspec/core/example_spec.rb +33 -16
  61. data/spec/rspec/core/formatters/base_formatter_spec.rb +3 -3
  62. data/spec/rspec/core/formatters/base_text_formatter_spec.rb +222 -71
  63. data/spec/rspec/core/formatters/helpers_spec.rb +8 -8
  64. data/spec/rspec/core/formatters/html_formatted-1.8.7-jruby.html +90 -26
  65. data/spec/rspec/core/formatters/html_formatted-1.8.7.html +12 -11
  66. data/spec/rspec/core/formatters/html_formatted-1.9.2.html +12 -11
  67. data/spec/rspec/core/formatters/{html_formatted-1.9.1.html → html_formatted-1.9.3.html} +12 -11
  68. data/spec/rspec/core/formatters/html_formatter_spec.rb +5 -5
  69. data/spec/rspec/core/formatters/progress_formatter_spec.rb +2 -2
  70. data/spec/rspec/core/formatters/snippet_extractor_spec.rb +2 -2
  71. data/spec/rspec/core/formatters/text_mate_formatted-1.8.7-jruby.html +91 -27
  72. data/spec/rspec/core/formatters/text_mate_formatted-1.8.7.html +13 -12
  73. data/spec/rspec/core/formatters/text_mate_formatted-1.9.2.html +29 -28
  74. data/spec/rspec/core/formatters/{text_mate_formatted-1.9.1.html → text_mate_formatted-1.9.3.html} +29 -28
  75. data/spec/rspec/core/formatters/text_mate_formatter_spec.rb +2 -2
  76. data/spec/rspec/core/hooks_filtering_spec.rb +18 -18
  77. data/spec/rspec/core/let_spec.rb +19 -6
  78. data/spec/rspec/core/metadata_spec.rb +146 -61
  79. data/spec/rspec/core/pending_example_spec.rb +4 -4
  80. data/spec/rspec/core/rake_task_spec.rb +71 -50
  81. data/spec/rspec/core/reporter_spec.rb +2 -2
  82. data/spec/rspec/core/ruby_project_spec.rb +2 -2
  83. data/spec/rspec/core/runner_spec.rb +4 -1
  84. data/spec/rspec/core/shared_example_group_spec.rb +15 -119
  85. data/spec/rspec/core/subject_spec.rb +13 -13
  86. data/spec/rspec/core/world_spec.rb +31 -22
  87. data/spec/rspec/core_spec.rb +1 -29
  88. data/spec/spec_helper.rb +51 -50
  89. data/spec/support/shared_example_groups.rb +3 -3
  90. data/spec/support/spec_files.rb +8 -8
  91. metadata +79 -94
  92. data/.document +0 -5
  93. data/.gitignore +0 -12
  94. data/.rspec +0 -0
  95. data/.travis.yml +0 -7
  96. data/Gemfile +0 -50
  97. data/Guardfile +0 -5
  98. data/License.txt +0 -23
  99. data/Rakefile +0 -89
  100. data/cucumber.yml +0 -2
  101. data/features/.nav +0 -57
  102. data/features/Changelog.md +0 -269
  103. data/rspec-core.gemspec +0 -24
  104. data/script/FullBuildRakeFile +0 -63
  105. data/script/console +0 -8
  106. data/script/cucumber +0 -1
  107. data/script/full_build +0 -1
  108. data/script/spec +0 -1
  109. data/spec/rspec/core/formatters/html_formatted-1.8.6.html +0 -398
  110. data/spec/rspec/core/formatters/text_mate_formatted-1.8.6.html +0 -398
  111. data/spec.txt +0 -1126
  112. /data/{bin → exe}/autospec +0 -0
  113. /data/{bin → exe}/rspec +0 -0
@@ -39,6 +39,18 @@ module RSpec
39
39
  dump_profile if profile_examples? && failure_count == 0
40
40
  output.puts "\nFinished in #{format_seconds(duration)} seconds\n"
41
41
  output.puts colorise_summary(summary_line(example_count, failure_count, pending_count))
42
+ dump_commands_to_rerun_failed_examples
43
+ end
44
+
45
+ def dump_commands_to_rerun_failed_examples
46
+ return if failed_examples.empty?
47
+ output.puts
48
+ output.puts("Failed examples:")
49
+ output.puts
50
+
51
+ failed_examples.each do |example|
52
+ output.puts(red("rspec #{BaseFormatter::relative_path(example.location)}") + " " + cyan("# #{example.full_description}"))
53
+ end
42
54
  end
43
55
 
44
56
  def dump_profile
@@ -46,7 +58,7 @@ module RSpec
46
58
  output.puts "\nTop #{sorted_examples.size} slowest examples:\n"
47
59
  sorted_examples.each do |example|
48
60
  output.puts " #{example.full_description}"
49
- output.puts grey(" #{red(format_seconds(example.execution_result[:run_time]))} #{red("seconds")} #{format_caller(example.location)}")
61
+ output.puts cyan(" #{red(format_seconds(example.execution_result[:run_time]))} #{red("seconds")} #{format_caller(example.location)}")
50
62
  end
51
63
  end
52
64
 
@@ -63,8 +75,13 @@ module RSpec
63
75
  output.puts "Pending:"
64
76
  pending_examples.each do |pending_example|
65
77
  output.puts yellow(" #{pending_example.full_description}")
66
- output.puts grey(" # #{pending_example.execution_result[:pending_message]}")
67
- output.puts grey(" # #{format_caller(pending_example.location)}")
78
+ output.puts cyan(" # #{pending_example.execution_result[:pending_message]}")
79
+ output.puts cyan(" # #{format_caller(pending_example.location)}")
80
+ if pending_example.execution_result[:exception] \
81
+ && RSpec.configuration.show_failures_in_pending_blocks?
82
+ dump_failure_info(pending_example)
83
+ dump_backtrace(pending_example)
84
+ end
68
85
  end
69
86
  end
70
87
  end
@@ -83,20 +100,12 @@ module RSpec
83
100
  color(text, "\e[1m")
84
101
  end
85
102
 
86
- def white(text)
87
- color(text, "\e[37m")
88
- end
89
-
90
- def green(text)
91
- color(text, "\e[32m")
92
- end
93
-
94
103
  def red(text)
95
104
  color(text, "\e[31m")
96
105
  end
97
106
 
98
- def magenta(text)
99
- color(text, "\e[35m")
107
+ def green(text)
108
+ color(text, "\e[32m")
100
109
  end
101
110
 
102
111
  def yellow(text)
@@ -107,8 +116,16 @@ module RSpec
107
116
  color(text, "\e[34m")
108
117
  end
109
118
 
110
- def grey(text)
111
- color(text, "\e[90m")
119
+ def magenta(text)
120
+ color(text, "\e[35m")
121
+ end
122
+
123
+ def cyan(text)
124
+ color(text, "\e[36m")
125
+ end
126
+
127
+ def white(text)
128
+ color(text, "\e[37m")
112
129
  end
113
130
 
114
131
  def short_padding
@@ -131,7 +148,7 @@ module RSpec
131
148
 
132
149
  def dump_backtrace(example)
133
150
  format_backtrace(example.execution_result[:exception].backtrace, example).each do |backtrace_info|
134
- output.puts grey("#{long_padding}# #{backtrace_info}")
151
+ output.puts cyan("#{long_padding}# #{backtrace_info}")
135
152
  end
136
153
  end
137
154
 
@@ -144,8 +161,12 @@ module RSpec
144
161
  end
145
162
 
146
163
  def dump_failure(example, index)
147
- exception = example.execution_result[:exception]
148
164
  output.puts "#{short_padding}#{index.next}) #{example.full_description}"
165
+ dump_failure_info(example)
166
+ end
167
+
168
+ def dump_failure_info(example)
169
+ exception = example.execution_result[:exception]
149
170
  output.puts "#{long_padding}#{red("Failure/Error:")} #{red(read_failed_line(exception, example).strip)}"
150
171
  output.puts "#{long_padding}#{red(exception.class.name << ":")}" unless exception.class.name =~ /RSpec/
151
172
  exception.message.split("\n").each { |line| output.puts "#{long_padding} #{red(line)}" } if exception.message
@@ -158,7 +179,6 @@ module RSpec
158
179
  end
159
180
  end
160
181
  end
161
-
162
182
  end
163
183
  end
164
184
  end
@@ -108,8 +108,10 @@ module RSpec
108
108
  #
109
109
  def extra_failure_content(exception)
110
110
  require 'rspec/core/formatters/snippet_extractor'
111
+ backtrace = exception.backtrace.map {|line| backtrace_line(line)}
112
+ backtrace.compact!
111
113
  @snippet_extractor ||= SnippetExtractor.new
112
- " <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(exception)}</code></pre>"
114
+ " <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(backtrace)}</code></pre>"
113
115
  end
114
116
 
115
117
  def move_progress
@@ -4,10 +4,16 @@ module RSpec
4
4
  # This class extracts code snippets by looking at the backtrace of the passed error
5
5
  class SnippetExtractor #:nodoc:
6
6
  class NullConverter; def convert(code, pre); code; end; end #:nodoc:
7
- begin; require 'syntax/convertors/html'; @@converter = Syntax::Convertors::HTML.for_syntax "ruby"; rescue LoadError => e; @@converter = NullConverter.new; end
7
+
8
+ begin
9
+ require 'syntax/convertors/html'
10
+ @@converter = Syntax::Convertors::HTML.for_syntax "ruby"
11
+ rescue LoadError => e
12
+ @@converter = NullConverter.new
13
+ end
8
14
 
9
- def snippet(error)
10
- raw_code, line = snippet_for(error.backtrace[0])
15
+ def snippet(backtrace)
16
+ raw_code, line = snippet_for(backtrace[0])
11
17
  highlighted = @@converter.convert(raw_code, false)
12
18
  highlighted << "\n<span class=\"comment\"># gem install syntax to get syntax highlighting</span>" if @@converter.is_a?(NullConverter)
13
19
  post_process(highlighted, line)
@@ -1,3 +1,4 @@
1
+ require 'cgi'
1
2
  require 'rspec/core/formatters/html_formatter'
2
3
 
3
4
  module RSpec
@@ -5,15 +6,28 @@ module RSpec
5
6
  module Formatters
6
7
  # Formats backtraces so they're clickable by TextMate
7
8
  class TextMateFormatter < HtmlFormatter
8
- def backtrace_line(line)
9
- if line = super(line)
10
- line.sub!(/([^:]*\.e?rb):(\d*)/) do
11
- "<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
12
- end
9
+ def backtrace_line(line, skip_textmate_conversion=false)
10
+ if skip_textmate_conversion
11
+ super(line)
12
+ else
13
+ format_backtrace_line_for_textmate(super(line))
14
+ end
15
+ end
13
16
 
14
- line
17
+ def format_backtrace_line_for_textmate(line)
18
+ return nil unless line
19
+ CGI.escapeHTML(line).sub(/([^:]*\.e?rb):(\d*)/) do
20
+ "<a href=\"txmt://open?url=file://#{File.expand_path($1)}&line=#{$2}\">#{$1}:#{$2}</a> "
15
21
  end
16
22
  end
23
+
24
+ def extra_failure_content(exception)
25
+ require 'rspec/core/formatters/snippet_extractor'
26
+ backtrace = exception.backtrace.map {|line| backtrace_line(line, :skip_textmate_conversion)}
27
+ backtrace.compact!
28
+ @snippet_extractor ||= SnippetExtractor.new
29
+ " <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(backtrace)}</code></pre>"
30
+ end
17
31
  end
18
32
  end
19
33
  end
@@ -8,12 +8,12 @@ module RSpec
8
8
 
9
9
  def initialize(options, &block)
10
10
  @options = options
11
- raise "no block given for #{self.class::TYPE} hook" unless block
11
+ raise "no block given for #{display_name}" unless block
12
12
  @block = block
13
13
  end
14
14
 
15
15
  def options_apply?(example_or_group)
16
- !example_or_group || example_or_group.apply?(:all?, options)
16
+ example_or_group.all_apply?(options)
17
17
  end
18
18
 
19
19
  def to_proc
@@ -23,10 +23,13 @@ module RSpec
23
23
  def call
24
24
  @block.call
25
25
  end
26
+
27
+ def display_name
28
+ self.class.name.split('::').last.gsub('Hook','').downcase << " hook"
29
+ end
26
30
  end
27
31
 
28
32
  class BeforeHook < Hook
29
- TYPE = 'before'
30
33
  def run_in(example_group_instance)
31
34
  if example_group_instance
32
35
  example_group_instance.instance_eval(&self)
@@ -37,7 +40,6 @@ module RSpec
37
40
  end
38
41
 
39
42
  class AfterHook < Hook
40
- TYPE = 'after'
41
43
  def run_in(example_group_instance)
42
44
  if example_group_instance
43
45
  example_group_instance.instance_eval_with_rescue(&self)
@@ -48,7 +50,6 @@ module RSpec
48
50
  end
49
51
 
50
52
  class AroundHook < Hook
51
- TYPE = 'around'
52
53
  def call(wrapped_example)
53
54
  @block.call(wrapped_example)
54
55
  end
@@ -76,7 +77,7 @@ module RSpec
76
77
 
77
78
  class AfterHooks < HookCollection
78
79
  def run_all(example_group_instance)
79
- reverse.each {|h| h.run_in(example_group_instance) } unless empty?
80
+ reverse.each {|h| h.run_in(example_group_instance) }
80
81
  end
81
82
 
82
83
  def run_all!(example_group_instance)
@@ -21,7 +21,7 @@ module RSpec
21
21
  # end
22
22
  def let(name, &block)
23
23
  define_method(name) do
24
- __memoized[name] ||= instance_eval(&block)
24
+ __memoized.fetch(name) {|k| __memoized[k] = instance_eval(&block) }
25
25
  end
26
26
  end
27
27
 
@@ -57,12 +57,12 @@ module RSpec
57
57
  # let(:thing) { Thing.new }
58
58
  #
59
59
  # it "is not invoked implicitly" do
60
- # Thing.count.should == 0
60
+ # Thing.count.should eq(0)
61
61
  # end
62
62
  #
63
63
  # it "can be invoked explicitly" do
64
64
  # thing
65
- # Thing.count.should == 1
65
+ # Thing.count.should eq(1)
66
66
  # end
67
67
  # end
68
68
  #
@@ -70,12 +70,12 @@ module RSpec
70
70
  # let!(:thing) { Thing.new }
71
71
  #
72
72
  # it "is invoked implicitly" do
73
- # Thing.count.should == 1
73
+ # Thing.count.should eq(1)
74
74
  # end
75
75
  #
76
76
  # it "returns memoized version on first invocation" do
77
77
  # thing
78
- # Thing.count.should == 1
78
+ # Thing.count.should eq(1)
79
79
  # end
80
80
  # end
81
81
  # end
@@ -2,7 +2,9 @@ module RSpec
2
2
  module Core
3
3
  class Metadata < Hash
4
4
 
5
- module LocationKeys
5
+ # Used to extend metadata Hashes to support lazy evaluation of locations
6
+ # and descriptions.
7
+ module MetadataHash
6
8
  def [](key)
7
9
  return super if has_key?(key)
8
10
  case key
@@ -12,12 +14,22 @@ module RSpec
12
14
  file_path, line_number = file_and_line_number
13
15
  store(:file_path, file_path)
14
16
  store(:line_number, line_number)
15
- self[key]
17
+ super
18
+ when :execution_result
19
+ store(:execution_result, {})
20
+ when :describes
21
+ store(:describes, described_class_for(self))
22
+ when :full_description
23
+ store(:full_description, full_description_for(self))
24
+ when :description
25
+ store(:description, build_description_from(*self[:description_args]))
16
26
  else
17
27
  super
18
28
  end
19
29
  end
20
30
 
31
+ private
32
+
21
33
  def location
22
34
  "#{self[:file_path]}:#{self[:line_number]}"
23
35
  end
@@ -30,97 +42,105 @@ module RSpec
30
42
  def first_caller_from_outside_rspec
31
43
  self[:caller].detect {|l| l !~ /\/lib\/rspec\/core/}
32
44
  end
45
+
46
+ def described_class_for(m)
47
+ m[:example_group][:describes]
48
+ end
49
+
50
+ def full_description_for(m)
51
+ build_description_from(m[:example_group][:full_description], *m[:description_args])
52
+ end
53
+
54
+ def build_description_from(*parts)
55
+ parts.map {|p| p.to_s}.reduce do |desc, p|
56
+ p =~ /^(#|::|\.)/ ? "#{desc}#{p}" : "#{desc} #{p}"
57
+ end || ""
58
+ end
33
59
  end
34
60
 
35
- def initialize(superclass_metadata=nil)
36
- @superclass_metadata = superclass_metadata
37
- if @superclass_metadata
38
- update(@superclass_metadata)
39
- example_group = {:example_group => @superclass_metadata[:example_group]}
61
+ module GroupMetadataHash
62
+ include MetadataHash
63
+
64
+ def described_class_for(*)
65
+ ancestors.each do |g|
66
+ return g[:describes] if g.has_key?(:describes)
67
+ end
68
+
69
+ ancestors.reverse.each do |g|
70
+ candidate = g[:description_args].first
71
+ return candidate unless String === candidate || Symbol === candidate
72
+ end
73
+
74
+ nil
75
+ end
76
+
77
+ def full_description_for(*)
78
+ build_description_from(*ancestors.reverse.map do |a|
79
+ a.has_key?(:full_description) ? a[:full_description] : a[:description_args]
80
+ end.flatten)
81
+ end
82
+
83
+ private
84
+
85
+ def ancestors
86
+ @ancestors ||= begin
87
+ groups = [group = self]
88
+ while group.has_key?(:example_group)
89
+ groups << group[:example_group]
90
+ group = group[:example_group]
91
+ end
92
+ groups
93
+ end
94
+ end
95
+ end
96
+
97
+ def initialize(parent_group_metadata=nil)
98
+ if parent_group_metadata
99
+ update(parent_group_metadata)
100
+ store(:example_group, {:example_group => parent_group_metadata[:example_group]}.extend(GroupMetadataHash))
40
101
  else
41
- example_group = {}
102
+ store(:example_group, {}.extend(GroupMetadataHash))
42
103
  end
43
104
 
44
- store(:example_group, example_group.extend(LocationKeys))
45
105
  yield self if block_given?
46
106
  end
47
107
 
48
- RESERVED_KEYS = [
49
- :description,
50
- :example_group,
51
- :execution_result,
52
- :file_path,
53
- :full_description,
54
- :line_number,
55
- :location
56
- ]
57
-
58
108
  def process(*args)
59
109
  user_metadata = args.last.is_a?(Hash) ? args.pop : {}
60
110
  ensure_valid_keys(user_metadata)
61
111
 
112
+ self[:example_group].store(:description_args, args)
62
113
  self[:example_group].store(:caller, user_metadata.delete(:caller) || caller)
63
- self[:example_group].store(:describes, described_class_from(*args))
64
- self[:example_group].store(:description, description_from(*args))
65
- self[:example_group].store(:full_description, full_description_from(*args))
66
- self[:example_group].store(:block, user_metadata.delete(:example_group_block))
67
114
 
68
115
  update(user_metadata)
69
116
  end
70
117
 
71
- def ensure_valid_keys(user_metadata)
72
- RESERVED_KEYS.each do |key|
73
- if user_metadata.keys.include?(key)
74
- raise <<-EOM
75
- #{"*"*50}
76
- :#{key} is not allowed
77
-
78
- RSpec reserves some hash keys for its own internal use,
79
- including :#{key}, which is used on:
80
-
81
- #{caller(0)[4]}.
82
-
83
- Here are all of RSpec's reserved hash keys:
84
-
85
- #{RESERVED_KEYS.join("\n ")}
86
- #{"*"*50}
87
- EOM
88
- raise ":#{key} is not allowed"
89
- end
90
- end
91
- end
92
-
93
118
  def for_example(description, user_metadata)
94
- dup.extend(LocationKeys).configure_for_example(description, user_metadata)
119
+ dup.extend(MetadataHash).configure_for_example(description, user_metadata)
95
120
  end
96
121
 
97
- def configure_for_example(description, user_metadata)
98
- store(:description, description.to_s)
99
- store(:full_description, "#{self[:example_group][:full_description]} #{self[:description]}")
100
- store(:execution_result, {})
101
- store(:caller, user_metadata.delete(:caller) || caller)
102
- update(user_metadata)
103
- end
104
-
105
- def apply?(predicate, filters)
106
- filters.send(predicate) do |key, value|
107
- apply_condition(key, value)
108
- end
122
+ def any_apply?(filters)
123
+ filters.any? {|k,v| filter_applies?(k,v)}
109
124
  end
110
125
 
111
- def relevant_line_numbers(metadata)
112
- line_numbers = [metadata[:line_number]]
113
- if metadata[:example_group]
114
- line_numbers + relevant_line_numbers(metadata[:example_group])
115
- else
116
- line_numbers
117
- end
126
+ def all_apply?(filters)
127
+ filters.all? {|k,v| filter_applies?(k,v)}
118
128
  end
119
129
 
120
- def apply_condition(key, value, metadata=self)
130
+ def filter_applies?(key, value, metadata=self)
121
131
  case value
122
132
  when Hash
123
- value.all? { |k, v| apply_condition(k, v, metadata[key]) }
133
+ if key == :locations
134
+ file_path = (self[:example_group] || {})[:file_path]
135
+ expanded_path = file_path && File.expand_path( file_path )
136
+ if expanded_path && line_numbers = value[expanded_path]
137
+ filter_applies?(:line_numbers, line_numbers)
138
+ else
139
+ true
140
+ end
141
+ else
142
+ value.all? { |k, v| filter_applies?(k, v, metadata[key]) }
143
+ end
124
144
  when Regexp
125
145
  metadata[key] =~ value
126
146
  when Proc
@@ -135,9 +155,12 @@ EOM
135
155
  else
136
156
  value.call(metadata[key]) rescue false
137
157
  end
138
- when Fixnum
139
- if key == :line_number
140
- relevant_line_numbers(metadata).include?(world.preceding_declaration_line(value))
158
+ when String
159
+ metadata[key].to_s == value.to_s
160
+ when Enumerable
161
+ if key == :line_numbers
162
+ preceding_declaration_lines = value.map{|v| world.preceding_declaration_line(v)}
163
+ !(relevant_line_numbers(metadata) & preceding_declaration_lines).empty?
141
164
  else
142
165
  metadata[key] == value
143
166
  end
@@ -146,42 +169,61 @@ EOM
146
169
  end
147
170
  end
148
171
 
149
- private
172
+ protected
150
173
 
151
- def world
152
- RSpec.world
174
+ def configure_for_example(description, user_metadata)
175
+ store(:description_args, [description])
176
+ store(:caller, user_metadata.delete(:caller) || caller)
177
+ update(user_metadata)
153
178
  end
154
179
 
155
- def superclass_metadata
156
- @superclass_metadata ||= { :example_group => {} }
157
- end
180
+ private
158
181
 
159
- def description_from(*args)
160
- args.inject("") do |result, a|
161
- a = a.to_s.strip
162
- if result == ""
163
- a
164
- elsif a =~ /^(#|::|\.)/
165
- "#{result}#{a}"
166
- else
167
- "#{result} #{a}"
182
+ RESERVED_KEYS = [
183
+ :description,
184
+ :example_group,
185
+ :execution_result,
186
+ :file_path,
187
+ :full_description,
188
+ :line_number,
189
+ :location
190
+ ]
191
+
192
+ def ensure_valid_keys(user_metadata)
193
+ RESERVED_KEYS.each do |key|
194
+ if user_metadata.keys.include?(key)
195
+ raise <<-EOM
196
+ #{"*"*50}
197
+ :#{key} is not allowed
198
+
199
+ RSpec reserves some hash keys for its own internal use,
200
+ including :#{key}, which is used on:
201
+
202
+ #{caller(0)[4]}.
203
+
204
+ Here are all of RSpec's reserved hash keys:
205
+
206
+ #{RESERVED_KEYS.join("\n ")}
207
+ #{"*"*50}
208
+ EOM
209
+ raise ":#{key} is not allowed"
168
210
  end
169
211
  end
170
212
  end
171
213
 
172
- def full_description_from(*args)
173
- if superclass_metadata[:example_group][:full_description]
174
- description_from(superclass_metadata[:example_group][:full_description], *args)
175
- else
176
- description_from(*args)
177
- end
214
+ def world
215
+ RSpec.world
178
216
  end
179
217
 
180
- def described_class_from(*args)
181
- superclass_metadata[:example_group][:describes] || begin
182
- args.first unless args.first.is_a?(String) || args.first.is_a?(Symbol)
218
+ def relevant_line_numbers(metadata)
219
+ line_numbers = [metadata[:line_number]]
220
+ if metadata[:example_group]
221
+ line_numbers + relevant_line_numbers(metadata[:example_group])
222
+ else
223
+ line_numbers
183
224
  end
184
225
  end
226
+
185
227
  end
186
228
  end
187
229
  end
@@ -87,7 +87,7 @@ a string such as "#method_name" to remove this warning.
87
87
 
88
88
  NOTICE
89
89
  end
90
- end
91
90
  end
91
+ end
92
92
  end
93
93
  end
@@ -40,8 +40,7 @@ module RSpec::Core
40
40
  options[:debug] = true
41
41
  end
42
42
 
43
- parser.on('-e', '--example PATTERN', "Run examples whose full descriptions match this pattern",
44
- "(PATTERN is compiled into a Ruby regular expression)") do |o|
43
+ parser.on('-e', '--example STRING', "Run examples whose full nested names include STRING") do |o|
45
44
  options[:full_description] = Regexp.compile(Regexp.escape(o))
46
45
  end
47
46
 
@@ -74,8 +73,8 @@ module RSpec::Core
74
73
  options[:libs] << dir
75
74
  end
76
75
 
77
- parser.on('-l', '--line_number LINE', 'Specify the line number of a single example to run') do |o|
78
- options[:line_number] = o
76
+ parser.on('-l', '--line_number LINE', 'Specify the line number of an example to run. May be specified multiple times.') do |o|
77
+ (options[:line_numbers] ||= []) << o
79
78
  end
80
79
 
81
80
  parser.on('-O', '--options PATH', 'Specify the path to an options file') do |path|
@@ -86,7 +85,7 @@ module RSpec::Core
86
85
  options[:profile_examples] = o
87
86
  end
88
87
 
89
- parser.on('-P', '--pattern PATTERN', 'Load files those matching this pattern. Default is "spec/**/*_spec.rb"') do |o|
88
+ parser.on('-P', '--pattern PATTERN', 'Load files matching this pattern. Default is "spec/**/*_spec.rb"') do |o|
90
89
  options[:pattern] = o
91
90
  end
92
91
 
@@ -100,8 +99,8 @@ module RSpec::Core
100
99
  exit
101
100
  end
102
101
 
103
- parser.on('-X', '--drb', 'Run examples via DRb') do |o|
104
- options[:drb] = true
102
+ parser.on('-X', '--[no-]drb', 'Run examples via DRb') do |o|
103
+ options[:drb] = o
105
104
  end
106
105
 
107
106
  parser.on('--configure COMMAND', 'Generate configuration files') do |cmd|
@@ -133,6 +132,11 @@ module RSpec::Core
133
132
  parser.on('--tty', 'Used internally by rspec when sending commands to other processes') do |o|
134
133
  options[:tty] = true
135
134
  end
135
+
136
+ parser.on('--default_path PATH', 'Set the default path where RSpec looks for examples.',
137
+ 'Can be a path to a file or a directory') do |path|
138
+ options[:default_path] = path
139
+ end
136
140
  end
137
141
  end
138
142
  end
@@ -24,7 +24,8 @@ module RSpec
24
24
  example.example_group_instance.instance_eval { verify_mocks_for_rspec }
25
25
  end
26
26
  example.metadata[:pending] = false
27
- rescue Exception
27
+ rescue Exception => e
28
+ example.execution_result[:exception] = e
28
29
  ensure
29
30
  teardown_mocks_for_rspec
30
31
  end