rspec-core 2.6.4 → 2.7.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (107) hide show
  1. data/README.md +1 -1
  2. data/{bin → exe}/autospec +0 -0
  3. data/{bin → exe}/rspec +0 -0
  4. data/features/Upgrade.md +11 -0
  5. data/features/command_line/exit_status.feature +20 -3
  6. data/features/command_line/format_option.feature +8 -0
  7. data/features/command_line/line_number_appended_to_path.feature +35 -1
  8. data/features/command_line/line_number_option.feature +16 -3
  9. data/features/command_line/pattern_option.feature +31 -0
  10. data/features/command_line/rake_task.feature +1 -1
  11. data/features/command_line/ruby.feature +22 -0
  12. data/features/configuration/default_path.feature +38 -0
  13. data/features/example_groups/{shared_example_group.feature → shared_examples.feature} +49 -26
  14. data/features/expectation_framework_integration/configure_expectation_framework.feature +1 -1
  15. data/features/filtering/inclusion_filters.feature +4 -5
  16. data/features/formatters/text_formatter.feature +16 -13
  17. data/features/helper_methods/let.feature +4 -4
  18. data/features/hooks/around_hooks.feature +1 -1
  19. data/features/hooks/before_and_after_hooks.feature +3 -3
  20. data/features/hooks/filtering.feature +13 -6
  21. data/features/metadata/user_defined.feature +12 -10
  22. data/features/pending/pending_examples.feature +21 -8
  23. data/features/step_definitions/additional_cli_steps.rb +1 -1
  24. data/features/subject/attribute_of_subject.feature +2 -2
  25. data/features/support/env.rb +1 -2
  26. data/lib/rspec/core.rb +1 -23
  27. data/lib/rspec/core/configuration.rb +64 -16
  28. data/lib/rspec/core/configuration_options.rb +11 -4
  29. data/lib/rspec/core/example.rb +10 -7
  30. data/lib/rspec/core/example_group.rb +34 -18
  31. data/lib/rspec/core/formatters/base_text_formatter.rb +10 -2
  32. data/lib/rspec/core/formatters/html_formatter.rb +3 -1
  33. data/lib/rspec/core/formatters/snippet_extractor.rb +9 -3
  34. data/lib/rspec/core/formatters/text_mate_formatter.rb +20 -6
  35. data/lib/rspec/core/hooks.rb +2 -2
  36. data/lib/rspec/core/let.rb +5 -5
  37. data/lib/rspec/core/metadata.rb +136 -94
  38. data/lib/rspec/core/option_parser.rb +10 -5
  39. data/lib/rspec/core/pending.rb +2 -1
  40. data/lib/rspec/core/rake_task.rb +26 -15
  41. data/lib/rspec/core/reporter.rb +2 -3
  42. data/lib/rspec/core/runner.rb +1 -1
  43. data/lib/rspec/core/shared_example_group.rb +4 -4
  44. data/lib/rspec/core/subject.rb +7 -7
  45. data/lib/rspec/core/version.rb +1 -1
  46. data/lib/rspec/core/world.rb +4 -8
  47. data/spec/autotest/discover_spec.rb +2 -2
  48. data/spec/autotest/failed_results_re_spec.rb +29 -21
  49. data/spec/autotest/rspec_spec.rb +3 -3
  50. data/spec/rspec/core/command_line_spec.rb +1 -6
  51. data/spec/rspec/core/configuration_options_spec.rb +65 -13
  52. data/spec/rspec/core/configuration_spec.rb +148 -37
  53. data/spec/rspec/core/deprecations_spec.rb +2 -2
  54. data/spec/rspec/core/drb_command_line_spec.rb +6 -6
  55. data/spec/rspec/core/example_group_spec.rb +197 -61
  56. data/spec/rspec/core/example_spec.rb +33 -16
  57. data/spec/rspec/core/formatters/base_formatter_spec.rb +3 -3
  58. data/spec/rspec/core/formatters/base_text_formatter_spec.rb +149 -1
  59. data/spec/rspec/core/formatters/helpers_spec.rb +8 -8
  60. data/spec/rspec/core/formatters/html_formatted-1.8.7-jruby.html +85 -17
  61. data/spec/rspec/core/formatters/html_formatted-1.8.7.html +12 -11
  62. data/spec/rspec/core/formatters/html_formatted-1.9.2.html +12 -11
  63. data/spec/rspec/core/formatters/{html_formatted-1.9.1.html → html_formatted-1.9.3.html} +12 -11
  64. data/spec/rspec/core/formatters/html_formatter_spec.rb +5 -5
  65. data/spec/rspec/core/formatters/progress_formatter_spec.rb +2 -2
  66. data/spec/rspec/core/formatters/snippet_extractor_spec.rb +2 -2
  67. data/spec/rspec/core/formatters/text_mate_formatted-1.8.7-jruby.html +86 -18
  68. data/spec/rspec/core/formatters/text_mate_formatted-1.8.7.html +13 -12
  69. data/spec/rspec/core/formatters/text_mate_formatted-1.9.2.html +29 -28
  70. data/spec/rspec/core/formatters/{text_mate_formatted-1.9.1.html → text_mate_formatted-1.9.3.html} +29 -28
  71. data/spec/rspec/core/formatters/text_mate_formatter_spec.rb +2 -2
  72. data/spec/rspec/core/hooks_filtering_spec.rb +18 -18
  73. data/spec/rspec/core/let_spec.rb +19 -6
  74. data/spec/rspec/core/metadata_spec.rb +146 -61
  75. data/spec/rspec/core/pending_example_spec.rb +4 -4
  76. data/spec/rspec/core/rake_task_spec.rb +71 -50
  77. data/spec/rspec/core/reporter_spec.rb +2 -2
  78. data/spec/rspec/core/ruby_project_spec.rb +2 -2
  79. data/spec/rspec/core/runner_spec.rb +4 -1
  80. data/spec/rspec/core/shared_example_group_spec.rb +15 -119
  81. data/spec/rspec/core/subject_spec.rb +13 -13
  82. data/spec/rspec/core/world_spec.rb +31 -22
  83. data/spec/rspec/core_spec.rb +1 -29
  84. data/spec/spec_helper.rb +51 -49
  85. data/spec/support/shared_example_groups.rb +3 -3
  86. data/spec/support/spec_files.rb +8 -8
  87. metadata +79 -93
  88. data/.document +0 -5
  89. data/.gitignore +0 -12
  90. data/.rspec +0 -0
  91. data/.travis.yml +0 -9
  92. data/Changelog.md +0 -305
  93. data/Gemfile +0 -49
  94. data/Guardfile +0 -5
  95. data/License.txt +0 -23
  96. data/Rakefile +0 -93
  97. data/cucumber.yml +0 -2
  98. data/features/.nav +0 -57
  99. data/rspec-core.gemspec +0 -24
  100. data/script/FullBuildRakeFile +0 -63
  101. data/script/console +0 -8
  102. data/script/cucumber +0 -1
  103. data/script/full_build +0 -1
  104. data/script/spec +0 -1
  105. data/spec.txt +0 -1126
  106. data/spec/rspec/core/formatters/html_formatted-1.8.6.html +0 -398
  107. data/spec/rspec/core/formatters/text_mate_formatted-1.8.6.html +0 -398
@@ -9,9 +9,7 @@ module RSpec
9
9
  end
10
10
 
11
11
  def configure(config)
12
- keys = options.keys
13
- keys.unshift(:requires) if keys.delete(:requires)
14
- keys.unshift(:libs) if keys.delete(:libs)
12
+ keys = order(options.keys, :libs, :requires, :default_path, :pattern)
15
13
 
16
14
  formatters = options[:formatters] if keys.delete(:formatters)
17
15
 
@@ -31,7 +29,6 @@ module RSpec
31
29
  argv << "--backtrace" if options[:full_backtrace]
32
30
  argv << "--tty" if options[:tty]
33
31
  argv << "--fail-fast" if options[:fail_fast]
34
- argv << "--line_number" << options[:line_number] if options[:line_number]
35
32
  argv << "--options" << options[:custom_options_file] if options[:custom_options_file]
36
33
  if options[:full_description]
37
34
  # The argument to --example is regexp-escaped before being stuffed
@@ -40,6 +37,9 @@ module RSpec
40
37
  # backslashes, so we must remove them.
41
38
  argv << "--example" << options[:full_description].source.delete('\\')
42
39
  end
40
+ if options[:line_numbers]
41
+ argv += options[:line_numbers].inject([]){|a,l| a << "--line_number" << l}
42
+ end
43
43
  if options[:filter]
44
44
  options[:filter].each_pair do |k, v|
45
45
  argv << "--tag" << k.to_s
@@ -71,6 +71,13 @@ module RSpec
71
71
 
72
72
  private
73
73
 
74
+ def order(keys, *ordered)
75
+ ordered.reverse.each do |key|
76
+ keys.unshift(key) if keys.delete(key)
77
+ end
78
+ keys
79
+ end
80
+
74
81
  def file_options
75
82
  custom_options_file ? custom_options : global_options.merge(local_options)
76
83
  end
@@ -4,6 +4,10 @@ module RSpec
4
4
 
5
5
  attr_reader :metadata, :options, :example_group_instance
6
6
 
7
+ # Returns the first exception raised, if any, in the context of running
8
+ # this example.
9
+ attr_reader :exception
10
+
7
11
  def self.delegate_to_metadata(*keys)
8
12
  keys.each do |key|
9
13
  define_method(key) {@metadata[key]}
@@ -23,13 +27,12 @@ module RSpec
23
27
  @example_group_class
24
28
  end
25
29
 
26
- def around_hooks
30
+ def around_hooks # :nodoc:
27
31
  @around_hooks ||= example_group.around_hooks_for(self)
28
32
  end
29
33
 
30
- def apply?(predicate, filters)
31
- @metadata.apply?(predicate, filters) ||
32
- @example_group_class.apply?(predicate, filters)
34
+ def all_apply?(filters)
35
+ @metadata.all_apply?(filters) || @example_group_class.all_apply?(filters)
33
36
  end
34
37
 
35
38
  alias_method :pending?, :pending
@@ -106,7 +109,7 @@ module RSpec
106
109
  if around_hooks.empty?
107
110
  yield
108
111
  else
109
- @example_group_class.eval_around_eachs(self, Example.procsy(metadata, &block)).call
112
+ @example_group_class.run_around_each_hooks(self, Example.procsy(metadata, &block)).call
110
113
  end
111
114
  end
112
115
 
@@ -142,11 +145,11 @@ module RSpec
142
145
 
143
146
  def run_before_each
144
147
  @example_group_instance.setup_mocks_for_rspec if @example_group_instance.respond_to?(:setup_mocks_for_rspec)
145
- @example_group_class.eval_before_eachs(self)
148
+ @example_group_class.run_before_each_hooks(self)
146
149
  end
147
150
 
148
151
  def run_after_each
149
- @example_group_class.eval_after_eachs(self)
152
+ @example_group_class.run_after_each_hooks(self)
150
153
  @example_group_instance.verify_mocks_for_rspec if @example_group_instance.respond_to?(:verify_mocks_for_rspec)
151
154
  ensure
152
155
  @example_group_instance.teardown_mocks_for_rspec if @example_group_instance.respond_to?(:teardown_mocks_for_rspec)
@@ -60,16 +60,20 @@ module RSpec
60
60
 
61
61
  alias_example_to :it
62
62
  alias_example_to :specify
63
- alias_example_to :focused, :focused => true, :focus => true
64
- alias_example_to :focus, :focused => true, :focus => true
65
- alias_example_to :pending, :pending => true
66
- alias_example_to :xit, :pending => true
63
+
64
+ alias_example_to :pending, :pending => true
65
+ alias_example_to :xexample, :pending => true
66
+ alias_example_to :xit, :pending => true
67
+ alias_example_to :xspecify, :pending => true
68
+
69
+ alias_example_to :focused, :focused => true, :focus => true
70
+ alias_example_to :focus, :focused => true, :focus => true
67
71
 
68
72
  def self.define_nested_shared_group_method(new_name, report_label=nil)
69
73
  module_eval(<<-END_RUBY, __FILE__, __LINE__)
70
74
  def self.#{new_name}(name, *args, &customization_block)
71
- shared_block = world.shared_example_groups[name]
72
- raise "Could not find shared example group named \#{name.inspect}" unless shared_block
75
+ shared_block = find_shared("examples", name)
76
+ raise "Could not find shared examples \#{name.inspect}" unless shared_block
73
77
 
74
78
  group = describe("#{report_label || "it should behave like"} \#{name}") do
75
79
  module_eval_with_args(*args, &shared_block)
@@ -90,11 +94,19 @@ module RSpec
90
94
  alias_it_should_behave_like_to :it_behaves_like, "behaves like"
91
95
 
92
96
  def self.include_context(name)
93
- module_eval(&world.shared_example_groups[name])
97
+ module_eval(&find_shared("context", name))
94
98
  end
95
99
 
96
- class << self
97
- alias_method :include_examples, :include_context
100
+ def self.include_examples(name)
101
+ module_eval(&find_shared("examples", name))
102
+ end
103
+
104
+ def self.find_shared(label, name)
105
+ if world.shared_example_groups.has_key?(name)
106
+ world.shared_example_groups[name]
107
+ else
108
+ raise ArgumentError, "Could not find shared #{label} #{name.inspect}"
109
+ end
98
110
  end
99
111
 
100
112
  def self.examples
@@ -200,7 +212,7 @@ module RSpec
200
212
  ivars.each { |ivar, val| example_group_instance.instance_variable_set(ivar, val) }
201
213
  end
202
214
 
203
- def self.eval_before_alls(example_group_instance)
215
+ def self.run_before_all_hooks(example_group_instance)
204
216
  return if descendant_filtered_examples.empty?
205
217
  assign_before_all_ivars(superclass.before_all_ivars, example_group_instance)
206
218
  world.run_hook_filtered(:before, :all, self, example_group_instance)
@@ -208,7 +220,7 @@ module RSpec
208
220
  store_before_all_ivars(example_group_instance)
209
221
  end
210
222
 
211
- def self.eval_around_eachs(example, initial_procsy)
223
+ def self.run_around_each_hooks(example, initial_procsy)
212
224
  example.around_hooks.reverse.inject(initial_procsy) do |procsy, around_hook|
213
225
  Example.procsy(procsy.metadata) do
214
226
  example.example_group_instance.instance_eval_with_args(procsy, &around_hook)
@@ -216,17 +228,17 @@ module RSpec
216
228
  end
217
229
  end
218
230
 
219
- def self.eval_before_eachs(example)
231
+ def self.run_before_each_hooks(example)
220
232
  world.run_hook_filtered(:before, :each, self, example.example_group_instance, example)
221
233
  ancestors.reverse.each { |ancestor| ancestor.run_hook(:before, :each, example.example_group_instance) }
222
234
  end
223
235
 
224
- def self.eval_after_eachs(example)
236
+ def self.run_after_each_hooks(example)
225
237
  ancestors.each { |ancestor| ancestor.run_hook(:after, :each, example.example_group_instance) }
226
238
  world.run_hook_filtered(:after, :each, self, example.example_group_instance, example)
227
239
  end
228
240
 
229
- def self.eval_after_alls(example_group_instance)
241
+ def self.run_after_all_hooks(example_group_instance)
230
242
  return if descendant_filtered_examples.empty?
231
243
  assign_before_all_ivars(before_all_ivars, example_group_instance)
232
244
 
@@ -258,14 +270,14 @@ An error occurred in an after(:all) hook.
258
270
  reporter.example_group_started(self)
259
271
 
260
272
  begin
261
- eval_before_alls(new)
273
+ run_before_all_hooks(new)
262
274
  result_for_this_group = run_examples(reporter)
263
275
  results_for_descendants = children.map {|child| child.run(reporter)}.all?
264
276
  result_for_this_group && results_for_descendants
265
277
  rescue Exception => ex
266
278
  fail_filtered_examples(ex, reporter)
267
279
  ensure
268
- eval_after_alls(new)
280
+ run_after_all_hooks(new)
269
281
  before_all_ivars.clear
270
282
  reporter.example_group_finished(self)
271
283
  end
@@ -297,8 +309,12 @@ An error occurred in an after(:all) hook.
297
309
  end.all?
298
310
  end
299
311
 
300
- def self.apply?(predicate, filters)
301
- metadata.apply?(predicate, filters)
312
+ def self.any_apply?(filters)
313
+ metadata.any_apply?(filters)
314
+ end
315
+
316
+ def self.all_apply?(filters)
317
+ metadata.all_apply?(filters)
302
318
  end
303
319
 
304
320
  def self.declaration_line_numbers
@@ -77,6 +77,11 @@ module RSpec
77
77
  output.puts yellow(" #{pending_example.full_description}")
78
78
  output.puts cyan(" # #{pending_example.execution_result[:pending_message]}")
79
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
80
85
  end
81
86
  end
82
87
  end
@@ -156,8 +161,12 @@ module RSpec
156
161
  end
157
162
 
158
163
  def dump_failure(example, index)
159
- exception = example.execution_result[:exception]
160
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]
161
170
  output.puts "#{long_padding}#{red("Failure/Error:")} #{red(read_failed_line(exception, example).strip)}"
162
171
  output.puts "#{long_padding}#{red(exception.class.name << ":")}" unless exception.class.name =~ /RSpec/
163
172
  exception.message.split("\n").each { |line| output.puts "#{long_padding} #{red(line)}" } if exception.message
@@ -170,7 +179,6 @@ module RSpec
170
179
  end
171
180
  end
172
181
  end
173
-
174
182
  end
175
183
  end
176
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
@@ -13,7 +13,7 @@ module RSpec
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
@@ -77,7 +77,7 @@ module RSpec
77
77
 
78
78
  class AfterHooks < HookCollection
79
79
  def run_all(example_group_instance)
80
- reverse.each {|h| h.run_in(example_group_instance) } unless empty?
80
+ reverse.each {|h| h.run_in(example_group_instance) }
81
81
  end
82
82
 
83
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