rspec-core 3.0.4 → 3.12.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 +5 -5
- checksums.yaml.gz.sig +0 -0
- data/.document +1 -1
- data/.yardopts +2 -1
- data/Changelog.md +888 -2
- data/{License.txt → LICENSE.md} +6 -5
- data/README.md +165 -24
- data/lib/rspec/autorun.rb +1 -0
- data/lib/rspec/core/backtrace_formatter.rb +19 -20
- 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 +138 -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 +69 -0
- data/lib/rspec/core/configuration.rb +1287 -246
- data/lib/rspec/core/configuration_options.rb +95 -35
- data/lib/rspec/core/did_you_mean.rb +46 -0
- data/lib/rspec/core/drb.rb +21 -12
- data/lib/rspec/core/dsl.rb +10 -6
- data/lib/rspec/core/example.rb +305 -113
- data/lib/rspec/core/example_group.rb +431 -223
- data/lib/rspec/core/example_status_persister.rb +235 -0
- data/lib/rspec/core/filter_manager.rb +86 -115
- data/lib/rspec/core/flat_map.rb +6 -4
- data/lib/rspec/core/formatters/base_bisect_formatter.rb +45 -0
- data/lib/rspec/core/formatters/base_formatter.rb +14 -116
- data/lib/rspec/core/formatters/base_text_formatter.rb +18 -21
- 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 +29 -18
- data/lib/rspec/core/formatters/deprecation_formatter.rb +16 -16
- data/lib/rspec/core/formatters/documentation_formatter.rb +49 -16
- data/lib/rspec/core/formatters/exception_presenter.rb +525 -0
- data/lib/rspec/core/formatters/failure_list_formatter.rb +23 -0
- data/lib/rspec/core/formatters/fallback_message_formatter.rb +28 -0
- data/lib/rspec/core/formatters/helpers.rb +45 -15
- data/lib/rspec/core/formatters/html_formatter.rb +33 -28
- data/lib/rspec/core/formatters/html_printer.rb +30 -20
- data/lib/rspec/core/formatters/html_snippet_extractor.rb +120 -0
- data/lib/rspec/core/formatters/json_formatter.rb +18 -9
- data/lib/rspec/core/formatters/profile_formatter.rb +10 -9
- data/lib/rspec/core/formatters/progress_formatter.rb +5 -4
- data/lib/rspec/core/formatters/protocol.rb +182 -0
- data/lib/rspec/core/formatters/snippet_extractor.rb +113 -82
- data/lib/rspec/core/formatters/syntax_highlighter.rb +91 -0
- data/lib/rspec/core/formatters.rb +81 -41
- data/lib/rspec/core/hooks.rb +314 -244
- data/lib/rspec/core/invocations.rb +87 -0
- data/lib/rspec/core/memoized_helpers.rb +161 -51
- data/lib/rspec/core/metadata.rb +132 -61
- data/lib/rspec/core/metadata_filter.rb +224 -64
- data/lib/rspec/core/minitest_assertions_adapter.rb +6 -3
- data/lib/rspec/core/mocking_adapters/flexmock.rb +4 -2
- data/lib/rspec/core/mocking_adapters/mocha.rb +11 -9
- data/lib/rspec/core/mocking_adapters/null.rb +2 -0
- data/lib/rspec/core/mocking_adapters/rr.rb +3 -1
- data/lib/rspec/core/mocking_adapters/rspec.rb +3 -1
- data/lib/rspec/core/notifications.rb +192 -206
- data/lib/rspec/core/option_parser.rb +174 -69
- data/lib/rspec/core/ordering.rb +48 -35
- data/lib/rspec/core/output_wrapper.rb +29 -0
- data/lib/rspec/core/pending.rb +25 -33
- data/lib/rspec/core/profiler.rb +34 -0
- data/lib/rspec/core/project_initializer/.rspec +0 -2
- data/lib/rspec/core/project_initializer/spec/spec_helper.rb +59 -39
- data/lib/rspec/core/project_initializer.rb +5 -3
- data/lib/rspec/core/rake_task.rb +99 -55
- data/lib/rspec/core/reporter.rb +128 -15
- data/lib/rspec/core/ruby_project.rb +14 -6
- data/lib/rspec/core/runner.rb +96 -45
- data/lib/rspec/core/sandbox.rb +37 -0
- data/lib/rspec/core/set.rb +54 -0
- data/lib/rspec/core/shared_example_group.rb +133 -43
- data/lib/rspec/core/shell_escape.rb +49 -0
- data/lib/rspec/core/test_unit_assertions_adapter.rb +4 -4
- data/lib/rspec/core/version.rb +1 -1
- data/lib/rspec/core/warnings.rb +6 -6
- data/lib/rspec/core/world.rb +172 -68
- data/lib/rspec/core.rb +66 -21
- data.tar.gz.sig +0 -0
- metadata +93 -69
- metadata.gz.sig +0 -0
- data/lib/rspec/core/backport_random.rb +0 -336
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
module RSpec
|
|
2
|
+
module Core
|
|
3
|
+
# @private
|
|
4
|
+
module Invocations
|
|
5
|
+
# @private
|
|
6
|
+
class InitializeProject
|
|
7
|
+
def call(*_args)
|
|
8
|
+
RSpec::Support.require_rspec_core "project_initializer"
|
|
9
|
+
ProjectInitializer.new.run
|
|
10
|
+
0
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @private
|
|
15
|
+
class DRbWithFallback
|
|
16
|
+
def call(options, err, out)
|
|
17
|
+
require 'rspec/core/drb'
|
|
18
|
+
begin
|
|
19
|
+
return DRbRunner.new(options).run(err, out)
|
|
20
|
+
rescue DRb::DRbConnError
|
|
21
|
+
err.puts "No DRb server is running. Running in local process instead ..."
|
|
22
|
+
end
|
|
23
|
+
RSpec::Core::Runner.new(options).run(err, out)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @private
|
|
28
|
+
class Bisect
|
|
29
|
+
def call(options, err, out)
|
|
30
|
+
RSpec::Support.require_rspec_core "bisect/coordinator"
|
|
31
|
+
runner = Runner.new(options).tap { |r| r.configure(err, out) }
|
|
32
|
+
formatter = bisect_formatter_klass_for(options.options[:bisect]).new(
|
|
33
|
+
out, runner.configuration.bisect_runner
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
success = RSpec::Core::Bisect::Coordinator.bisect_with(
|
|
37
|
+
runner, options.args, formatter
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
runner.exit_code(success)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
private
|
|
44
|
+
|
|
45
|
+
def bisect_formatter_klass_for(argument)
|
|
46
|
+
return Formatters::BisectDebugFormatter if argument == "verbose"
|
|
47
|
+
Formatters::BisectProgressFormatter
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# @private
|
|
52
|
+
class PrintVersion
|
|
53
|
+
def call(_options, _err, out)
|
|
54
|
+
overall_version = RSpec::Core::Version::STRING
|
|
55
|
+
unless overall_version =~ /[a-zA-Z]+/
|
|
56
|
+
overall_version = overall_version.split('.').first(2).join('.')
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
out.puts "RSpec #{overall_version}"
|
|
60
|
+
|
|
61
|
+
[:Core, :Expectations, :Mocks, :Rails, :Support].each do |const_name|
|
|
62
|
+
lib_name = const_name.to_s.downcase
|
|
63
|
+
begin
|
|
64
|
+
require "rspec/#{lib_name}/version"
|
|
65
|
+
rescue LoadError
|
|
66
|
+
# Not worth mentioning libs that are not installed
|
|
67
|
+
nil
|
|
68
|
+
else
|
|
69
|
+
out.puts " - rspec-#{lib_name} #{RSpec.const_get(const_name)::Version::STRING}"
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
0
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @private
|
|
78
|
+
PrintHelp = Struct.new(:parser, :hidden_options) do
|
|
79
|
+
def call(_options, _err, out)
|
|
80
|
+
# Removing the hidden options from the output.
|
|
81
|
+
out.puts parser.to_s.gsub(/^\s+(#{hidden_options.join('|')})\b.*$\n/, '')
|
|
82
|
+
0
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
RSpec::Support.require_rspec_support 'reentrant_mutex'
|
|
2
|
+
|
|
1
3
|
module RSpec
|
|
2
4
|
module Core
|
|
3
5
|
# This module is included in {ExampleGroup}, making the methods
|
|
@@ -8,7 +10,7 @@ module RSpec
|
|
|
8
10
|
# @note `subject` was contributed by Joe Ferris to support the one-liner
|
|
9
11
|
# syntax embraced by shoulda matchers:
|
|
10
12
|
#
|
|
11
|
-
# describe Widget do
|
|
13
|
+
# RSpec.describe Widget do
|
|
12
14
|
# it { is_expected.to validate_presence_of(:name) }
|
|
13
15
|
# # or
|
|
14
16
|
# it { should validate_presence_of(:name) }
|
|
@@ -20,8 +22,8 @@ module RSpec
|
|
|
20
22
|
#
|
|
21
23
|
# @example
|
|
22
24
|
#
|
|
23
|
-
# #
|
|
24
|
-
# describe Person do
|
|
25
|
+
# # Explicit declaration of subject.
|
|
26
|
+
# RSpec.describe Person do
|
|
25
27
|
# subject { Person.new(:birthdate => 19.years.ago) }
|
|
26
28
|
# it "should be eligible to vote" do
|
|
27
29
|
# subject.should be_eligible_to_vote
|
|
@@ -29,35 +31,33 @@ module RSpec
|
|
|
29
31
|
# end
|
|
30
32
|
# end
|
|
31
33
|
#
|
|
32
|
-
# #
|
|
33
|
-
# describe Person do
|
|
34
|
+
# # Implicit subject => { Person.new }.
|
|
35
|
+
# RSpec.describe Person do
|
|
34
36
|
# it "should be eligible to vote" do
|
|
35
37
|
# subject.should be_eligible_to_vote
|
|
36
38
|
# # ^ ^ explicit reference to subject not recommended
|
|
37
39
|
# end
|
|
38
40
|
# end
|
|
39
41
|
#
|
|
40
|
-
# #
|
|
41
|
-
# describe Person do
|
|
42
|
+
# # One-liner syntax - expectation is set on the subject.
|
|
43
|
+
# RSpec.describe Person do
|
|
42
44
|
# it { is_expected.to be_eligible_to_vote }
|
|
43
45
|
# # or
|
|
44
46
|
# it { should be_eligible_to_vote }
|
|
45
47
|
# end
|
|
46
48
|
#
|
|
47
|
-
# @note Because `subject` is designed to create state that is reset
|
|
48
|
-
# each example, and `before(:context)` is designed to setup
|
|
49
|
-
# shared across _all_ examples in an example group,
|
|
50
|
-
# intended to be used in a `before(:context)` hook.
|
|
49
|
+
# @note Because `subject` is designed to create state that is reset
|
|
50
|
+
# between each example, and `before(:context)` is designed to setup
|
|
51
|
+
# state that is shared across _all_ examples in an example group,
|
|
52
|
+
# `subject` is _not_ intended to be used in a `before(:context)` hook.
|
|
51
53
|
#
|
|
52
54
|
# @see #should
|
|
53
55
|
# @see #should_not
|
|
54
56
|
# @see #is_expected
|
|
55
57
|
def subject
|
|
56
|
-
__memoized.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
Class === described ? described.new : described
|
|
60
|
-
end
|
|
58
|
+
__memoized.fetch_or_store(:subject) do
|
|
59
|
+
described = described_class || self.class.metadata.fetch(:description_args).first
|
|
60
|
+
Class === described ? described.new : described
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
@@ -67,7 +67,7 @@ module RSpec
|
|
|
67
67
|
#
|
|
68
68
|
# @example
|
|
69
69
|
#
|
|
70
|
-
# describe Person do
|
|
70
|
+
# RSpec.describe Person do
|
|
71
71
|
# it { should be_eligible_to_vote }
|
|
72
72
|
# end
|
|
73
73
|
#
|
|
@@ -78,6 +78,7 @@ module RSpec
|
|
|
78
78
|
# @note If you are using RSpec's newer expect-based syntax you may
|
|
79
79
|
# want to use `is_expected.to` instead of `should`.
|
|
80
80
|
def should(matcher=nil, message=nil)
|
|
81
|
+
enforce_value_expectation(matcher, 'should')
|
|
81
82
|
RSpec::Expectations::PositiveExpectationHandler.handle_matcher(subject, matcher, message)
|
|
82
83
|
end
|
|
83
84
|
|
|
@@ -86,7 +87,7 @@ module RSpec
|
|
|
86
87
|
#
|
|
87
88
|
# @example
|
|
88
89
|
#
|
|
89
|
-
# describe Person do
|
|
90
|
+
# RSpec.describe Person do
|
|
90
91
|
# it { should_not be_eligible_to_vote }
|
|
91
92
|
# end
|
|
92
93
|
#
|
|
@@ -97,6 +98,7 @@ module RSpec
|
|
|
97
98
|
# @note If you are using RSpec's newer expect-based syntax you may
|
|
98
99
|
# want to use `is_expected.to_not` instead of `should_not`.
|
|
99
100
|
def should_not(matcher=nil, message=nil)
|
|
101
|
+
enforce_value_expectation(matcher, 'should_not')
|
|
100
102
|
RSpec::Expectations::NegativeExpectationHandler.handle_matcher(subject, matcher, message)
|
|
101
103
|
end
|
|
102
104
|
|
|
@@ -119,38 +121,110 @@ module RSpec
|
|
|
119
121
|
expect(subject)
|
|
120
122
|
end
|
|
121
123
|
|
|
124
|
+
# @private
|
|
125
|
+
# should just be placed in private section,
|
|
126
|
+
# but Ruby issues warnings on private attributes.
|
|
127
|
+
# and expanding it to the equivalent method upsets Rubocop,
|
|
128
|
+
# b/c it should obviously be a reader
|
|
129
|
+
attr_reader :__memoized
|
|
130
|
+
private :__memoized
|
|
131
|
+
|
|
122
132
|
private
|
|
123
133
|
|
|
124
134
|
# @private
|
|
125
|
-
def
|
|
126
|
-
|
|
135
|
+
def initialize(*)
|
|
136
|
+
__init_memoized
|
|
137
|
+
super
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
# @private
|
|
141
|
+
def __init_memoized
|
|
142
|
+
@__memoized = if RSpec.configuration.threadsafe?
|
|
143
|
+
ThreadsafeMemoized.new
|
|
144
|
+
else
|
|
145
|
+
NonThreadSafeMemoized.new
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# @private
|
|
150
|
+
def enforce_value_expectation(matcher, method_name)
|
|
151
|
+
return if matcher_supports_value_expectations?(matcher)
|
|
152
|
+
|
|
153
|
+
RSpec.deprecate(
|
|
154
|
+
"#{method_name} #{RSpec::Support::ObjectFormatter.format(matcher)}",
|
|
155
|
+
:message =>
|
|
156
|
+
"The implicit block expectation syntax is deprecated, you should pass " \
|
|
157
|
+
"a block to `expect` to use the provided block expectation matcher " \
|
|
158
|
+
"(#{RSpec::Support::ObjectFormatter.format(matcher)}), " \
|
|
159
|
+
"or the matcher must implement `supports_value_expectations?`."
|
|
160
|
+
)
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def matcher_supports_value_expectations?(matcher)
|
|
164
|
+
matcher.supports_value_expectations?
|
|
165
|
+
rescue
|
|
166
|
+
true
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# @private
|
|
170
|
+
class ThreadsafeMemoized
|
|
171
|
+
def initialize
|
|
172
|
+
@memoized = {}
|
|
173
|
+
@mutex = Support::ReentrantMutex.new
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
def fetch_or_store(key)
|
|
177
|
+
@memoized.fetch(key) do # only first access pays for synchronization
|
|
178
|
+
@mutex.synchronize do
|
|
179
|
+
@memoized.fetch(key) { @memoized[key] = yield }
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# @private
|
|
186
|
+
class NonThreadSafeMemoized
|
|
187
|
+
def initialize
|
|
188
|
+
@memoized = {}
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def fetch_or_store(key)
|
|
192
|
+
@memoized.fetch(key) { @memoized[key] = yield }
|
|
193
|
+
end
|
|
127
194
|
end
|
|
128
195
|
|
|
129
196
|
# Used internally to customize the behavior of the
|
|
130
197
|
# memoized hash when used in a `before(:context)` hook.
|
|
131
198
|
#
|
|
132
199
|
# @private
|
|
133
|
-
class
|
|
200
|
+
class ContextHookMemoized
|
|
134
201
|
def self.isolate_for_context_hook(example_group_instance)
|
|
135
|
-
|
|
202
|
+
exploding_memoized = self
|
|
136
203
|
|
|
137
204
|
example_group_instance.instance_exec do
|
|
138
|
-
@__memoized =
|
|
205
|
+
@__memoized = exploding_memoized
|
|
139
206
|
|
|
140
207
|
begin
|
|
141
208
|
yield
|
|
142
209
|
ensure
|
|
143
|
-
|
|
210
|
+
# This is doing a reset instead of just isolating for context hook.
|
|
211
|
+
# Really, this should set the old @__memoized back into place.
|
|
212
|
+
#
|
|
213
|
+
# Caller is the before and after context hooks
|
|
214
|
+
# which are both called from self.run
|
|
215
|
+
# I didn't look at why it made tests fail, maybe an object was getting reused in RSpec tests,
|
|
216
|
+
# if so, then that probably already works, and its the tests that are wrong.
|
|
217
|
+
__init_memoized
|
|
144
218
|
end
|
|
145
219
|
end
|
|
146
220
|
end
|
|
147
221
|
|
|
148
|
-
def self.
|
|
222
|
+
def self.fetch_or_store(key, &_block)
|
|
149
223
|
description = if key == :subject
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
224
|
+
"subject"
|
|
225
|
+
else
|
|
226
|
+
"let declaration `#{key}`"
|
|
227
|
+
end
|
|
154
228
|
|
|
155
229
|
raise <<-EOS
|
|
156
230
|
#{description} accessed in #{article} #{hook_expression} hook at:
|
|
@@ -206,25 +280,26 @@ EOS
|
|
|
206
280
|
# maybe 3 declarations) in any given example group, but that can
|
|
207
281
|
# quickly degrade with overuse. YMMV.
|
|
208
282
|
#
|
|
209
|
-
# @note `let`
|
|
210
|
-
#
|
|
211
|
-
#
|
|
283
|
+
# @note `let` can be configured to be threadsafe or not.
|
|
284
|
+
# If it is threadsafe, it will take longer to access the value.
|
|
285
|
+
# If it is not threadsafe, it may behave in surprising ways in examples
|
|
286
|
+
# that spawn separate threads. Specify this on `RSpec.configure`
|
|
212
287
|
#
|
|
213
288
|
# @note Because `let` is designed to create state that is reset between
|
|
214
|
-
# each example, and `before(:context)` is designed to setup state that
|
|
215
|
-
# shared across _all_ examples in an example group, `let` is _not_
|
|
289
|
+
# each example, and `before(:context)` is designed to setup state that
|
|
290
|
+
# is shared across _all_ examples in an example group, `let` is _not_
|
|
216
291
|
# intended to be used in a `before(:context)` hook.
|
|
217
292
|
#
|
|
218
293
|
# @example
|
|
219
294
|
#
|
|
220
|
-
# describe Thing do
|
|
295
|
+
# RSpec.describe Thing do
|
|
221
296
|
# let(:thing) { Thing.new }
|
|
222
297
|
#
|
|
223
298
|
# it "does something" do
|
|
224
|
-
# #
|
|
299
|
+
# # First invocation, executes block, memoizes and returns result.
|
|
225
300
|
# thing.do_something
|
|
226
301
|
#
|
|
227
|
-
# #
|
|
302
|
+
# # Second invocation, returns the memoized value.
|
|
228
303
|
# thing.should be_something
|
|
229
304
|
# end
|
|
230
305
|
# end
|
|
@@ -232,14 +307,40 @@ EOS
|
|
|
232
307
|
# We have to pass the block directly to `define_method` to
|
|
233
308
|
# allow it to use method constructs like `super` and `return`.
|
|
234
309
|
raise "#let or #subject called without a block" if block.nil?
|
|
235
|
-
|
|
310
|
+
|
|
311
|
+
# A list of reserved words that can't be used as a name for a memoized helper
|
|
312
|
+
# Matches for both symbols and passed strings
|
|
313
|
+
if [:initialize, :to_s].include?(name.to_sym)
|
|
314
|
+
raise ArgumentError, "#let or #subject called with reserved name `#{name}`"
|
|
315
|
+
end
|
|
316
|
+
|
|
317
|
+
our_module = MemoizedHelpers.module_for(self)
|
|
318
|
+
|
|
319
|
+
# If we have a module clash in our helper module
|
|
320
|
+
# then we need to remove it to prevent a warning.
|
|
321
|
+
#
|
|
322
|
+
# Note we do not check ancestor modules (see: `instance_methods(false)`)
|
|
323
|
+
# as we can override them.
|
|
324
|
+
if our_module.instance_methods(false).include?(name)
|
|
325
|
+
our_module.__send__(:remove_method, name)
|
|
326
|
+
end
|
|
327
|
+
our_module.__send__(:define_method, name, &block)
|
|
328
|
+
|
|
329
|
+
# If we have a module clash in the example module
|
|
330
|
+
# then we need to remove it to prevent a warning.
|
|
331
|
+
#
|
|
332
|
+
# Note we do not check ancestor modules (see: `instance_methods(false)`)
|
|
333
|
+
# as we can override them.
|
|
334
|
+
if instance_methods(false).include?(name)
|
|
335
|
+
remove_method(name)
|
|
336
|
+
end
|
|
236
337
|
|
|
237
338
|
# Apply the memoization. The method has been defined in an ancestor
|
|
238
339
|
# module so we can use `super` here to get the value.
|
|
239
340
|
if block.arity == 1
|
|
240
|
-
define_method(name) { __memoized.
|
|
341
|
+
define_method(name) { __memoized.fetch_or_store(name) { super(RSpec.current_example, &nil) } }
|
|
241
342
|
else
|
|
242
|
-
define_method(name) { __memoized.
|
|
343
|
+
define_method(name) { __memoized.fetch_or_store(name) { super(&nil) } }
|
|
243
344
|
end
|
|
244
345
|
end
|
|
245
346
|
|
|
@@ -267,7 +368,7 @@ EOS
|
|
|
267
368
|
# end
|
|
268
369
|
# end
|
|
269
370
|
#
|
|
270
|
-
# describe Thing do
|
|
371
|
+
# RSpec.describe Thing do
|
|
271
372
|
# after(:example) { Thing.reset_count }
|
|
272
373
|
#
|
|
273
374
|
# context "using let" do
|
|
@@ -302,27 +403,34 @@ EOS
|
|
|
302
403
|
end
|
|
303
404
|
|
|
304
405
|
# Declares a `subject` for an example group which can then be wrapped
|
|
305
|
-
# with `expect` using `is_expected` to make it the target of an
|
|
306
|
-
# in a concise, one-line example.
|
|
406
|
+
# with `expect` using `is_expected` to make it the target of an
|
|
407
|
+
# expectation in a concise, one-line example.
|
|
307
408
|
#
|
|
308
409
|
# Given a `name`, defines a method with that name which returns the
|
|
309
410
|
# `subject`. This lets you declare the subject once and access it
|
|
310
411
|
# implicitly in one-liners and explicitly using an intention revealing
|
|
311
412
|
# name.
|
|
312
413
|
#
|
|
414
|
+
# When given a `name`, calling `super` in the block is not supported.
|
|
415
|
+
#
|
|
416
|
+
# @note `subject` can be configured to be threadsafe or not.
|
|
417
|
+
# If it is threadsafe, it will take longer to access the value.
|
|
418
|
+
# If it is not threadsafe, it may behave in surprising ways in examples
|
|
419
|
+
# that spawn separate threads. Specify this on `RSpec.configure`
|
|
420
|
+
#
|
|
313
421
|
# @param name [String,Symbol] used to define an accessor with an
|
|
314
422
|
# intention revealing name
|
|
315
423
|
# @param block defines the value to be returned by `subject` in examples
|
|
316
424
|
#
|
|
317
425
|
# @example
|
|
318
426
|
#
|
|
319
|
-
# describe CheckingAccount, "with $50" do
|
|
427
|
+
# RSpec.describe CheckingAccount, "with $50" do
|
|
320
428
|
# subject { CheckingAccount.new(Money.new(50, :USD)) }
|
|
321
429
|
# it { is_expected.to have_a_balance_of(Money.new(50, :USD)) }
|
|
322
430
|
# it { is_expected.not_to be_overdrawn }
|
|
323
431
|
# end
|
|
324
432
|
#
|
|
325
|
-
# describe CheckingAccount, "with a non-zero starting balance" do
|
|
433
|
+
# RSpec.describe CheckingAccount, "with a non-zero starting balance" do
|
|
326
434
|
# subject(:account) { CheckingAccount.new(Money.new(50, :USD)) }
|
|
327
435
|
# it { is_expected.not_to be_overdrawn }
|
|
328
436
|
# it "has a balance equal to the starting balance" do
|
|
@@ -346,9 +454,9 @@ EOS
|
|
|
346
454
|
end
|
|
347
455
|
end
|
|
348
456
|
|
|
349
|
-
# Just like `subject`, except the block is invoked by an implicit
|
|
350
|
-
# hook. This serves a dual purpose of setting up state and
|
|
351
|
-
# memoized reference to that state.
|
|
457
|
+
# Just like `subject`, except the block is invoked by an implicit
|
|
458
|
+
# `before` hook. This serves a dual purpose of setting up state and
|
|
459
|
+
# providing a memoized reference to that state.
|
|
352
460
|
#
|
|
353
461
|
# @example
|
|
354
462
|
#
|
|
@@ -370,7 +478,7 @@ EOS
|
|
|
370
478
|
# end
|
|
371
479
|
# end
|
|
372
480
|
#
|
|
373
|
-
# describe Thing do
|
|
481
|
+
# RSpec.describe Thing do
|
|
374
482
|
# after(:example) { Thing.reset_count }
|
|
375
483
|
#
|
|
376
484
|
# context "using subject" do
|
|
@@ -420,9 +528,9 @@ EOS
|
|
|
420
528
|
def self.module_for(example_group)
|
|
421
529
|
get_constant_or_yield(example_group, :LetDefinitions) do
|
|
422
530
|
mod = Module.new do
|
|
423
|
-
include
|
|
531
|
+
include(Module.new {
|
|
424
532
|
example_group.const_set(:NamedSubjectPreventSuper, self)
|
|
425
|
-
}
|
|
533
|
+
})
|
|
426
534
|
end
|
|
427
535
|
|
|
428
536
|
example_group.const_set(:LetDefinitions, mod)
|
|
@@ -441,6 +549,7 @@ EOS
|
|
|
441
549
|
# Gets the named constant or yields.
|
|
442
550
|
# On 1.8, const_defined? / const_get do not take into
|
|
443
551
|
# account the inheritance hierarchy.
|
|
552
|
+
# :nocov:
|
|
444
553
|
def self.get_constant_or_yield(example_group, name)
|
|
445
554
|
if example_group.const_defined?(name)
|
|
446
555
|
example_group.const_get(name)
|
|
@@ -448,6 +557,7 @@ EOS
|
|
|
448
557
|
yield
|
|
449
558
|
end
|
|
450
559
|
end
|
|
560
|
+
# :nocov:
|
|
451
561
|
else
|
|
452
562
|
# @private
|
|
453
563
|
#
|