rspec-core 3.8.2
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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.document +5 -0
- data/.yardopts +8 -0
- data/Changelog.md +2243 -0
- data/LICENSE.md +26 -0
- data/README.md +384 -0
- data/exe/rspec +4 -0
- data/lib/rspec/autorun.rb +3 -0
- data/lib/rspec/core.rb +185 -0
- data/lib/rspec/core/backtrace_formatter.rb +65 -0
- data/lib/rspec/core/bisect/coordinator.rb +62 -0
- data/lib/rspec/core/bisect/example_minimizer.rb +173 -0
- data/lib/rspec/core/bisect/fork_runner.rb +134 -0
- data/lib/rspec/core/bisect/server.rb +61 -0
- data/lib/rspec/core/bisect/shell_command.rb +126 -0
- data/lib/rspec/core/bisect/shell_runner.rb +73 -0
- data/lib/rspec/core/bisect/utilities.rb +58 -0
- data/lib/rspec/core/configuration.rb +2308 -0
- data/lib/rspec/core/configuration_options.rb +233 -0
- data/lib/rspec/core/drb.rb +113 -0
- data/lib/rspec/core/dsl.rb +98 -0
- data/lib/rspec/core/example.rb +656 -0
- data/lib/rspec/core/example_group.rb +889 -0
- data/lib/rspec/core/example_status_persister.rb +235 -0
- data/lib/rspec/core/filter_manager.rb +231 -0
- data/lib/rspec/core/flat_map.rb +20 -0
- data/lib/rspec/core/formatters.rb +269 -0
- data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
- data/lib/rspec/core/formatters/base_formatter.rb +70 -0
- data/lib/rspec/core/formatters/base_text_formatter.rb +75 -0
- data/lib/rspec/core/formatters/bisect_drb_formatter.rb +29 -0
- data/lib/rspec/core/formatters/bisect_progress_formatter.rb +157 -0
- data/lib/rspec/core/formatters/console_codes.rb +68 -0
- data/lib/rspec/core/formatters/deprecation_formatter.rb +223 -0
- data/lib/rspec/core/formatters/documentation_formatter.rb +70 -0
- data/lib/rspec/core/formatters/exception_presenter.rb +508 -0
- data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
- data/lib/rspec/core/formatters/helpers.rb +110 -0
- data/lib/rspec/core/formatters/html_formatter.rb +153 -0
- data/lib/rspec/core/formatters/html_printer.rb +414 -0
- data/lib/rspec/core/formatters/html_snippet_extractor.rb +120 -0
- data/lib/rspec/core/formatters/json_formatter.rb +102 -0
- data/lib/rspec/core/formatters/profile_formatter.rb +68 -0
- data/lib/rspec/core/formatters/progress_formatter.rb +29 -0
- data/lib/rspec/core/formatters/protocol.rb +182 -0
- data/lib/rspec/core/formatters/snippet_extractor.rb +134 -0
- data/lib/rspec/core/formatters/syntax_highlighter.rb +91 -0
- data/lib/rspec/core/hooks.rb +624 -0
- data/lib/rspec/core/invocations.rb +87 -0
- data/lib/rspec/core/memoized_helpers.rb +554 -0
- data/lib/rspec/core/metadata.rb +498 -0
- data/lib/rspec/core/metadata_filter.rb +255 -0
- data/lib/rspec/core/minitest_assertions_adapter.rb +31 -0
- data/lib/rspec/core/mocking_adapters/flexmock.rb +31 -0
- data/lib/rspec/core/mocking_adapters/mocha.rb +57 -0
- data/lib/rspec/core/mocking_adapters/null.rb +14 -0
- data/lib/rspec/core/mocking_adapters/rr.rb +31 -0
- data/lib/rspec/core/mocking_adapters/rspec.rb +32 -0
- data/lib/rspec/core/notifications.rb +521 -0
- data/lib/rspec/core/option_parser.rb +309 -0
- data/lib/rspec/core/ordering.rb +158 -0
- data/lib/rspec/core/output_wrapper.rb +29 -0
- data/lib/rspec/core/pending.rb +165 -0
- data/lib/rspec/core/profiler.rb +34 -0
- data/lib/rspec/core/project_initializer.rb +48 -0
- data/lib/rspec/core/project_initializer/.rspec +1 -0
- data/lib/rspec/core/project_initializer/spec/spec_helper.rb +100 -0
- data/lib/rspec/core/rake_task.rb +168 -0
- data/lib/rspec/core/reporter.rb +257 -0
- data/lib/rspec/core/ruby_project.rb +53 -0
- data/lib/rspec/core/runner.rb +199 -0
- data/lib/rspec/core/sandbox.rb +37 -0
- data/lib/rspec/core/set.rb +54 -0
- data/lib/rspec/core/shared_context.rb +55 -0
- data/lib/rspec/core/shared_example_group.rb +269 -0
- data/lib/rspec/core/shell_escape.rb +49 -0
- data/lib/rspec/core/test_unit_assertions_adapter.rb +30 -0
- data/lib/rspec/core/version.rb +9 -0
- data/lib/rspec/core/warnings.rb +40 -0
- data/lib/rspec/core/world.rb +275 -0
- metadata +292 -0
- metadata.gz.sig +0 -0
@@ -0,0 +1,498 @@
|
|
1
|
+
module RSpec
|
2
|
+
module Core
|
3
|
+
# Each ExampleGroup class and Example instance owns an instance of
|
4
|
+
# Metadata, which is Hash extended to support lazy evaluation of values
|
5
|
+
# associated with keys that may or may not be used by any example or group.
|
6
|
+
#
|
7
|
+
# In addition to metadata that is used internally, this also stores
|
8
|
+
# user-supplied metadata, e.g.
|
9
|
+
#
|
10
|
+
# describe Something, :type => :ui do
|
11
|
+
# it "does something", :slow => true do
|
12
|
+
# # ...
|
13
|
+
# end
|
14
|
+
# end
|
15
|
+
#
|
16
|
+
# `:type => :ui` is stored in the Metadata owned by the example group, and
|
17
|
+
# `:slow => true` is stored in the Metadata owned by the example. These can
|
18
|
+
# then be used to select which examples are run using the `--tag` option on
|
19
|
+
# the command line, or several methods on `Configuration` used to filter a
|
20
|
+
# run (e.g. `filter_run_including`, `filter_run_excluding`, etc).
|
21
|
+
#
|
22
|
+
# @see Example#metadata
|
23
|
+
# @see ExampleGroup.metadata
|
24
|
+
# @see FilterManager
|
25
|
+
# @see Configuration#filter_run_including
|
26
|
+
# @see Configuration#filter_run_excluding
|
27
|
+
module Metadata
|
28
|
+
# Matches strings either at the beginning of the input or prefixed with a
|
29
|
+
# whitespace, containing the current path, either postfixed with the
|
30
|
+
# separator, or at the end of the string. Match groups are the character
|
31
|
+
# before and the character after the string if any.
|
32
|
+
#
|
33
|
+
# http://rubular.com/r/fT0gmX6VJX
|
34
|
+
# http://rubular.com/r/duOrD4i3wb
|
35
|
+
# http://rubular.com/r/sbAMHFrOx1
|
36
|
+
def self.relative_path_regex
|
37
|
+
@relative_path_regex ||= /(\A|\s)#{File.expand_path('.')}(#{File::SEPARATOR}|\s|\Z)/
|
38
|
+
end
|
39
|
+
|
40
|
+
# @api private
|
41
|
+
#
|
42
|
+
# @param line [String] current code line
|
43
|
+
# @return [String] relative path to line
|
44
|
+
def self.relative_path(line)
|
45
|
+
line = line.sub(relative_path_regex, "\\1.\\2".freeze)
|
46
|
+
line = line.sub(/\A([^:]+:\d+)$/, '\\1'.freeze)
|
47
|
+
return nil if line == '-e:1'.freeze
|
48
|
+
line
|
49
|
+
rescue SecurityError
|
50
|
+
# :nocov:
|
51
|
+
nil
|
52
|
+
# :nocov:
|
53
|
+
end
|
54
|
+
|
55
|
+
# @private
|
56
|
+
# Iteratively walks up from the given metadata through all
|
57
|
+
# example group ancestors, yielding each metadata hash along the way.
|
58
|
+
def self.ascending(metadata)
|
59
|
+
yield metadata
|
60
|
+
return unless (group_metadata = metadata.fetch(:example_group) { metadata[:parent_example_group] })
|
61
|
+
|
62
|
+
loop do
|
63
|
+
yield group_metadata
|
64
|
+
break unless (group_metadata = group_metadata[:parent_example_group])
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# @private
|
69
|
+
# Returns an enumerator that iteratively walks up the given metadata through all
|
70
|
+
# example group ancestors, yielding each metadata hash along the way.
|
71
|
+
def self.ascend(metadata)
|
72
|
+
enum_for(:ascending, metadata)
|
73
|
+
end
|
74
|
+
|
75
|
+
# @private
|
76
|
+
# Used internally to build a hash from an args array.
|
77
|
+
# Symbols are converted into hash keys with a value of `true`.
|
78
|
+
# This is done to support simple tagging using a symbol, rather
|
79
|
+
# than needing to do `:symbol => true`.
|
80
|
+
def self.build_hash_from(args, warn_about_example_group_filtering=false)
|
81
|
+
hash = args.last.is_a?(Hash) ? args.pop : {}
|
82
|
+
|
83
|
+
hash[args.pop] = true while args.last.is_a?(Symbol)
|
84
|
+
|
85
|
+
if warn_about_example_group_filtering && hash.key?(:example_group)
|
86
|
+
RSpec.deprecate("Filtering by an `:example_group` subhash",
|
87
|
+
:replacement => "the subhash to filter directly")
|
88
|
+
end
|
89
|
+
|
90
|
+
hash
|
91
|
+
end
|
92
|
+
|
93
|
+
# @private
|
94
|
+
def self.deep_hash_dup(object)
|
95
|
+
return object.dup if Array === object
|
96
|
+
return object unless Hash === object
|
97
|
+
|
98
|
+
object.inject(object.dup) do |duplicate, (key, value)|
|
99
|
+
duplicate[key] = deep_hash_dup(value)
|
100
|
+
duplicate
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# @private
|
105
|
+
def self.id_from(metadata)
|
106
|
+
"#{metadata[:rerun_file_path]}[#{metadata[:scoped_id]}]"
|
107
|
+
end
|
108
|
+
|
109
|
+
# @private
|
110
|
+
def self.location_tuple_from(metadata)
|
111
|
+
[metadata[:absolute_file_path], metadata[:line_number]]
|
112
|
+
end
|
113
|
+
|
114
|
+
# @private
|
115
|
+
# Used internally to populate metadata hashes with computed keys
|
116
|
+
# managed by RSpec.
|
117
|
+
class HashPopulator
|
118
|
+
attr_reader :metadata, :user_metadata, :description_args, :block
|
119
|
+
|
120
|
+
def initialize(metadata, user_metadata, index_provider, description_args, block)
|
121
|
+
@metadata = metadata
|
122
|
+
@user_metadata = user_metadata
|
123
|
+
@index_provider = index_provider
|
124
|
+
@description_args = description_args
|
125
|
+
@block = block
|
126
|
+
end
|
127
|
+
|
128
|
+
def populate
|
129
|
+
ensure_valid_user_keys
|
130
|
+
|
131
|
+
metadata[:block] = block
|
132
|
+
metadata[:description_args] = description_args
|
133
|
+
metadata[:description] = build_description_from(*metadata[:description_args])
|
134
|
+
metadata[:full_description] = full_description
|
135
|
+
metadata[:described_class] = described_class
|
136
|
+
|
137
|
+
populate_location_attributes
|
138
|
+
metadata.update(user_metadata)
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
|
143
|
+
def populate_location_attributes
|
144
|
+
backtrace = user_metadata.delete(:caller)
|
145
|
+
|
146
|
+
file_path, line_number = if backtrace
|
147
|
+
file_path_and_line_number_from(backtrace)
|
148
|
+
elsif block.respond_to?(:source_location)
|
149
|
+
block.source_location
|
150
|
+
else
|
151
|
+
file_path_and_line_number_from(caller)
|
152
|
+
end
|
153
|
+
|
154
|
+
relative_file_path = Metadata.relative_path(file_path)
|
155
|
+
absolute_file_path = File.expand_path(relative_file_path)
|
156
|
+
metadata[:file_path] = relative_file_path
|
157
|
+
metadata[:line_number] = line_number.to_i
|
158
|
+
metadata[:location] = "#{relative_file_path}:#{line_number}"
|
159
|
+
metadata[:absolute_file_path] = absolute_file_path
|
160
|
+
metadata[:rerun_file_path] ||= relative_file_path
|
161
|
+
metadata[:scoped_id] = build_scoped_id_for(absolute_file_path)
|
162
|
+
end
|
163
|
+
|
164
|
+
def file_path_and_line_number_from(backtrace)
|
165
|
+
first_caller_from_outside_rspec = backtrace.find { |l| l !~ CallerFilter::LIB_REGEX }
|
166
|
+
first_caller_from_outside_rspec ||= backtrace.first
|
167
|
+
/(.+?):(\d+)(?:|:\d+)/.match(first_caller_from_outside_rspec).captures
|
168
|
+
end
|
169
|
+
|
170
|
+
def description_separator(parent_part, child_part)
|
171
|
+
if parent_part.is_a?(Module) && /^(?:#|::|\.)/.match(child_part.to_s)
|
172
|
+
''.freeze
|
173
|
+
else
|
174
|
+
' '.freeze
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def build_description_from(parent_description=nil, my_description=nil)
|
179
|
+
return parent_description.to_s unless my_description
|
180
|
+
return my_description.to_s if parent_description.to_s == ''
|
181
|
+
separator = description_separator(parent_description, my_description)
|
182
|
+
(parent_description.to_s + separator) << my_description.to_s
|
183
|
+
end
|
184
|
+
|
185
|
+
def build_scoped_id_for(file_path)
|
186
|
+
index = @index_provider.call(file_path).to_s
|
187
|
+
parent_scoped_id = metadata.fetch(:scoped_id) { return index }
|
188
|
+
"#{parent_scoped_id}:#{index}"
|
189
|
+
end
|
190
|
+
|
191
|
+
def ensure_valid_user_keys
|
192
|
+
RESERVED_KEYS.each do |key|
|
193
|
+
next unless user_metadata.key?(key)
|
194
|
+
raise <<-EOM.gsub(/^\s+\|/, '')
|
195
|
+
|#{"*" * 50}
|
196
|
+
|:#{key} is not allowed
|
197
|
+
|
|
198
|
+
|RSpec reserves some hash keys for its own internal use,
|
199
|
+
|including :#{key}, which is used on:
|
200
|
+
|
|
201
|
+
| #{CallerFilter.first_non_rspec_line}.
|
202
|
+
|
|
203
|
+
|Here are all of RSpec's reserved hash keys:
|
204
|
+
|
|
205
|
+
| #{RESERVED_KEYS.join("\n ")}
|
206
|
+
|#{"*" * 50}
|
207
|
+
EOM
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
# @private
|
213
|
+
class ExampleHash < HashPopulator
|
214
|
+
def self.create(group_metadata, user_metadata, index_provider, description, block)
|
215
|
+
example_metadata = group_metadata.dup
|
216
|
+
group_metadata = Hash.new(&ExampleGroupHash.backwards_compatibility_default_proc do |hash|
|
217
|
+
hash[:parent_example_group]
|
218
|
+
end)
|
219
|
+
group_metadata.update(example_metadata)
|
220
|
+
|
221
|
+
example_metadata[:execution_result] = Example::ExecutionResult.new
|
222
|
+
example_metadata[:example_group] = group_metadata
|
223
|
+
example_metadata[:shared_group_inclusion_backtrace] = SharedExampleGroupInclusionStackFrame.current_backtrace
|
224
|
+
example_metadata.delete(:parent_example_group)
|
225
|
+
|
226
|
+
description_args = description.nil? ? [] : [description]
|
227
|
+
hash = new(example_metadata, user_metadata, index_provider, description_args, block)
|
228
|
+
hash.populate
|
229
|
+
hash.metadata
|
230
|
+
end
|
231
|
+
|
232
|
+
private
|
233
|
+
|
234
|
+
def described_class
|
235
|
+
metadata[:example_group][:described_class]
|
236
|
+
end
|
237
|
+
|
238
|
+
def full_description
|
239
|
+
build_description_from(
|
240
|
+
metadata[:example_group][:full_description],
|
241
|
+
metadata[:description]
|
242
|
+
)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
# @private
|
247
|
+
class ExampleGroupHash < HashPopulator
|
248
|
+
def self.create(parent_group_metadata, user_metadata, example_group_index, *args, &block)
|
249
|
+
group_metadata = hash_with_backwards_compatibility_default_proc
|
250
|
+
|
251
|
+
if parent_group_metadata
|
252
|
+
group_metadata.update(parent_group_metadata)
|
253
|
+
group_metadata[:parent_example_group] = parent_group_metadata
|
254
|
+
end
|
255
|
+
|
256
|
+
hash = new(group_metadata, user_metadata, example_group_index, args, block)
|
257
|
+
hash.populate
|
258
|
+
hash.metadata
|
259
|
+
end
|
260
|
+
|
261
|
+
def self.hash_with_backwards_compatibility_default_proc
|
262
|
+
Hash.new(&backwards_compatibility_default_proc { |hash| hash })
|
263
|
+
end
|
264
|
+
|
265
|
+
def self.backwards_compatibility_default_proc(&example_group_selector)
|
266
|
+
Proc.new do |hash, key|
|
267
|
+
case key
|
268
|
+
when :example_group
|
269
|
+
# We commonly get here when rspec-core is applying a previously
|
270
|
+
# configured filter rule, such as when a gem configures:
|
271
|
+
#
|
272
|
+
# RSpec.configure do |c|
|
273
|
+
# c.include MyGemHelpers, :example_group => { :file_path => /spec\/my_gem_specs/ }
|
274
|
+
# end
|
275
|
+
#
|
276
|
+
# It's confusing for a user to get a deprecation at this point in
|
277
|
+
# the code, so instead we issue a deprecation from the config APIs
|
278
|
+
# that take a metadata hash, and MetadataFilter sets this thread
|
279
|
+
# local to silence the warning here since it would be so
|
280
|
+
# confusing.
|
281
|
+
unless RSpec::Support.thread_local_data[:silence_metadata_example_group_deprecations]
|
282
|
+
RSpec.deprecate("The `:example_group` key in an example group's metadata hash",
|
283
|
+
:replacement => "the example group's hash directly for the " \
|
284
|
+
"computed keys and `:parent_example_group` to access the parent " \
|
285
|
+
"example group metadata")
|
286
|
+
end
|
287
|
+
|
288
|
+
group_hash = example_group_selector.call(hash)
|
289
|
+
LegacyExampleGroupHash.new(group_hash) if group_hash
|
290
|
+
when :example_group_block
|
291
|
+
RSpec.deprecate("`metadata[:example_group_block]`",
|
292
|
+
:replacement => "`metadata[:block]`")
|
293
|
+
hash[:block]
|
294
|
+
when :describes
|
295
|
+
RSpec.deprecate("`metadata[:describes]`",
|
296
|
+
:replacement => "`metadata[:described_class]`")
|
297
|
+
hash[:described_class]
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
302
|
+
private
|
303
|
+
|
304
|
+
def described_class
|
305
|
+
candidate = metadata[:description_args].first
|
306
|
+
return candidate unless NilClass === candidate || String === candidate
|
307
|
+
parent_group = metadata[:parent_example_group]
|
308
|
+
parent_group && parent_group[:described_class]
|
309
|
+
end
|
310
|
+
|
311
|
+
def full_description
|
312
|
+
description = metadata[:description]
|
313
|
+
parent_example_group = metadata[:parent_example_group]
|
314
|
+
return description unless parent_example_group
|
315
|
+
|
316
|
+
parent_description = parent_example_group[:full_description]
|
317
|
+
separator = description_separator(parent_example_group[:description_args].last,
|
318
|
+
metadata[:description_args].first)
|
319
|
+
|
320
|
+
parent_description + separator + description
|
321
|
+
end
|
322
|
+
end
|
323
|
+
|
324
|
+
# @private
|
325
|
+
RESERVED_KEYS = [
|
326
|
+
:description,
|
327
|
+
:description_args,
|
328
|
+
:described_class,
|
329
|
+
:example_group,
|
330
|
+
:parent_example_group,
|
331
|
+
:execution_result,
|
332
|
+
:last_run_status,
|
333
|
+
:file_path,
|
334
|
+
:absolute_file_path,
|
335
|
+
:rerun_file_path,
|
336
|
+
:full_description,
|
337
|
+
:line_number,
|
338
|
+
:location,
|
339
|
+
:scoped_id,
|
340
|
+
:block,
|
341
|
+
:shared_group_inclusion_backtrace
|
342
|
+
]
|
343
|
+
end
|
344
|
+
|
345
|
+
# Mixin that makes the including class imitate a hash for backwards
|
346
|
+
# compatibility. The including class should use `attr_accessor` to
|
347
|
+
# declare attributes.
|
348
|
+
# @private
|
349
|
+
module HashImitatable
|
350
|
+
def self.included(klass)
|
351
|
+
klass.extend ClassMethods
|
352
|
+
end
|
353
|
+
|
354
|
+
def to_h
|
355
|
+
hash = extra_hash_attributes.dup
|
356
|
+
|
357
|
+
self.class.hash_attribute_names.each do |name|
|
358
|
+
hash[name] = __send__(name)
|
359
|
+
end
|
360
|
+
|
361
|
+
hash
|
362
|
+
end
|
363
|
+
|
364
|
+
(Hash.public_instance_methods - Object.public_instance_methods).each do |method_name|
|
365
|
+
next if [:[], :[]=, :to_h].include?(method_name.to_sym)
|
366
|
+
|
367
|
+
define_method(method_name) do |*args, &block|
|
368
|
+
issue_deprecation(method_name, *args)
|
369
|
+
|
370
|
+
hash = hash_for_delegation
|
371
|
+
self.class.hash_attribute_names.each do |name|
|
372
|
+
hash.delete(name) unless instance_variable_defined?(:"@#{name}")
|
373
|
+
end
|
374
|
+
|
375
|
+
hash.__send__(method_name, *args, &block).tap do
|
376
|
+
# apply mutations back to the object
|
377
|
+
hash.each do |name, value|
|
378
|
+
if directly_supports_attribute?(name)
|
379
|
+
set_value(name, value)
|
380
|
+
else
|
381
|
+
extra_hash_attributes[name] = value
|
382
|
+
end
|
383
|
+
end
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
def [](key)
|
389
|
+
issue_deprecation(:[], key)
|
390
|
+
|
391
|
+
if directly_supports_attribute?(key)
|
392
|
+
get_value(key)
|
393
|
+
else
|
394
|
+
extra_hash_attributes[key]
|
395
|
+
end
|
396
|
+
end
|
397
|
+
|
398
|
+
def []=(key, value)
|
399
|
+
issue_deprecation(:[]=, key, value)
|
400
|
+
|
401
|
+
if directly_supports_attribute?(key)
|
402
|
+
set_value(key, value)
|
403
|
+
else
|
404
|
+
extra_hash_attributes[key] = value
|
405
|
+
end
|
406
|
+
end
|
407
|
+
|
408
|
+
private
|
409
|
+
|
410
|
+
def extra_hash_attributes
|
411
|
+
@extra_hash_attributes ||= {}
|
412
|
+
end
|
413
|
+
|
414
|
+
def directly_supports_attribute?(name)
|
415
|
+
self.class.hash_attribute_names.include?(name)
|
416
|
+
end
|
417
|
+
|
418
|
+
def get_value(name)
|
419
|
+
__send__(name)
|
420
|
+
end
|
421
|
+
|
422
|
+
def set_value(name, value)
|
423
|
+
__send__(:"#{name}=", value)
|
424
|
+
end
|
425
|
+
|
426
|
+
def hash_for_delegation
|
427
|
+
to_h
|
428
|
+
end
|
429
|
+
|
430
|
+
def issue_deprecation(_method_name, *_args)
|
431
|
+
# no-op by default: subclasses can override
|
432
|
+
end
|
433
|
+
|
434
|
+
# @private
|
435
|
+
module ClassMethods
|
436
|
+
def hash_attribute_names
|
437
|
+
@hash_attribute_names ||= []
|
438
|
+
end
|
439
|
+
|
440
|
+
def attr_accessor(*names)
|
441
|
+
hash_attribute_names.concat(names)
|
442
|
+
super
|
443
|
+
end
|
444
|
+
end
|
445
|
+
end
|
446
|
+
|
447
|
+
# @private
|
448
|
+
# Together with the example group metadata hash default block,
|
449
|
+
# provides backwards compatibility for the old `:example_group`
|
450
|
+
# key. In RSpec 2.x, the computed keys of a group's metadata
|
451
|
+
# were exposed from a nested subhash keyed by `[:example_group]`, and
|
452
|
+
# then the parent group's metadata was exposed by sub-subhash
|
453
|
+
# keyed by `[:example_group][:example_group]`.
|
454
|
+
#
|
455
|
+
# In RSpec 3, we reorganized this to that the computed keys are
|
456
|
+
# exposed directly of the group metadata hash (no nesting), and
|
457
|
+
# `:parent_example_group` returns the parent group's metadata.
|
458
|
+
#
|
459
|
+
# Maintaining backwards compatibility was difficult: we wanted
|
460
|
+
# `:example_group` to return an object that:
|
461
|
+
#
|
462
|
+
# * Exposes the top-level metadata keys that used to be nested
|
463
|
+
# under `:example_group`.
|
464
|
+
# * Supports mutation (rspec-rails, for example, assigns
|
465
|
+
# `metadata[:example_group][:described_class]` when you use
|
466
|
+
# anonymous controller specs) such that changes are written
|
467
|
+
# back to the top-level metadata hash.
|
468
|
+
# * Exposes the parent group metadata as
|
469
|
+
# `[:example_group][:example_group]`.
|
470
|
+
class LegacyExampleGroupHash
|
471
|
+
include HashImitatable
|
472
|
+
|
473
|
+
def initialize(metadata)
|
474
|
+
@metadata = metadata
|
475
|
+
parent_group_metadata = metadata.fetch(:parent_example_group) { {} }[:example_group]
|
476
|
+
self[:example_group] = parent_group_metadata if parent_group_metadata
|
477
|
+
end
|
478
|
+
|
479
|
+
def to_h
|
480
|
+
super.merge(@metadata)
|
481
|
+
end
|
482
|
+
|
483
|
+
private
|
484
|
+
|
485
|
+
def directly_supports_attribute?(name)
|
486
|
+
name != :example_group
|
487
|
+
end
|
488
|
+
|
489
|
+
def get_value(name)
|
490
|
+
@metadata[name]
|
491
|
+
end
|
492
|
+
|
493
|
+
def set_value(name, value)
|
494
|
+
@metadata[name] = value
|
495
|
+
end
|
496
|
+
end
|
497
|
+
end
|
498
|
+
end
|