rspec-core 3.0.0.beta1 → 3.0.0.beta2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. data.tar.gz.sig +0 -0
  2. data/Changelog.md +137 -0
  3. data/README.md +2 -2
  4. data/exe/rspec +2 -23
  5. data/features/README.md +1 -5
  6. data/features/command_line/README.md +7 -10
  7. data/features/command_line/exit_status.feature +1 -1
  8. data/features/command_line/format_option.feature +1 -1
  9. data/features/command_line/init.feature +40 -1
  10. data/features/command_line/line_number_option.feature +2 -2
  11. data/features/command_line/ruby.feature +5 -4
  12. data/features/configuration/enable_global_dsl.feature +54 -0
  13. data/features/example_groups/aliasing.feature +48 -0
  14. data/features/example_groups/basic_structure.feature +1 -1
  15. data/features/expectation_framework_integration/configure_expectation_framework.feature +1 -1
  16. data/features/filtering/if_and_unless.feature +0 -30
  17. data/features/formatters/custom_formatter.feature +32 -0
  18. data/features/formatters/regression_tests.feature +95 -0
  19. data/features/hooks/around_hooks.feature +1 -0
  20. data/features/hooks/before_and_after_hooks.feature +2 -2
  21. data/features/mock_framework_integration/use_flexmock.feature +11 -13
  22. data/features/mock_framework_integration/use_mocha.feature +11 -13
  23. data/features/mock_framework_integration/use_rr.feature +11 -13
  24. data/features/mock_framework_integration/use_rspec.feature +11 -13
  25. data/features/pending_and_skipped_examples/README.md +3 -0
  26. data/features/pending_and_skipped_examples/pending_examples.feature +118 -0
  27. data/features/pending_and_skipped_examples/skipped_examples.feature +106 -0
  28. data/features/step_definitions/additional_cli_steps.rb +34 -0
  29. data/features/subject/explicit_subject.feature +1 -1
  30. data/features/subject/one_liner_syntax.feature +71 -0
  31. data/lib/rspec/core.rb +6 -14
  32. data/lib/rspec/core/backtrace_formatter.rb +16 -4
  33. data/lib/rspec/core/command_line.rb +2 -3
  34. data/lib/rspec/core/configuration.rb +114 -125
  35. data/lib/rspec/core/configuration_options.rb +32 -18
  36. data/lib/rspec/core/dsl.rb +80 -18
  37. data/lib/rspec/core/example.rb +84 -33
  38. data/lib/rspec/core/example_group.rb +95 -43
  39. data/lib/rspec/core/filter_manager.rb +31 -40
  40. data/lib/rspec/core/formatters.rb +137 -0
  41. data/lib/rspec/core/formatters/base_formatter.rb +28 -41
  42. data/lib/rspec/core/formatters/base_text_formatter.rb +26 -37
  43. data/lib/rspec/core/formatters/deprecation_formatter.rb +48 -27
  44. data/lib/rspec/core/formatters/documentation_formatter.rb +27 -22
  45. data/lib/rspec/core/formatters/html_formatter.rb +48 -56
  46. data/lib/rspec/core/formatters/html_printer.rb +11 -18
  47. data/lib/rspec/core/formatters/json_formatter.rb +18 -22
  48. data/lib/rspec/core/formatters/legacy_formatter.rb +227 -0
  49. data/lib/rspec/core/formatters/progress_formatter.rb +7 -10
  50. data/lib/rspec/core/hooks.rb +250 -217
  51. data/lib/rspec/core/memoized_helpers.rb +43 -9
  52. data/lib/rspec/core/mocking_adapters/flexmock.rb +29 -0
  53. data/lib/rspec/core/{mocking/with_mocha.rb → mocking_adapters/mocha.rb} +19 -16
  54. data/lib/rspec/core/mocking_adapters/null.rb +12 -0
  55. data/lib/rspec/core/mocking_adapters/rr.rb +28 -0
  56. data/lib/rspec/core/mocking_adapters/rspec.rb +30 -0
  57. data/lib/rspec/core/notifications.rb +100 -0
  58. data/lib/rspec/core/option_parser.rb +11 -18
  59. data/lib/rspec/core/pending.rb +78 -47
  60. data/lib/rspec/core/project_initializer.rb +2 -49
  61. data/lib/rspec/core/project_initializer/dot_rspec +3 -0
  62. data/lib/rspec/core/project_initializer/spec_helper.rb +82 -0
  63. data/lib/rspec/core/rake_task.rb +5 -14
  64. data/lib/rspec/core/reporter.rb +24 -32
  65. data/lib/rspec/core/ruby_project.rb +1 -1
  66. data/lib/rspec/core/runner.rb +14 -4
  67. data/lib/rspec/core/shared_example_group.rb +40 -13
  68. data/lib/rspec/core/version.rb +1 -1
  69. data/spec/command_line/order_spec.rb +15 -15
  70. data/spec/rspec/core/backtrace_formatter_spec.rb +15 -1
  71. data/spec/rspec/core/command_line_spec.rb +18 -17
  72. data/spec/rspec/core/configuration_options_spec.rb +57 -34
  73. data/spec/rspec/core/configuration_spec.rb +162 -184
  74. data/spec/rspec/core/drb_command_line_spec.rb +5 -7
  75. data/spec/rspec/core/drb_options_spec.rb +2 -2
  76. data/spec/rspec/core/dsl_spec.rb +79 -15
  77. data/spec/rspec/core/example_group_spec.rb +253 -39
  78. data/spec/rspec/core/example_spec.rb +149 -33
  79. data/spec/rspec/core/filter_manager_spec.rb +9 -26
  80. data/spec/rspec/core/formatters/base_formatter_spec.rb +2 -5
  81. data/spec/rspec/core/formatters/base_text_formatter_spec.rb +42 -145
  82. data/spec/rspec/core/formatters/deprecation_formatter_spec.rb +64 -34
  83. data/spec/rspec/core/formatters/documentation_formatter_spec.rb +15 -28
  84. data/spec/rspec/core/formatters/helpers_spec.rb +2 -2
  85. data/spec/rspec/core/formatters/{html_formatted-1.8.7.html → html_formatted-2.1.0.html} +22 -44
  86. data/spec/rspec/core/formatters/{html_formatted-1.8.7-jruby.html → html_formatted.html} +30 -49
  87. data/spec/rspec/core/formatters/html_formatter_spec.rb +35 -19
  88. data/spec/rspec/core/formatters/json_formatter_spec.rb +42 -40
  89. data/spec/rspec/core/formatters/legacy_formatter_spec.rb +137 -0
  90. data/spec/rspec/core/formatters/progress_formatter_spec.rb +38 -25
  91. data/spec/rspec/core/formatters/snippet_extractor_spec.rb +1 -1
  92. data/spec/rspec/core/formatters_spec.rb +120 -0
  93. data/spec/rspec/core/hooks_filtering_spec.rb +1 -1
  94. data/spec/rspec/core/hooks_spec.rb +13 -2
  95. data/spec/rspec/core/memoized_helpers_spec.rb +17 -8
  96. data/spec/rspec/core/metadata_spec.rb +3 -3
  97. data/spec/rspec/core/option_parser_spec.rb +53 -46
  98. data/spec/rspec/core/ordering_spec.rb +4 -4
  99. data/spec/rspec/core/pending_example_spec.rb +23 -126
  100. data/spec/rspec/core/pending_spec.rb +8 -0
  101. data/spec/rspec/core/project_initializer_spec.rb +8 -41
  102. data/spec/rspec/core/rake_task_spec.rb +15 -4
  103. data/spec/rspec/core/random_spec.rb +1 -1
  104. data/spec/rspec/core/reporter_spec.rb +50 -37
  105. data/spec/rspec/core/resources/formatter_specs.rb +9 -11
  106. data/spec/rspec/core/rspec_matchers_spec.rb +1 -1
  107. data/spec/rspec/core/ruby_project_spec.rb +3 -3
  108. data/spec/rspec/core/runner_spec.rb +65 -23
  109. data/spec/rspec/core/shared_context_spec.rb +4 -4
  110. data/spec/rspec/core/shared_example_group/collection_spec.rb +1 -1
  111. data/spec/rspec/core/shared_example_group_spec.rb +20 -11
  112. data/spec/rspec/core/warnings_spec.rb +1 -1
  113. data/spec/rspec/core/world_spec.rb +10 -10
  114. data/spec/rspec/core_spec.rb +2 -2
  115. data/spec/spec_helper.rb +12 -24
  116. data/spec/support/config_options_helper.rb +1 -3
  117. data/spec/support/formatter_support.rb +83 -0
  118. data/spec/support/isolate_load_path_mutation.rb +1 -2
  119. data/spec/support/isolated_directory.rb +1 -1
  120. data/spec/support/isolated_home_directory.rb +1 -1
  121. data/spec/support/legacy_formatter_using_sub_classing_example.rb +87 -0
  122. data/spec/support/matchers.rb +20 -0
  123. data/spec/support/mathn_integration_support.rb +2 -2
  124. data/spec/support/old_style_formatter_example.rb +69 -0
  125. data/spec/support/shared_example_groups.rb +1 -1
  126. data/spec/support/spec_files.rb +3 -3
  127. metadata +192 -69
  128. metadata.gz.sig +3 -1
  129. checksums.yaml +0 -15
  130. checksums.yaml.gz.sig +0 -2
  131. data/features/configuration/show_failures_in_pending_blocks.feature +0 -61
  132. data/features/pending/pending_examples.feature +0 -229
  133. data/features/subject/implicit_receiver.feature +0 -29
  134. data/lib/rspec/core/mocking/with_absolutely_nothing.rb +0 -11
  135. data/lib/rspec/core/mocking/with_flexmock.rb +0 -27
  136. data/lib/rspec/core/mocking/with_rr.rb +0 -27
  137. data/lib/rspec/core/mocking/with_rspec.rb +0 -27
  138. data/spec/rspec/core/formatters/html_formatted-1.8.7-rbx.html +0 -477
  139. data/spec/rspec/core/formatters/html_formatted-1.9.2.html +0 -425
  140. data/spec/rspec/core/formatters/html_formatted-1.9.3-jruby.html +0 -416
  141. data/spec/rspec/core/formatters/html_formatted-1.9.3-rbx.html +0 -477
  142. data/spec/rspec/core/formatters/html_formatted-1.9.3.html +0 -419
  143. data/spec/rspec/core/formatters/html_formatted-2.0.0.html +0 -425
  144. data/spec/support/in_sub_process.rb +0 -37
  145. data/spec/support/sandboxed_mock_space.rb +0 -100
@@ -4,8 +4,9 @@ require 'json'
4
4
  module RSpec
5
5
  module Core
6
6
  module Formatters
7
-
8
7
  class JsonFormatter < BaseFormatter
8
+ Formatters.register self, :message, :dump_summary, :stop, :close,
9
+ :dump_profile
9
10
 
10
11
  attr_reader :output_hash
11
12
 
@@ -14,32 +15,23 @@ module RSpec
14
15
  @output_hash = {}
15
16
  end
16
17
 
17
- def message(message)
18
- (@output_hash[:messages] ||= []) << message
18
+ def message(notification)
19
+ (@output_hash[:messages] ||= []) << notification.message
19
20
  end
20
21
 
21
- def dump_summary(duration, example_count, failure_count, pending_count)
22
- super(duration, example_count, failure_count, pending_count)
22
+ def dump_summary(summary)
23
23
  @output_hash[:summary] = {
24
- :duration => duration,
25
- :example_count => example_count,
26
- :failure_count => failure_count,
27
- :pending_count => pending_count
24
+ :duration => summary.duration,
25
+ :example_count => summary.example_count,
26
+ :failure_count => summary.failure_count,
27
+ :pending_count => summary.pending_count
28
28
  }
29
- @output_hash[:summary_line] = summary_line(example_count, failure_count, pending_count)
30
-
31
- dump_profile unless mute_profile_output?(failure_count)
32
- end
29
+ @output_hash[:summary_line] = summary.summary_line
33
30
 
34
- def summary_line(example_count, failure_count, pending_count)
35
- summary = pluralize(example_count, "example")
36
- summary << ", " << pluralize(failure_count, "failure")
37
- summary << ", #{pending_count} pending" if pending_count > 0
38
- summary
31
+ dump_profile unless mute_profile_output?(summary.failure_count)
39
32
  end
40
33
 
41
- def stop
42
- super
34
+ def stop(notification)
43
35
  @output_hash[:examples] = examples.map do |example|
44
36
  format_example(example).tap do |hash|
45
37
  if e=example.exception
@@ -53,7 +45,7 @@ module RSpec
53
45
  end
54
46
  end
55
47
 
56
- def close
48
+ def close(notification)
57
49
  output.write @output_hash.to_json
58
50
  output.close if IO === output && output != $stdout
59
51
  end
@@ -64,6 +56,7 @@ module RSpec
64
56
  dump_profile_slowest_example_groups
65
57
  end
66
58
 
59
+ # @api private
67
60
  def dump_profile_slowest_examples
68
61
  @output_hash[:profile] = {}
69
62
  sorted_examples = slowest_examples
@@ -76,6 +69,7 @@ module RSpec
76
69
  @output_hash[:profile][:total] = sorted_examples[:total]
77
70
  end
78
71
 
72
+ # @api private
79
73
  def dump_profile_slowest_example_groups
80
74
  @output_hash[:profile] ||= {}
81
75
  @output_hash[:profile][:groups] = slowest_groups.map do |loc, hash|
@@ -84,13 +78,15 @@ module RSpec
84
78
  end
85
79
 
86
80
  private
81
+
87
82
  def format_example(example)
88
83
  {
89
84
  :description => example.description,
90
85
  :full_description => example.full_description,
91
86
  :status => example.execution_result[:status],
92
87
  :file_path => example.metadata[:file_path],
93
- :line_number => example.metadata[:line_number]
88
+ :line_number => example.metadata[:line_number],
89
+ :run_time => example.execution_result[:run_time]
94
90
  }
95
91
  end
96
92
  end
@@ -0,0 +1,227 @@
1
+ require 'rspec/core/formatters/helpers'
2
+ require 'stringio'
3
+
4
+ module RSpec
5
+ module Core
6
+ module Formatters
7
+
8
+ # The `LegacyFormatter` is used to wrap older RSpec 2.x style formatters
9
+ # for the new 3.x implementation. It takes care of registering all the
10
+ # old notifications and translating them to the older formatter.
11
+ #
12
+ # @see RSpec::Core::Formatters::BaseFormatter
13
+ class LegacyFormatter
14
+ NOTIFICATIONS = %W[start message example_group_started example_group_finished example_started
15
+ example_passed example_failed example_pending start_dump dump_pending
16
+ dump_failures dump_summary seed close stop deprecation deprecation_summary]
17
+
18
+ module LegacyInterface
19
+
20
+ def start(count)
21
+ super Notifications::CountNotification.new(count)
22
+ end
23
+
24
+ def example_group_started(group)
25
+ super Notifications::GroupNotification.new(group) if defined?(super)
26
+ end
27
+
28
+ def example_group_finished(group)
29
+ super Notifications::GroupNotification.new(group) if defined?(super)
30
+ end
31
+
32
+ def example_started(example)
33
+ super Notifications::ExampleNotification.new(example) if defined?(super)
34
+ end
35
+
36
+ def example_passed(example)
37
+ super Notifications::ExampleNotification.new(example) if defined?(super)
38
+ end
39
+
40
+ def example_pending(example)
41
+ super Notifications::ExampleNotification.new(example) if defined?(super)
42
+ end
43
+
44
+ def example_failed(example)
45
+ super Notifications::ExampleNotification.new(example) if defined?(super)
46
+ end
47
+
48
+ def message(message)
49
+ super Notifications::MessageNotification.new(message) if defined?(super)
50
+ end
51
+
52
+ attr_reader :duration, :example_count, :failure_count, :pending_count
53
+ def dump_summary(duration, examples, failures, pending)
54
+ @duration = duration
55
+ @example_count = examples
56
+ @failure_count = failures
57
+ @pending_count = pending
58
+ super Notifications::SummaryNotification.new(duration, examples, failures, pending) if defined?(super)
59
+ end
60
+
61
+ def seed(seed)
62
+ super Notifications::SeedNotification.new(seed, true) if defined?(super)
63
+ end
64
+
65
+
66
+ def start_dump
67
+ super(Notifications::NullNotification) if defined?(super)
68
+ end
69
+
70
+ def dump_failures
71
+ super(Notifications::NullNotification) if defined?(super)
72
+ end
73
+
74
+ def dump_pending
75
+ super(Notifications::NullNotification) if defined?(super)
76
+ end
77
+
78
+ def dump_profile
79
+ super(Notifications::NullNotification) if defined?(super)
80
+ end
81
+
82
+ def close
83
+ super(Notifications::NullNotification) if defined?(super)
84
+ end
85
+
86
+ def stop
87
+ super(Notifications::NullNotification) if defined?(super)
88
+ end
89
+ end
90
+
91
+ # @api private
92
+ attr_reader :formatter
93
+
94
+ # @api public
95
+ #
96
+ # @param formatter
97
+ def initialize(formatter_class, *args)
98
+ if formatter_class.ancestors.include?(BaseFormatter)
99
+ formatter_class.class_eval do
100
+ include LegacyInterface
101
+ end
102
+ end
103
+ @formatter = formatter_class.new(*args)
104
+ end
105
+
106
+ # @api public
107
+ #
108
+ # This method is invoked during the setup phase to register
109
+ # a formatters with the reporter
110
+ #
111
+ def notifications
112
+ @notifications ||= NOTIFICATIONS.select { |m| @formatter.respond_to? m }
113
+ end
114
+
115
+ # @api public
116
+ #
117
+ # @param example_count
118
+ def start(notification)
119
+ @formatter.start notification.count
120
+ end
121
+
122
+ # @api public
123
+ #
124
+ # @param example_group
125
+ def example_group_started(notification)
126
+ @formatter.example_group_started notification.group
127
+ end
128
+
129
+ # @api public
130
+ #
131
+ # @param example_group
132
+ def example_group_finished(notification)
133
+ @formatter.example_group_finished notification.group
134
+ end
135
+
136
+ # @api public
137
+ #
138
+ # @param example
139
+ def example_started(notification)
140
+ @formatter.example_started notification.example
141
+ end
142
+
143
+ # @api public
144
+ #
145
+ # @param example
146
+ def example_passed(notification)
147
+ @formatter.example_passed notification.example
148
+ end
149
+
150
+ # @api public
151
+ #
152
+ # @param example
153
+ def example_pending(notification)
154
+ @formatter.example_pending notification.example
155
+ end
156
+
157
+ # @api public
158
+ #
159
+ # @param example
160
+ def example_failed(notification)
161
+ @formatter.example_failed notification.example
162
+ end
163
+
164
+ # @api public
165
+ #
166
+ # @param message
167
+ def message(notification)
168
+ @formatter.message notification.message
169
+ end
170
+
171
+ # @api public
172
+ #
173
+ def stop(notification)
174
+ @formatter.stop
175
+ end
176
+
177
+ # @api public
178
+ #
179
+ def start_dump(notification)
180
+ @formatter.start_dump
181
+ end
182
+
183
+ # @api public
184
+ #
185
+ def dump_failures(notification)
186
+ @formatter.dump_failures
187
+ end
188
+
189
+ # @api public
190
+ #
191
+ # @param duration
192
+ # @param example_count
193
+ # @param failure_count
194
+ # @param pending_count
195
+ def dump_summary(summary)
196
+ @formatter.dump_summary summary.duration, summary.example_count, summary.failure_count, summary.pending_count
197
+ end
198
+
199
+ # @api public
200
+ #
201
+ def dump_pending(notification)
202
+ @formatter.dump_pending
203
+ end
204
+
205
+ # @api public
206
+ #
207
+ def dump_profile(notification)
208
+ @formatter.dump_profile
209
+ end
210
+
211
+ # @api public
212
+ #
213
+ # @param seed
214
+ def seed(notification)
215
+ @formatter.seed notification.seed
216
+ end
217
+
218
+ # @api public
219
+ #
220
+ def close(notification)
221
+ @formatter.close
222
+ end
223
+
224
+ end
225
+ end
226
+ end
227
+ end
@@ -2,31 +2,28 @@ require 'rspec/core/formatters/base_text_formatter'
2
2
  module RSpec
3
3
  module Core
4
4
  module Formatters
5
-
6
5
  class ProgressFormatter < BaseTextFormatter
6
+ Formatters.register self, :example_passed, :example_pending, :example_failed, :start_dump
7
7
 
8
- def example_passed(example)
9
- super(example)
8
+ def example_passed(notification)
10
9
  output.print success_color('.')
11
10
  end
12
11
 
13
- def example_pending(example)
14
- super(example)
12
+ def example_pending(notification)
13
+ super
15
14
  output.print pending_color('*')
16
15
  end
17
16
 
18
- def example_failed(example)
19
- super(example)
17
+ def example_failed(notification)
18
+ super
20
19
  output.print failure_color('F')
21
20
  end
22
21
 
23
- def start_dump
24
- super()
22
+ def start_dump(notification)
25
23
  output.puts
26
24
  end
27
25
 
28
26
  end
29
-
30
27
  end
31
28
  end
32
29
  end
@@ -1,167 +1,6 @@
1
1
  module RSpec
2
2
  module Core
3
3
  module Hooks
4
- class Hook
5
- attr_reader :block, :options
6
-
7
- def initialize(block, options)
8
- @block = block
9
- @options = options
10
- end
11
-
12
- def options_apply?(example_or_group)
13
- example_or_group.all_apply?(options)
14
- end
15
- end
16
-
17
- class BeforeHook < Hook
18
- def run(example)
19
- example.instance_exec(example, &block)
20
- end
21
-
22
- def display_name
23
- "before hook"
24
- end
25
- end
26
-
27
- class AfterHook < Hook
28
- def run(example)
29
- example.instance_exec_with_rescue("in an after hook", &block)
30
- end
31
-
32
- def display_name
33
- "after hook"
34
- end
35
- end
36
-
37
- class AfterAllHook < Hook
38
- def run(example)
39
- example.instance_exec(example, &block)
40
- rescue Exception => e
41
- # TODO: come up with a better solution for this.
42
- RSpec.configuration.reporter.message <<-EOS
43
-
44
- An error occurred in an after(:all) hook.
45
- #{e.class}: #{e.message}
46
- occurred at #{e.backtrace.first}
47
-
48
- EOS
49
- end
50
-
51
- def display_name
52
- "after(:all) hook"
53
- end
54
- end
55
-
56
- class AroundHook < Hook
57
- def display_name
58
- "around hook"
59
- end
60
- end
61
-
62
- class BaseHookCollection
63
- Array.public_instance_methods(false).each do |name|
64
- define_method(name) { |*a, &b| hooks.__send__(name, *a, &b) }
65
- end
66
-
67
- attr_reader :hooks
68
- protected :hooks
69
-
70
- alias append push
71
- alias prepend unshift
72
-
73
- def initialize(hooks=[])
74
- @hooks = hooks
75
- end
76
- end
77
-
78
- class HookCollection < BaseHookCollection
79
- def for(example_or_group)
80
- self.class.
81
- new(hooks.select {|hook| hook.options_apply?(example_or_group)}).
82
- with(example_or_group)
83
- end
84
-
85
- def with(example)
86
- @example = example
87
- self
88
- end
89
-
90
- def run
91
- hooks.each {|h| h.run(@example)}
92
- end
93
- end
94
-
95
- class AroundHookCollection < BaseHookCollection
96
- def for(example, initial_procsy=nil)
97
- self.class.new(hooks.select {|hook| hook.options_apply?(example)}).
98
- with(example, initial_procsy)
99
- end
100
-
101
- def with(example, initial_procsy)
102
- @example = example
103
- @initial_procsy = initial_procsy
104
- self
105
- end
106
-
107
- def run
108
- hooks.reduce(@initial_procsy) do |procsy, around_hook|
109
- procsy.wrap do
110
- @example.instance_exec(procsy, &around_hook.block)
111
- end
112
- end.call
113
- end
114
- end
115
-
116
- class GroupHookCollection < BaseHookCollection
117
- def for(group)
118
- @group = group
119
- self
120
- end
121
-
122
- def run
123
- hooks.shift.run(@group) until hooks.empty?
124
- end
125
- end
126
-
127
- class HookCollections
128
- def initialize(data)
129
- @data = data
130
- end
131
-
132
- def [](key)
133
- @data[key]
134
- end
135
-
136
- def register_globals(host, globals)
137
- process(host, globals, :before, :each)
138
- process(host, globals, :after, :each)
139
- process(host, globals, :around, :each)
140
-
141
- process(host, globals, :before, :all)
142
- process(host, globals, :after, :all)
143
- end
144
-
145
- private
146
-
147
- def process(host, globals, position, scope)
148
- globals[position][scope].each do |hook|
149
- next unless scope == :each || hook.options_apply?(host)
150
- next if host.parent_groups.any? {|a| a.hooks[position][scope].include?(hook)}
151
- self[position][scope] << hook
152
- end
153
- end
154
- end
155
-
156
- # @private
157
- def hooks
158
- @hooks ||= HookCollections.new(
159
- :around => { :each => AroundHookCollection.new },
160
- :before => { :each => HookCollection.new, :all => HookCollection.new, :suite => HookCollection.new },
161
- :after => { :each => HookCollection.new, :all => HookCollection.new, :suite => HookCollection.new }
162
- )
163
- end
164
-
165
4
  # @api public
166
5
  # @overload before(&block)
167
6
  # @overload before(scope, &block)
@@ -329,7 +168,7 @@ EOS
329
168
  # end
330
169
  # end
331
170
  def before(*args, &block)
332
- register_hook :append, :before, *args, &block
171
+ hooks.register :append, :before, *args, &block
333
172
  end
334
173
 
335
174
  alias_method :append_before, :before
@@ -339,7 +178,7 @@ EOS
339
178
  #
340
179
  # See #before for scoping semantics.
341
180
  def prepend_before(*args, &block)
342
- register_hook :prepend, :before, *args, &block
181
+ hooks.register :prepend, :before, *args, &block
343
182
  end
344
183
 
345
184
  # @api public
@@ -391,7 +230,7 @@ EOS
391
230
  # Similarly, if more than one `after` is declared within any one scope,
392
231
  # they are run in reverse order of that in which they are declared.
393
232
  def after(*args, &block)
394
- register_hook :prepend, :after, *args, &block
233
+ hooks.register :prepend, :after, *args, &block
395
234
  end
396
235
 
397
236
  alias_method :prepend_after, :after
@@ -401,7 +240,7 @@ EOS
401
240
  #
402
241
  # See #after for scoping semantics.
403
242
  def append_after(*args, &block)
404
- register_hook :append, :after, *args, &block
243
+ hooks.register :append, :after, *args, &block
405
244
  end
406
245
 
407
246
  # @api public
@@ -449,85 +288,279 @@ EOS
449
288
  # around(:each) {|ex| FakeFS(&ex)}
450
289
  #
451
290
  def around(*args, &block)
452
- register_hook :prepend, :around, *args, &block
291
+ hooks.register :prepend, :around, *args, &block
292
+ end
293
+
294
+ # @api private
295
+ # Holds the various registered hooks.
296
+ def hooks
297
+ @hooks ||= HookCollections.new(self,
298
+ :around => { :each => AroundHookCollection.new },
299
+ :before => { :each => HookCollection.new, :all => HookCollection.new, :suite => HookCollection.new },
300
+ :after => { :each => HookCollection.new, :all => HookCollection.new, :suite => HookCollection.new }
301
+ )
453
302
  end
454
303
 
304
+ private
305
+
455
306
  # @private
456
- #
457
- # Runs all of the blocks stored with the hook in the context of the
458
- # example. If no example is provided, just calls the hook directly.
459
- def run_hook(hook, scope, example_or_group=ExampleGroup.new, initial_procsy=nil)
460
- return if RSpec.configuration.dry_run?
307
+ class Hook
308
+ attr_reader :block, :options
461
309
 
462
- find_hook(hook, scope, example_or_group, initial_procsy).run
310
+ def initialize(block, options)
311
+ @block = block
312
+ @options = options
313
+ end
314
+
315
+ def options_apply?(example_or_group)
316
+ example_or_group.all_apply?(options)
317
+ end
463
318
  end
464
319
 
465
320
  # @private
466
- def around_each_hooks_for(example, initial_procsy=nil)
467
- AroundHookCollection.new(FlatMap.flat_map(parent_groups) {|a| a.hooks[:around][:each]}).for(example, initial_procsy)
321
+ class BeforeHook < Hook
322
+ def run(example)
323
+ example.instance_exec(example, &block)
324
+ end
325
+
326
+ def display_name
327
+ "before hook"
328
+ end
468
329
  end
469
330
 
470
- private
331
+ # @private
332
+ class AfterHook < Hook
333
+ def run(example)
334
+ example.instance_exec_with_rescue("in an after hook", &block)
335
+ end
471
336
 
472
- SCOPES = [:each, :all, :suite]
337
+ def display_name
338
+ "after hook"
339
+ end
340
+ end
473
341
 
474
- HOOK_TYPES = {
475
- :before => Hash.new { BeforeHook },
476
- :after => Hash.new { AfterHook },
477
- :around => Hash.new { AroundHook }
478
- }
342
+ # @private
343
+ class AfterAllHook < Hook
344
+ def run(example)
345
+ example.instance_exec(example, &block)
346
+ rescue Exception => e
347
+ # TODO: come up with a better solution for this.
348
+ RSpec.configuration.reporter.message <<-EOS
479
349
 
480
- HOOK_TYPES[:after][:all] = AfterAllHook
350
+ An error occurred in an after(:all) hook.
351
+ #{e.class}: #{e.message}
352
+ occurred at #{e.backtrace.first}
481
353
 
482
- def before_all_hooks_for(group)
483
- GroupHookCollection.new(hooks[:before][:all]).for(group)
484
- end
354
+ EOS
355
+ end
485
356
 
486
- def after_all_hooks_for(group)
487
- GroupHookCollection.new(hooks[:after][:all]).for(group)
357
+ def display_name
358
+ "after(:all) hook"
359
+ end
488
360
  end
489
361
 
490
- def before_each_hooks_for(example)
491
- HookCollection.new(FlatMap.flat_map(parent_groups.reverse) {|a| a.hooks[:before][:each]}).for(example)
362
+ # @private
363
+ class AroundHook < Hook
364
+ def display_name
365
+ "around hook"
366
+ end
492
367
  end
493
368
 
494
- def after_each_hooks_for(example)
495
- HookCollection.new(FlatMap.flat_map(parent_groups) {|a| a.hooks[:after][:each]}).for(example)
369
+ # @private
370
+ class BaseHookCollection
371
+ Array.public_instance_methods(false).each do |name|
372
+ define_method(name) { |*a, &b| hooks.__send__(name, *a, &b) }
373
+ end
374
+
375
+ attr_reader :hooks
376
+ protected :hooks
377
+
378
+ alias append push
379
+ alias prepend unshift
380
+
381
+ def initialize(hooks=[])
382
+ @hooks = hooks
383
+ end
496
384
  end
497
385
 
498
- def register_hook prepend_or_append, hook, *args, &block
499
- scope, options = scope_and_options_from(*args)
500
- hooks[hook][scope].send(prepend_or_append, HOOK_TYPES[hook][scope].new(block, options))
386
+ # @private
387
+ class HookCollection < BaseHookCollection
388
+ def for(example_or_group)
389
+ self.class.
390
+ new(hooks.select {|hook| hook.options_apply?(example_or_group)}).
391
+ with(example_or_group)
392
+ end
393
+
394
+ def with(example)
395
+ @example = example
396
+ self
397
+ end
398
+
399
+ def run
400
+ hooks.each {|h| h.run(@example)}
401
+ end
501
402
  end
502
403
 
503
- def find_hook(hook, scope, example_or_group, initial_procsy)
504
- case [hook, scope]
505
- when [:before, :all]
506
- before_all_hooks_for(example_or_group)
507
- when [:after, :all]
508
- after_all_hooks_for(example_or_group)
509
- when [:around, :each]
510
- around_each_hooks_for(example_or_group, initial_procsy)
511
- when [:before, :each]
512
- before_each_hooks_for(example_or_group)
513
- when [:after, :each]
514
- after_each_hooks_for(example_or_group)
515
- when [:before, :suite], [:after, :suite]
516
- hooks[hook][:suite].with(example_or_group)
404
+ # @private
405
+ class AroundHookCollection < BaseHookCollection
406
+ def for(example, initial_procsy=nil)
407
+ self.class.new(hooks.select {|hook| hook.options_apply?(example)}).
408
+ with(example, initial_procsy)
409
+ end
410
+
411
+ def with(example, initial_procsy)
412
+ @example = example
413
+ @initial_procsy = initial_procsy
414
+ self
415
+ end
416
+
417
+ def run
418
+ hooks.inject(@initial_procsy) do |procsy, around_hook|
419
+ procsy.wrap do
420
+ @example.instance_exec(procsy, &around_hook.block)
421
+ end
422
+ end.call
517
423
  end
518
424
  end
519
425
 
520
- def scope_and_options_from(*args)
521
- return extract_scope_from(args), Metadata.build_hash_from(args)
426
+ # @private
427
+ class GroupHookCollection < BaseHookCollection
428
+ def for(group)
429
+ @group = group
430
+ self
431
+ end
432
+
433
+ def run
434
+ hooks.shift.run(@group) until hooks.empty?
435
+ end
522
436
  end
523
437
 
524
- def extract_scope_from(args)
525
- if SCOPES.include?(args.first)
526
- args.shift
527
- elsif args.any? { |a| a.is_a?(Symbol) }
528
- raise ArgumentError.new("You must explicitly give a scope (:each, :all, or :suite) when using symbols as metadata for a hook.")
529
- else
530
- :each
438
+ # @private
439
+ class HookCollections
440
+ def initialize(owner, data)
441
+ @owner = owner
442
+ @data = data
443
+ end
444
+
445
+ def [](key)
446
+ @data[key]
447
+ end
448
+
449
+ def register_globals(host, globals)
450
+ process(host, globals, :before, :each)
451
+ process(host, globals, :after, :each)
452
+ process(host, globals, :around, :each)
453
+
454
+ process(host, globals, :before, :all)
455
+ process(host, globals, :after, :all)
456
+ end
457
+
458
+ def around_each_hooks_for(example, initial_procsy=nil)
459
+ AroundHookCollection.new(FlatMap.flat_map(@owner.parent_groups) do |a|
460
+ a.hooks[:around][:each]
461
+ end).for(example, initial_procsy)
462
+ end
463
+
464
+ def register(prepend_or_append, hook, *args, &block)
465
+ scope, options = scope_and_options_from(*args)
466
+ self[hook][scope].__send__(prepend_or_append, HOOK_TYPES[hook][scope].new(block, options))
467
+ end
468
+
469
+ # @private
470
+ #
471
+ # Runs all of the blocks stored with the hook in the context of the
472
+ # example. If no example is provided, just calls the hook directly.
473
+ def run(hook, scope, example_or_group=ExampleGroup.new, initial_procsy=nil)
474
+ return if RSpec.configuration.dry_run?
475
+
476
+ find_hook(hook, scope, example_or_group, initial_procsy).run
477
+ end
478
+
479
+ SCOPES = [:each, :all, :suite]
480
+
481
+ SCOPE_ALIASES = {
482
+ :example => :each,
483
+ :context => :all,
484
+ }
485
+
486
+ HOOK_TYPES = {
487
+ :before => Hash.new { BeforeHook },
488
+ :after => Hash.new { AfterHook },
489
+ :around => Hash.new { AroundHook }
490
+ }
491
+
492
+ HOOK_TYPES[:after][:all] = AfterAllHook
493
+
494
+ private
495
+
496
+ def process(host, globals, position, scope)
497
+ globals[position][scope].each do |hook|
498
+ next unless scope == :each || hook.options_apply?(host)
499
+ next if host.parent_groups.any? {|a| a.hooks[position][scope].include?(hook)}
500
+ self[position][scope] << hook
501
+ end
502
+ end
503
+
504
+ def scope_and_options_from(*args)
505
+ return extract_scope_from(args), Metadata.build_hash_from(args)
506
+ end
507
+
508
+ def extract_scope_from(args)
509
+ if known_scope?(args.first)
510
+ normalized_scope_for(args.shift)
511
+ elsif args.any? { |a| a.is_a?(Symbol) }
512
+ error_message = "You must explicitly give a scope (#{SCOPES.join(", ")}) or scope alias (#{SCOPE_ALIASES.keys.join(", ")}) when using symbols as metadata for a hook."
513
+ raise ArgumentError.new error_message
514
+ else
515
+ :each
516
+ end
517
+ end
518
+
519
+ # @api private
520
+ def known_scope?(scope)
521
+ SCOPES.include?(scope) || SCOPE_ALIASES.keys.include?(scope)
522
+ end
523
+
524
+ # @api private
525
+ def normalized_scope_for(scope)
526
+ SCOPE_ALIASES[scope] || scope
527
+ end
528
+
529
+ def find_hook(hook, scope, example_or_group, initial_procsy)
530
+ case [hook, scope]
531
+ when [:before, :all]
532
+ before_all_hooks_for(example_or_group)
533
+ when [:after, :all]
534
+ after_all_hooks_for(example_or_group)
535
+ when [:around, :each]
536
+ around_each_hooks_for(example_or_group, initial_procsy)
537
+ when [:before, :each]
538
+ before_each_hooks_for(example_or_group)
539
+ when [:after, :each]
540
+ after_each_hooks_for(example_or_group)
541
+ when [:before, :suite], [:after, :suite]
542
+ self[hook][:suite].with(example_or_group)
543
+ end
544
+ end
545
+
546
+ def before_all_hooks_for(group)
547
+ GroupHookCollection.new(self[:before][:all]).for(group)
548
+ end
549
+
550
+ def after_all_hooks_for(group)
551
+ GroupHookCollection.new(self[:after][:all]).for(group)
552
+ end
553
+
554
+ def before_each_hooks_for(example)
555
+ HookCollection.new(FlatMap.flat_map(@owner.parent_groups.reverse) do |a|
556
+ a.hooks[:before][:each]
557
+ end).for(example)
558
+ end
559
+
560
+ def after_each_hooks_for(example)
561
+ HookCollection.new(FlatMap.flat_map(@owner.parent_groups) do |a|
562
+ a.hooks[:after][:each]
563
+ end).for(example)
531
564
  end
532
565
  end
533
566
  end