rspec-core 2.99.0 → 3.0.0

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 (216) hide show
  1. checksums.yaml +8 -8
  2. checksums.yaml.gz.sig +0 -0
  3. data/.yardopts +1 -0
  4. data/Changelog.md +339 -1
  5. data/License.txt +1 -0
  6. data/README.md +19 -37
  7. data/exe/rspec +2 -23
  8. data/lib/rspec/core/backport_random.rb +336 -0
  9. data/lib/rspec/core/backtrace_formatter.rb +70 -0
  10. data/lib/rspec/core/configuration.rb +479 -577
  11. data/lib/rspec/core/configuration_options.rb +71 -54
  12. data/lib/rspec/core/drb.rb +111 -0
  13. data/lib/rspec/core/dsl.rb +86 -18
  14. data/lib/rspec/core/example.rb +267 -119
  15. data/lib/rspec/core/example_group.rb +381 -393
  16. data/lib/rspec/core/filter_manager.rb +133 -114
  17. data/lib/rspec/core/flat_map.rb +18 -0
  18. data/lib/rspec/core/formatters/base_formatter.rb +51 -125
  19. data/lib/rspec/core/formatters/base_text_formatter.rb +34 -286
  20. data/lib/rspec/core/formatters/console_codes.rb +24 -1
  21. data/lib/rspec/core/formatters/deprecation_formatter.rb +25 -26
  22. data/lib/rspec/core/formatters/documentation_formatter.rb +27 -25
  23. data/lib/rspec/core/formatters/helpers.rb +14 -36
  24. data/lib/rspec/core/formatters/html_formatter.rb +58 -65
  25. data/lib/rspec/core/formatters/html_printer.rb +12 -18
  26. data/lib/rspec/core/formatters/json_formatter.rb +54 -31
  27. data/lib/rspec/core/formatters/profile_formatter.rb +67 -0
  28. data/lib/rspec/core/formatters/progress_formatter.rb +11 -15
  29. data/lib/rspec/core/formatters/snippet_extractor.rb +22 -11
  30. data/lib/rspec/core/formatters.rb +90 -43
  31. data/lib/rspec/core/hooks.rb +325 -270
  32. data/lib/rspec/core/memoized_helpers.rb +45 -156
  33. data/lib/rspec/core/metadata.rb +284 -236
  34. data/lib/rspec/core/metadata_filter.rb +86 -0
  35. data/lib/rspec/core/mocking_adapters/flexmock.rb +29 -0
  36. data/lib/rspec/core/{mocking/with_mocha.rb → mocking_adapters/mocha.rb} +19 -16
  37. data/lib/rspec/core/mocking_adapters/null.rb +12 -0
  38. data/lib/rspec/core/mocking_adapters/rr.rb +29 -0
  39. data/lib/rspec/core/mocking_adapters/rspec.rb +30 -0
  40. data/lib/rspec/core/notifications.rb +511 -0
  41. data/lib/rspec/core/option_parser.rb +49 -74
  42. data/lib/rspec/core/ordering.rb +156 -0
  43. data/lib/rspec/core/pending.rb +94 -96
  44. data/lib/rspec/core/project_initializer/.rspec +3 -0
  45. data/lib/rspec/core/project_initializer/spec/spec_helper.rb +78 -0
  46. data/lib/rspec/core/project_initializer.rb +24 -64
  47. data/lib/rspec/core/rake_task.rb +44 -130
  48. data/lib/rspec/core/reporter.rb +76 -59
  49. data/lib/rspec/core/ruby_project.rb +2 -1
  50. data/lib/rspec/core/runner.rb +117 -52
  51. data/lib/rspec/core/shared_context.rb +10 -10
  52. data/lib/rspec/core/shared_example_group.rb +108 -121
  53. data/lib/rspec/core/version.rb +3 -1
  54. data/lib/rspec/core/warnings.rb +40 -0
  55. data/lib/rspec/core/world.rb +66 -10
  56. data/lib/rspec/core.rb +64 -104
  57. data.tar.gz.sig +3 -0
  58. metadata +75 -322
  59. metadata.gz.sig +1 -0
  60. data/exe/autospec +0 -13
  61. data/features/Autotest.md +0 -38
  62. data/features/README.md +0 -17
  63. data/features/Upgrade.md +0 -364
  64. data/features/command_line/README.md +0 -28
  65. data/features/command_line/example_name_option.feature +0 -97
  66. data/features/command_line/exit_status.feature +0 -82
  67. data/features/command_line/format_option.feature +0 -75
  68. data/features/command_line/init.feature +0 -18
  69. data/features/command_line/line_number_appended_to_path.feature +0 -140
  70. data/features/command_line/line_number_option.feature +0 -53
  71. data/features/command_line/order.feature +0 -26
  72. data/features/command_line/pattern_option.feature +0 -49
  73. data/features/command_line/rake_task.feature +0 -122
  74. data/features/command_line/require_option.feature +0 -43
  75. data/features/command_line/ruby.feature +0 -22
  76. data/features/command_line/tag.feature +0 -98
  77. data/features/command_line/warnings_option.feature +0 -29
  78. data/features/configuration/alias_example_to.feature +0 -48
  79. data/features/configuration/backtrace_clean_patterns.feature +0 -102
  80. data/features/configuration/custom_settings.feature +0 -84
  81. data/features/configuration/default_path.feature +0 -38
  82. data/features/configuration/deprecation_stream.feature +0 -58
  83. data/features/configuration/fail_fast.feature +0 -77
  84. data/features/configuration/failure_exit_code.feature +0 -36
  85. data/features/configuration/order_and_seed.feature +0 -3
  86. data/features/configuration/output_stream.feature +0 -24
  87. data/features/configuration/pattern.feature +0 -38
  88. data/features/configuration/profile.feature +0 -220
  89. data/features/configuration/read_options_from_file.feature +0 -90
  90. data/features/configuration/run_all_when_everything_filtered.feature +0 -76
  91. data/features/configuration/show_failures_in_pending_blocks.feature +0 -61
  92. data/features/configuration/treat_symbols_as_metadata_keys_with_true_values.feature +0 -52
  93. data/features/example_groups/basic_structure.feature +0 -55
  94. data/features/example_groups/shared_context.feature +0 -74
  95. data/features/example_groups/shared_examples.feature +0 -294
  96. data/features/expectation_framework_integration/configure_expectation_framework.feature +0 -197
  97. data/features/filtering/exclusion_filters.feature +0 -138
  98. data/features/filtering/if_and_unless.feature +0 -168
  99. data/features/filtering/inclusion_filters.feature +0 -105
  100. data/features/formatters/configurable_colors.feature +0 -31
  101. data/features/formatters/custom_formatter.feature +0 -36
  102. data/features/formatters/json_formatter.feature +0 -30
  103. data/features/formatters/text_formatter.feature +0 -46
  104. data/features/helper_methods/arbitrary_methods.feature +0 -40
  105. data/features/helper_methods/let.feature +0 -50
  106. data/features/helper_methods/modules.feature +0 -149
  107. data/features/hooks/around_hooks.feature +0 -343
  108. data/features/hooks/before_and_after_hooks.feature +0 -427
  109. data/features/hooks/filtering.feature +0 -234
  110. data/features/metadata/current_example.feature +0 -56
  111. data/features/metadata/described_class.feature +0 -17
  112. data/features/metadata/user_defined.feature +0 -113
  113. data/features/mock_framework_integration/use_any_framework.feature +0 -106
  114. data/features/mock_framework_integration/use_flexmock.feature +0 -96
  115. data/features/mock_framework_integration/use_mocha.feature +0 -97
  116. data/features/mock_framework_integration/use_rr.feature +0 -98
  117. data/features/mock_framework_integration/use_rspec.feature +0 -97
  118. data/features/pending/pending_examples.feature +0 -229
  119. data/features/spec_files/arbitrary_file_suffix.feature +0 -13
  120. data/features/step_definitions/additional_cli_steps.rb +0 -49
  121. data/features/subject/attribute_of_subject.feature +0 -124
  122. data/features/subject/explicit_subject.feature +0 -101
  123. data/features/subject/implicit_subject.feature +0 -63
  124. data/features/subject/one_liner_syntax.feature +0 -71
  125. data/features/support/env.rb +0 -14
  126. data/features/support/rubinius.rb +0 -6
  127. data/lib/autotest/discover.rb +0 -10
  128. data/lib/autotest/rspec2.rb +0 -77
  129. data/lib/rspec/core/backtrace_cleaner.rb +0 -46
  130. data/lib/rspec/core/backward_compatibility.rb +0 -55
  131. data/lib/rspec/core/caller_filter.rb +0 -60
  132. data/lib/rspec/core/command_line.rb +0 -47
  133. data/lib/rspec/core/deprecated_mutable_array_proxy.rb +0 -32
  134. data/lib/rspec/core/deprecation.rb +0 -26
  135. data/lib/rspec/core/drb_command_line.rb +0 -26
  136. data/lib/rspec/core/drb_options.rb +0 -87
  137. data/lib/rspec/core/extensions/instance_eval_with_args.rb +0 -44
  138. data/lib/rspec/core/extensions/kernel.rb +0 -9
  139. data/lib/rspec/core/extensions/module_eval_with_args.rb +0 -38
  140. data/lib/rspec/core/extensions/ordered.rb +0 -27
  141. data/lib/rspec/core/formatters/text_mate_formatter.rb +0 -34
  142. data/lib/rspec/core/metadata_hash_builder.rb +0 -97
  143. data/lib/rspec/core/mocking/with_absolutely_nothing.rb +0 -11
  144. data/lib/rspec/core/mocking/with_flexmock.rb +0 -27
  145. data/lib/rspec/core/mocking/with_rr.rb +0 -27
  146. data/lib/rspec/core/mocking/with_rspec.rb +0 -27
  147. data/lib/rspec/core/shared_example_group/collection.rb +0 -45
  148. data/spec/autotest/discover_spec.rb +0 -49
  149. data/spec/autotest/failed_results_re_spec.rb +0 -45
  150. data/spec/autotest/rspec_spec.rb +0 -133
  151. data/spec/command_line/order_spec.rb +0 -204
  152. data/spec/rspec/core/backtrace_cleaner_spec.rb +0 -68
  153. data/spec/rspec/core/caller_filter_spec.rb +0 -58
  154. data/spec/rspec/core/command_line_spec.rb +0 -127
  155. data/spec/rspec/core/command_line_spec_output.txt +0 -0
  156. data/spec/rspec/core/configuration_options_spec.rb +0 -417
  157. data/spec/rspec/core/configuration_spec.rb +0 -2072
  158. data/spec/rspec/core/deprecation_spec.rb +0 -27
  159. data/spec/rspec/core/deprecations_spec.rb +0 -59
  160. data/spec/rspec/core/drb_command_line_spec.rb +0 -102
  161. data/spec/rspec/core/drb_options_spec.rb +0 -193
  162. data/spec/rspec/core/dsl_spec.rb +0 -25
  163. data/spec/rspec/core/example_group_spec.rb +0 -1337
  164. data/spec/rspec/core/example_spec.rb +0 -502
  165. data/spec/rspec/core/filter_manager_spec.rb +0 -246
  166. data/spec/rspec/core/formatters/base_formatter_spec.rb +0 -108
  167. data/spec/rspec/core/formatters/base_text_formatter_spec.rb +0 -524
  168. data/spec/rspec/core/formatters/console_codes_spec.rb +0 -50
  169. data/spec/rspec/core/formatters/deprecation_formatter_spec.rb +0 -263
  170. data/spec/rspec/core/formatters/documentation_formatter_spec.rb +0 -89
  171. data/spec/rspec/core/formatters/helpers_spec.rb +0 -104
  172. data/spec/rspec/core/formatters/html_formatted.html +0 -397
  173. data/spec/rspec/core/formatters/html_formatter_spec.rb +0 -131
  174. data/spec/rspec/core/formatters/json_formatter_spec.rb +0 -115
  175. data/spec/rspec/core/formatters/progress_formatter_spec.rb +0 -30
  176. data/spec/rspec/core/formatters/snippet_extractor_spec.rb +0 -26
  177. data/spec/rspec/core/formatters/text_mate_formatted.html +0 -397
  178. data/spec/rspec/core/formatters/text_mate_formatter_spec.rb +0 -107
  179. data/spec/rspec/core/hooks_filtering_spec.rb +0 -227
  180. data/spec/rspec/core/hooks_spec.rb +0 -269
  181. data/spec/rspec/core/kernel_extensions_spec.rb +0 -9
  182. data/spec/rspec/core/memoized_helpers_spec.rb +0 -776
  183. data/spec/rspec/core/metadata_spec.rb +0 -514
  184. data/spec/rspec/core/option_parser_spec.rb +0 -297
  185. data/spec/rspec/core/pending_example_spec.rb +0 -260
  186. data/spec/rspec/core/pending_spec.rb +0 -27
  187. data/spec/rspec/core/project_initializer_spec.rb +0 -130
  188. data/spec/rspec/core/rake_task_spec.rb +0 -202
  189. data/spec/rspec/core/reporter_spec.rb +0 -167
  190. data/spec/rspec/core/resources/a_bar.rb +0 -0
  191. data/spec/rspec/core/resources/a_foo.rb +0 -0
  192. data/spec/rspec/core/resources/a_spec.rb +0 -1
  193. data/spec/rspec/core/resources/custom_example_group_runner.rb +0 -14
  194. data/spec/rspec/core/resources/formatter_specs.rb +0 -60
  195. data/spec/rspec/core/resources/utf8_encoded.rb +0 -8
  196. data/spec/rspec/core/rspec_matchers_spec.rb +0 -45
  197. data/spec/rspec/core/ruby_project_spec.rb +0 -26
  198. data/spec/rspec/core/runner_spec.rb +0 -82
  199. data/spec/rspec/core/shared_context_spec.rb +0 -114
  200. data/spec/rspec/core/shared_example_group/collection_spec.rb +0 -70
  201. data/spec/rspec/core/shared_example_group_spec.rb +0 -138
  202. data/spec/rspec/core/world_spec.rb +0 -142
  203. data/spec/rspec/core_spec.rb +0 -108
  204. data/spec/spec_helper.rb +0 -135
  205. data/spec/support/config_options_helper.rb +0 -15
  206. data/spec/support/helper_methods.rb +0 -55
  207. data/spec/support/in_sub_process.rb +0 -37
  208. data/spec/support/isolate_load_path_mutation.rb +0 -6
  209. data/spec/support/isolated_directory.rb +0 -10
  210. data/spec/support/isolated_home_directory.rb +0 -16
  211. data/spec/support/matchers.rb +0 -65
  212. data/spec/support/mathn_integration_support.rb +0 -12
  213. data/spec/support/sandboxed_mock_space.rb +0 -116
  214. data/spec/support/shared_example_groups.rb +0 -42
  215. data/spec/support/silence_dsl_deprecations.rb +0 -32
  216. data/spec/support/spec_files.rb +0 -44
@@ -1,164 +1,28 @@
1
1
  module RSpec
2
2
  module Core
3
+ # Provides `before`, `after` and `around` hooks as a means of
4
+ # supporting common setup and teardown. This module is extended
5
+ # onto {ExampleGroup}, making the methods available from any `describe`
6
+ # or `context` block and included in {Configuration}, making them
7
+ # available off of the configuration object to define global setup
8
+ # or teardown logic.
3
9
  module Hooks
4
- include MetadataHashBuilder::WithConfigWarning
5
-
6
- class Hook
7
- attr_reader :block, :options
8
-
9
- def initialize(block, options)
10
- @block = block
11
- @options = options
12
- end
13
-
14
- def options_apply?(example_or_group)
15
- example_or_group.all_apply?(options)
16
- end
17
- end
18
-
19
- class BeforeHook < Hook
20
- def run(example)
21
- example.instance_eval_with_args(example, &block)
22
- end
23
-
24
- def display_name
25
- "before hook"
26
- end
27
- end
28
-
29
- class AfterHook < Hook
30
- def run(example)
31
- example.instance_eval_with_rescue("in an after hook", &block)
32
- end
33
-
34
- def display_name
35
- "after hook"
36
- end
37
- end
38
-
39
- class AfterAllHook < Hook
40
- def run(example)
41
- example.instance_exec(example, &block)
42
- rescue Exception => e
43
- # TODO: come up with a better solution for this.
44
- RSpec.configuration.reporter.message <<-EOS
45
-
46
- An error occurred in an after(:all) hook.
47
- #{e.class}: #{e.message}
48
- occurred at #{e.backtrace.first}
49
-
50
- EOS
51
- end
52
-
53
- def display_name
54
- "after(:all) hook"
55
- end
56
- end
57
-
58
- class AroundHook < Hook
59
- def display_name
60
- "around hook"
61
- end
62
- end
63
-
64
- module HookCollectionAliases
65
- def self.included(host)
66
- host.send :alias_method, :prepend, :unshift
67
- host.send :alias_method, :append, :push
68
- end
69
- end
70
-
71
- class HookCollection < Array
72
- include HookCollectionAliases
73
-
74
- def for(example_or_group)
75
- self.class.new(select {|hook| hook.options_apply?(example_or_group)}).
76
- with(example_or_group)
77
- end
78
-
79
- def with(example)
80
- @example = example
81
- self
82
- end
83
-
84
- def run
85
- each {|h| h.run(@example) } unless empty?
86
- end
87
- end
88
-
89
- class AroundHookCollection < Array
90
- include HookCollectionAliases
91
-
92
- def for(example, initial_procsy=nil)
93
- self.class.new(select {|hook| hook.options_apply?(example)}).
94
- with(example, initial_procsy)
95
- end
96
-
97
- def with(example, initial_procsy)
98
- @example = example
99
- @initial_procsy = initial_procsy
100
- self
101
- end
102
-
103
- def run
104
- inject(@initial_procsy) do |procsy, around_hook|
105
- Example.procsy(procsy.metadata) do
106
- @example.instance_eval_with_args(procsy, &around_hook.block)
107
- end
108
- end.call
109
- end
110
- end
111
-
112
- class GroupHookCollection < Array
113
- def for(group)
114
- @group = group
115
- self
116
- end
117
-
118
- def run
119
- shift.run(@group) until empty?
120
- end
121
- end
122
-
123
- module RegistersGlobals
124
- def register_globals host, globals
125
- [:before, :after, :around].each do |position|
126
- process host, globals, position, :each
127
- next if position == :around # no around(:all) hooks
128
- process host, globals, position, :all
129
- end
130
- end
131
-
132
- private
133
- def process host, globals, position, scope
134
- globals[position][scope].each do |hook|
135
- unless host.parent_groups.any? { |a| a.hooks[position][scope].include? hook }
136
- self[position][scope] << hook if scope == :each || hook.options_apply?(host)
137
- end
138
- end
139
- end
140
- end
141
-
142
- # @private
143
- def hooks
144
- @hooks ||= {
145
- :around => { :each => AroundHookCollection.new },
146
- :before => { :each => HookCollection.new, :all => HookCollection.new, :suite => HookCollection.new },
147
- :after => { :each => HookCollection.new, :all => HookCollection.new, :suite => HookCollection.new }
148
- }.extend(RegistersGlobals)
149
- end
150
-
151
10
  # @api public
11
+ #
152
12
  # @overload before(&block)
153
13
  # @overload before(scope, &block)
14
+ # @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to `:example`)
154
15
  # @overload before(scope, conditions, &block)
16
+ # @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to `:example`)
17
+ # @param conditions [Hash]
18
+ # constrains this hook to examples matching these conditions e.g.
19
+ # `before(:example, :ui => true) { ... }` will only run with examples or
20
+ # groups declared with `:ui => true`.
155
21
  # @overload before(conditions, &block)
156
- #
157
- # @param [Symbol] scope `:each`, `:all`, or `:suite` (defaults to `:each`)
158
- # @param [Hash] conditions
159
- # constrains this hook to examples matching these conditions e.g.
160
- # `before(:each, :ui => true) { ... }` will only run with examples or
161
- # groups declared with `:ui => true`.
22
+ # @param conditions [Hash]
23
+ # constrains this hook to examples matching these conditions e.g.
24
+ # `before(:example, :ui => true) { ... }` will only run with examples or
25
+ # groups declared with `:ui => true`.
162
26
  #
163
27
  # @see #after
164
28
  # @see #around
@@ -167,43 +31,43 @@ EOS
167
31
  # @see SharedExampleGroup
168
32
  # @see Configuration
169
33
  #
170
- # Declare a block of code to be run before each example (using `:each`)
171
- # or once before any example (using `:all`). These are usually declared
34
+ # Declare a block of code to be run before each example (using `:example`)
35
+ # or once before any example (using `:context`). These are usually declared
172
36
  # directly in the {ExampleGroup} to which they apply, but they can also
173
37
  # be shared across multiple groups.
174
38
  #
175
39
  # You can also use `before(:suite)` to run a block of code before any
176
40
  # example groups are run. This should be declared in {RSpec.configure}
177
41
  #
178
- # Instance variables declared in `before(:each)` or `before(:all)` are
42
+ # Instance variables declared in `before(:example)` or `before(:context)` are
179
43
  # accessible within each example.
180
44
  #
181
45
  # ### Order
182
46
  #
183
47
  # `before` hooks are stored in three scopes, which are run in order:
184
- # `:suite`, `:all`, and `:each`. They can also be declared in several
48
+ # `:suite`, `:context`, and `:example`. They can also be declared in several
185
49
  # different places: `RSpec.configure`, a parent group, the current group.
186
50
  # They are run in the following order:
187
51
  #
188
- # before(:suite) # declared in RSpec.configure
189
- # before(:all) # declared in RSpec.configure
190
- # before(:all) # declared in a parent group
191
- # before(:all) # declared in the current group
192
- # before(:each) # declared in RSpec.configure
193
- # before(:each) # declared in a parent group
194
- # before(:each) # declared in the current group
52
+ # before(:suite) # declared in RSpec.configure
53
+ # before(:context) # declared in RSpec.configure
54
+ # before(:context) # declared in a parent group
55
+ # before(:context) # declared in the current group
56
+ # before(:example) # declared in RSpec.configure
57
+ # before(:example) # declared in a parent group
58
+ # before(:example) # declared in the current group
195
59
  #
196
60
  # If more than one `before` is declared within any one scope, they are run
197
61
  # in the order in which they are declared.
198
62
  #
199
63
  # ### Conditions
200
64
  #
201
- # When you add a conditions hash to `before(:each)` or `before(:all)`,
65
+ # When you add a conditions hash to `before(:example)` or `before(:context)`,
202
66
  # RSpec will only apply that hook to groups or examples that match the
203
67
  # conditions. e.g.
204
68
  #
205
69
  # RSpec.configure do |config|
206
- # config.before(:each, :authorized => true) do
70
+ # config.before(:example, :authorized => true) do
207
71
  # log_in_as :authorized_user
208
72
  # end
209
73
  # end
@@ -232,7 +96,7 @@ EOS
232
96
  #
233
97
  # When an exception is raised in a `before` block, RSpec skips any
234
98
  # subsequent `before` blocks and the example, but runs all of the
235
- # `after(:each)` and `after(:all)` hooks.
99
+ # `after(:example)` and `after(:context)` hooks.
236
100
  #
237
101
  # ### Warning: implicit before blocks
238
102
  #
@@ -243,20 +107,20 @@ EOS
243
107
  # before block depends on state that is prepared in another before block
244
108
  # that gets run later.
245
109
  #
246
- # ### Warning: `before(:all)`
110
+ # ### Warning: `before(:context)`
247
111
  #
248
- # It is very tempting to use `before(:all)` to speed things up, but we
112
+ # It is very tempting to use `before(:context)` to speed things up, but we
249
113
  # recommend that you avoid this as there are a number of gotchas, as well
250
114
  # as things that simply don't work.
251
115
  #
252
116
  # #### context
253
117
  #
254
- # `before(:all)` is run in an example that is generated to provide group
118
+ # `before(:context)` is run in an example that is generated to provide group
255
119
  # context for the block.
256
120
  #
257
121
  # #### instance variables
258
122
  #
259
- # Instance variables declared in `before(:all)` are shared across all the
123
+ # Instance variables declared in `before(:context)` are shared across all the
260
124
  # examples in the group. This means that each example can change the
261
125
  # state of a shared object, resulting in an ordering dependency that can
262
126
  # make it difficult to reason about failures.
@@ -264,7 +128,7 @@ EOS
264
128
  # #### unsupported rspec constructs
265
129
  #
266
130
  # RSpec has several constructs that reset state between each example
267
- # automatically. These are not intended for use from within `before(:all)`:
131
+ # automatically. These are not intended for use from within `before(:context)`:
268
132
  #
269
133
  # * `let` declarations
270
134
  # * `subject` declarations
@@ -276,17 +140,17 @@ EOS
276
140
  # ActiveRecord) are typically designed around the idea of setting up
277
141
  # before an example, running that one example, and then tearing down.
278
142
  # This means that mocks and stubs can (sometimes) be declared in
279
- # `before(:all)`, but get torn down before the first real example is ever
143
+ # `before(:context)`, but get torn down before the first real example is ever
280
144
  # run.
281
145
  #
282
- # You _can_ create database-backed model objects in a `before(:all)` in
146
+ # You _can_ create database-backed model objects in a `before(:context)` in
283
147
  # rspec-rails, but it will not be wrapped in a transaction for you, so
284
- # you are on your own to clean up in an `after(:all)` block.
148
+ # you are on your own to clean up in an `after(:context)` block.
285
149
  #
286
- # @example before(:each) declared in an {ExampleGroup}
150
+ # @example before(:example) declared in an {ExampleGroup}
287
151
  #
288
152
  # describe Thing do
289
- # before(:each) do
153
+ # before(:example) do
290
154
  # @thing = Thing.new
291
155
  # end
292
156
  #
@@ -295,10 +159,10 @@ EOS
295
159
  # end
296
160
  # end
297
161
  #
298
- # @example before(:all) declared in an {ExampleGroup}
162
+ # @example before(:context) declared in an {ExampleGroup}
299
163
  #
300
164
  # describe Parser do
301
- # before(:all) do
165
+ # before(:context) do
302
166
  # File.open(file_to_parse, 'w') do |f|
303
167
  # f.write <<-CONTENT
304
168
  # stuff in the file
@@ -310,35 +174,42 @@ EOS
310
174
  # Parser.parse(file_to_parse)
311
175
  # end
312
176
  #
313
- # after(:all) do
177
+ # after(:context) do
314
178
  # File.delete(file_to_parse)
315
179
  # end
316
180
  # end
181
+ #
182
+ # @note The `:example` and `:context` scopes are also available as
183
+ # `:each` and `:all`, respectively. Use whichever you prefer.
317
184
  def before(*args, &block)
318
- register_hook :append, :before, *args, &block
185
+ hooks.register :append, :before, *args, &block
319
186
  end
320
187
 
321
188
  alias_method :append_before, :before
322
189
 
323
190
  # Adds `block` to the front of the list of `before` blocks in the same
324
- # scope (`:each`, `:all`, or `:suite`).
191
+ # scope (`:example`, `:context`, or `:suite`).
325
192
  #
326
- # See #before for scoping semantics.
193
+ # See {#before} for scoping semantics.
327
194
  def prepend_before(*args, &block)
328
- register_hook :prepend, :before, *args, &block
195
+ hooks.register :prepend, :before, *args, &block
329
196
  end
330
197
 
331
198
  # @api public
332
199
  # @overload after(&block)
333
200
  # @overload after(scope, &block)
201
+ # @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to `:example`)
334
202
  # @overload after(scope, conditions, &block)
203
+ # @param scope [Symbol] `:example`, `:context`, or `:suite` (defaults to `:example`)
204
+ # @param conditions [Hash]
205
+ # constrains this hook to examples matching these conditions e.g.
206
+ # `after(:example, :ui => true) { ... }` will only run with examples or
207
+ # groups declared with `:ui => true`.
335
208
  # @overload after(conditions, &block)
336
- #
337
- # @param [Symbol] scope `:each`, `:all`, or `:suite` (defaults to `:each`)
338
- # @param [Hash] conditions
339
- # constrains this hook to examples matching these conditions e.g.
340
- # `after(:each, :ui => true) { ... }` will only run with examples or
341
- # groups declared with `:ui => true`.
209
+ # @param conditions [Hash]
210
+ # constrains this hook to examples matching these conditions e.g.
211
+ # `after(:example, :ui => true) { ... }` will only run with examples or
212
+ # groups declared with `:ui => true`.
342
213
  #
343
214
  # @see #before
344
215
  # @see #around
@@ -347,10 +218,9 @@ EOS
347
218
  # @see SharedExampleGroup
348
219
  # @see Configuration
349
220
  #
350
- # Declare a block of code to be run after each example (using `:each`) or
351
- # once after all examples (using `:all`). See
352
- # [#before](Hooks#before-instance_method) for more information about
353
- # ordering.
221
+ # Declare a block of code to be run after each example (using `:example`) or
222
+ # once after all examples n the context (using `:context`). See {#before} for
223
+ # more information about ordering.
354
224
  #
355
225
  # ### Exceptions
356
226
  #
@@ -362,48 +232,57 @@ EOS
362
232
  # ### Order
363
233
  #
364
234
  # `after` hooks are stored in three scopes, which are run in order:
365
- # `:each`, `:all`, and `:suite`. They can also be declared in several
235
+ # `:example`, `:context`, and `:suite`. They can also be declared in several
366
236
  # different places: `RSpec.configure`, a parent group, the current group.
367
237
  # They are run in the following order:
368
238
  #
369
- # after(:each) # declared in the current group
370
- # after(:each) # declared in a parent group
371
- # after(:each) # declared in RSpec.configure
372
- # after(:all) # declared in the current group
373
- # after(:all) # declared in a parent group
374
- # after(:all) # declared in RSpec.configure
239
+ # after(:example) # declared in the current group
240
+ # after(:example) # declared in a parent group
241
+ # after(:example) # declared in RSpec.configure
242
+ # after(:context) # declared in the current group
243
+ # after(:context) # declared in a parent group
244
+ # after(:context) # declared in RSpec.configure
245
+ # after(:suite) # declared in RSpec.configure
375
246
  #
376
247
  # This is the reverse of the order in which `before` hooks are run.
377
248
  # Similarly, if more than one `after` is declared within any one scope,
378
249
  # they are run in reverse order of that in which they are declared.
250
+ #
251
+ # @note The `:example` and `:context` scopes are also available as
252
+ # `:each` and `:all`, respectively. Use whichever you prefer.
379
253
  def after(*args, &block)
380
- register_hook :prepend, :after, *args, &block
254
+ hooks.register :prepend, :after, *args, &block
381
255
  end
382
256
 
383
257
  alias_method :prepend_after, :after
384
258
 
385
259
  # Adds `block` to the back of the list of `after` blocks in the same
386
- # scope (`:each`, `:all`, or `:suite`).
260
+ # scope (`:example`, `:context`, or `:suite`).
387
261
  #
388
- # See #after for scoping semantics.
262
+ # See {#after} for scoping semantics.
389
263
  def append_after(*args, &block)
390
- register_hook :append, :after, *args, &block
264
+ hooks.register :append, :after, *args, &block
391
265
  end
392
266
 
393
267
  # @api public
394
268
  # @overload around(&block)
395
269
  # @overload around(scope, &block)
270
+ # @param scope [Symbol] `:example` (defaults to `:example`)
271
+ # present for syntax parity with `before` and `after`, but
272
+ # `:example`/`:each` is the only supported value.
396
273
  # @overload around(scope, conditions, &block)
274
+ # @param scope [Symbol] `:example` (defaults to `:example`)
275
+ # present for syntax parity with `before` and `after`, but
276
+ # `:example`/`:each` is the only supported value.
277
+ # @param conditions [Hash]
278
+ # constrains this hook to examples matching these conditions e.g.
279
+ # `around(:example, :ui => true) { ... }` will only run with examples or
280
+ # groups declared with `:ui => true`.
397
281
  # @overload around(conditions, &block)
398
- #
399
- # @param [Symbol] scope `:each` (defaults to `:each`)
400
- # present for syntax parity with `before` and `after`, but `:each` is
401
- # the only supported value.
402
- #
403
- # @param [Hash] conditions
404
- # constrains this hook to examples matching these conditions e.g.
405
- # `around(:each, :ui => true) { ... }` will only run with examples or
406
- # groups declared with `:ui => true`.
282
+ # @param conditions [Hash]
283
+ # constrains this hook to examples matching these conditions e.g.
284
+ # `around(:example, :ui => true) { ... }` will only run with examples or
285
+ # groups declared with `:ui => true`.
407
286
  #
408
287
  # @yield [Example] the example to run
409
288
  #
@@ -415,12 +294,12 @@ EOS
415
294
  # resources that are made available within the examples and their
416
295
  # associated `before` and `after` hooks.
417
296
  #
418
- # @note `:each` is the only supported scope.
297
+ # @note `:example`/`:each` is the only supported scope.
419
298
  #
420
299
  # Declare a block of code, parts of which will be run before and parts
421
300
  # after the example. It is your responsibility to run the example:
422
301
  #
423
- # around(:each) do |ex|
302
+ # around(:example) do |ex|
424
303
  # # do some stuff before
425
304
  # ex.run
426
305
  # # do some stuff after
@@ -431,87 +310,263 @@ EOS
431
310
  # that manage their own setup and teardown using a block or proc syntax,
432
311
  # e.g.
433
312
  #
434
- # around(:each) {|ex| Database.transaction(&ex)}
435
- # around(:each) {|ex| FakeFS(&ex)}
313
+ # around(:example) {|ex| Database.transaction(&ex)}
314
+ # around(:example) {|ex| FakeFS(&ex)}
436
315
  #
437
316
  def around(*args, &block)
438
- register_hook :prepend, :around, *args, &block
317
+ hooks.register :prepend, :around, *args, &block
439
318
  end
440
319
 
441
320
  # @private
442
- #
443
- # Runs all of the blocks stored with the hook in the context of the
444
- # example. If no example is provided, just calls the hook directly.
445
- def run_hook(hook, scope, example_or_group=ExampleGroup.new, initial_procsy=nil)
446
- find_hook(hook, scope, example_or_group, initial_procsy).run
447
- end
448
-
449
- # @private
450
- def around_each_hooks_for(example, initial_procsy=nil)
451
- AroundHookCollection.new(parent_groups.map {|a| a.hooks[:around][:each]}.flatten).for(example, initial_procsy)
321
+ # Holds the various registered hooks.
322
+ def hooks
323
+ @hooks ||= HookCollections.new(self,
324
+ :around => { :example => AroundHookCollection.new },
325
+ :before => { :example => HookCollection.new, :context => HookCollection.new, :suite => HookCollection.new },
326
+ :after => { :example => HookCollection.new, :context => HookCollection.new, :suite => HookCollection.new }
327
+ )
452
328
  end
453
329
 
454
330
  private
455
331
 
456
- SCOPES = [:each, :all, :suite]
332
+ # @private
333
+ class Hook
334
+ attr_reader :block, :options
457
335
 
458
- HOOK_TYPES = {
459
- :before => Hash.new { BeforeHook },
460
- :after => Hash.new { AfterHook },
461
- :around => Hash.new { AroundHook }
462
- }
336
+ def initialize(block, options)
337
+ @block = block
338
+ @options = options
339
+ end
463
340
 
464
- HOOK_TYPES[:after][:all] = AfterAllHook
341
+ def options_apply?(example_or_group)
342
+ example_or_group.all_apply?(options)
343
+ end
344
+ end
465
345
 
466
- def before_all_hooks_for(group)
467
- GroupHookCollection.new(hooks[:before][:all]).for(group)
346
+ # @private
347
+ class BeforeHook < Hook
348
+ def run(example)
349
+ example.instance_exec(example, &block)
350
+ end
468
351
  end
469
352
 
470
- def after_all_hooks_for(group)
471
- GroupHookCollection.new(hooks[:after][:all]).for(group)
353
+ # @private
354
+ class AfterHook < Hook
355
+ def run(example)
356
+ example.instance_exec_with_rescue("in an after hook", &block)
357
+ end
472
358
  end
473
359
 
474
- def before_each_hooks_for(example)
475
- HookCollection.new(parent_groups.reverse.map {|a| a.hooks[:before][:each]}.flatten).for(example)
360
+ # @private
361
+ class AfterContextHook < Hook
362
+ def run(example)
363
+ example.instance_exec(example, &block)
364
+ rescue Exception => e
365
+ # TODO: come up with a better solution for this.
366
+ RSpec.configuration.reporter.message <<-EOS
367
+
368
+ An error occurred in an `after(:context)` hook.
369
+ #{e.class}: #{e.message}
370
+ occurred at #{e.backtrace.first}
371
+
372
+ EOS
373
+ end
476
374
  end
477
375
 
478
- def after_each_hooks_for(example)
479
- HookCollection.new(parent_groups.map {|a| a.hooks[:after][:each]}.flatten).for(example)
376
+ # @private
377
+ AroundHook = Hook
378
+
379
+ # @private
380
+ class BaseHookCollection
381
+ Array.public_instance_methods(false).each do |name|
382
+ define_method(name) { |*a, &b| hooks.__send__(name, *a, &b) }
383
+ end
384
+
385
+ attr_reader :hooks
386
+ protected :hooks
387
+
388
+ alias append push
389
+ alias prepend unshift
390
+
391
+ def initialize(hooks=[])
392
+ @hooks = hooks
393
+ end
480
394
  end
481
395
 
482
- def register_hook prepend_or_append, hook, *args, &block
483
- scope, options = scope_and_options_from(*args)
484
- hooks[hook][scope].send(prepend_or_append, HOOK_TYPES[hook][scope].new(block, options))
396
+ # @private
397
+ class HookCollection < BaseHookCollection
398
+ def for(example_or_group)
399
+ self.class.
400
+ new(hooks.select {|hook| hook.options_apply?(example_or_group)}).
401
+ with(example_or_group)
402
+ end
403
+
404
+ def with(example)
405
+ @example = example
406
+ self
407
+ end
408
+
409
+ def run
410
+ hooks.each {|h| h.run(@example)}
411
+ end
485
412
  end
486
413
 
487
- def find_hook(hook, scope, example_or_group, initial_procsy)
488
- case [hook, scope]
489
- when [:before, :all]
490
- before_all_hooks_for(example_or_group)
491
- when [:after, :all]
492
- after_all_hooks_for(example_or_group)
493
- when [:around, :each]
494
- around_each_hooks_for(example_or_group, initial_procsy)
495
- when [:before, :each]
496
- before_each_hooks_for(example_or_group)
497
- when [:after, :each]
498
- after_each_hooks_for(example_or_group)
499
- when [:before, :suite], [:after, :suite]
500
- hooks[hook][:suite].with(example_or_group)
414
+ # @private
415
+ class AroundHookCollection < BaseHookCollection
416
+ def for(example, initial_procsy=nil)
417
+ self.class.new(hooks.select {|hook| hook.options_apply?(example)}).
418
+ with(example, initial_procsy)
419
+ end
420
+
421
+ def with(example, initial_procsy)
422
+ @example = example
423
+ @initial_procsy = initial_procsy
424
+ self
425
+ end
426
+
427
+ def run
428
+ hooks.inject(@initial_procsy) do |procsy, around_hook|
429
+ procsy.wrap do
430
+ @example.instance_exec(procsy, &around_hook.block)
431
+ end
432
+ end.call
501
433
  end
502
434
  end
503
435
 
504
- def scope_and_options_from(*args)
505
- return extract_scope_from(args), build_metadata_hash_from(args)
436
+ # @private
437
+ class GroupHookCollection < BaseHookCollection
438
+ def for(group)
439
+ @group = group
440
+ self
441
+ end
442
+
443
+ def run
444
+ hooks.shift.run(@group) until hooks.empty?
445
+ end
506
446
  end
507
447
 
508
- def extract_scope_from(args)
509
- if SCOPES.include?(args.first)
510
- args.shift
511
- elsif args.any? { |a| a.is_a?(Symbol) }
512
- raise ArgumentError.new("You must explicitly give a scope (:each, :all, or :suite) when using symbols as metadata for a hook.")
513
- else
514
- :each
448
+ # @private
449
+ class HookCollections
450
+ def initialize(owner, data)
451
+ @owner = owner
452
+ @data = data
453
+ end
454
+
455
+ def [](key)
456
+ @data[key]
457
+ end
458
+
459
+ def register_globals(host, globals)
460
+ process(host, globals, :before, :example)
461
+ process(host, globals, :after, :example)
462
+ process(host, globals, :around, :example)
463
+
464
+ process(host, globals, :before, :context)
465
+ process(host, globals, :after, :context)
466
+ end
467
+
468
+ def around_example_hooks_for(example, initial_procsy=nil)
469
+ AroundHookCollection.new(FlatMap.flat_map(@owner.parent_groups) do |a|
470
+ a.hooks[:around][:example]
471
+ end).for(example, initial_procsy)
472
+ end
473
+
474
+ def register(prepend_or_append, hook, *args, &block)
475
+ scope, options = scope_and_options_from(*args)
476
+ self[hook][scope].__send__(prepend_or_append, HOOK_TYPES[hook][scope].new(block, options))
477
+ end
478
+
479
+ # @private
480
+ #
481
+ # Runs all of the blocks stored with the hook in the context of the
482
+ # example. If no example is provided, just calls the hook directly.
483
+ def run(hook, scope, example_or_group, initial_procsy=nil)
484
+ return if RSpec.configuration.dry_run?
485
+ find_hook(hook, scope, example_or_group, initial_procsy).run
486
+ end
487
+
488
+ SCOPES = [:example, :context, :suite]
489
+
490
+ SCOPE_ALIASES = { :each => :example, :all => :context }
491
+
492
+ HOOK_TYPES = {
493
+ :before => Hash.new { BeforeHook },
494
+ :after => Hash.new { AfterHook },
495
+ :around => Hash.new { AroundHook }
496
+ }
497
+
498
+ HOOK_TYPES[:after][:context] = AfterContextHook
499
+
500
+ private
501
+
502
+ def process(host, globals, position, scope)
503
+ globals[position][scope].each do |hook|
504
+ next unless scope == :example || hook.options_apply?(host)
505
+ next if host.parent_groups.any? {|a| a.hooks[position][scope].include?(hook)}
506
+ self[position][scope] << hook
507
+ end
508
+ end
509
+
510
+ def scope_and_options_from(*args)
511
+ return extract_scope_from(args), Metadata.build_hash_from(args)
512
+ end
513
+
514
+ def extract_scope_from(args)
515
+ if known_scope?(args.first)
516
+ normalized_scope_for(args.shift)
517
+ elsif args.any? { |a| a.is_a?(Symbol) }
518
+ 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."
519
+ raise ArgumentError.new error_message
520
+ else
521
+ :example
522
+ end
523
+ end
524
+
525
+ # @api private
526
+ def known_scope?(scope)
527
+ SCOPES.include?(scope) || SCOPE_ALIASES.keys.include?(scope)
528
+ end
529
+
530
+ # @api private
531
+ def normalized_scope_for(scope)
532
+ SCOPE_ALIASES[scope] || scope
533
+ end
534
+
535
+ def find_hook(hook, scope, example_or_group, initial_procsy)
536
+ case [hook, scope]
537
+ when [:before, :context]
538
+ before_context_hooks_for(example_or_group)
539
+ when [:after, :context]
540
+ after_context_hooks_for(example_or_group)
541
+ when [:around, :example]
542
+ around_example_hooks_for(example_or_group, initial_procsy)
543
+ when [:before, :example]
544
+ before_example_hooks_for(example_or_group)
545
+ when [:after, :example]
546
+ after_example_hooks_for(example_or_group)
547
+ when [:before, :suite], [:after, :suite]
548
+ self[hook][:suite].with(example_or_group)
549
+ end
550
+ end
551
+
552
+ def before_context_hooks_for(group)
553
+ GroupHookCollection.new(self[:before][:context]).for(group)
554
+ end
555
+
556
+ def after_context_hooks_for(group)
557
+ GroupHookCollection.new(self[:after][:context]).for(group)
558
+ end
559
+
560
+ def before_example_hooks_for(example)
561
+ HookCollection.new(FlatMap.flat_map(@owner.parent_groups.reverse) do |a|
562
+ a.hooks[:before][:example]
563
+ end).for(example)
564
+ end
565
+
566
+ def after_example_hooks_for(example)
567
+ HookCollection.new(FlatMap.flat_map(@owner.parent_groups) do |a|
568
+ a.hooks[:after][:example]
569
+ end).for(example)
515
570
  end
516
571
  end
517
572
  end