rspec-core 2.12.2 → 2.13.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.
- data/Changelog.md +31 -0
- data/README.md +11 -10
- data/features/command_line/example_name_option.feature +6 -10
- data/features/command_line/tag.feature +15 -8
- data/features/configuration/backtrace_clean_patterns.feature +102 -0
- data/features/configuration/failure_exit_code.feature +36 -0
- data/features/configuration/order_and_seed.feature +3 -0
- data/features/configuration/output_stream.feature +24 -0
- data/features/configuration/pattern.feature +30 -0
- data/features/configuration/profile.feature +163 -0
- data/features/configuration/run_all_when_everything_filtered.feature +60 -0
- data/features/configuration/show_failures_in_pending_blocks.feature +61 -0
- data/features/configuration/treat_symbols_as_metadata_keys_with_true_values.feature +52 -0
- data/features/filtering/exclusion_filters.feature +1 -2
- data/features/formatters/configurable_colors.feature +31 -0
- data/features/step_definitions/additional_cli_steps.rb +21 -0
- data/features/subject/explicit_subject.feature +19 -0
- data/lib/autotest/rspec2.rb +1 -1
- data/lib/rspec/core.rb +1 -2
- data/lib/rspec/core/configuration.rb +33 -3
- data/lib/rspec/core/configuration_options.rb +5 -5
- data/lib/rspec/core/deprecation.rb +1 -1
- data/lib/rspec/core/example.rb +2 -2
- data/lib/rspec/core/example_group.rb +1 -3
- data/lib/rspec/core/formatters/base_text_formatter.rb +93 -27
- data/lib/rspec/core/formatters/documentation_formatter.rb +3 -3
- data/lib/rspec/core/formatters/progress_formatter.rb +3 -3
- data/lib/rspec/core/memoized_helpers.rb +425 -0
- data/lib/rspec/core/metadata.rb +6 -2
- data/lib/rspec/core/option_parser.rb +8 -2
- data/lib/rspec/core/pending.rb +7 -0
- data/lib/rspec/core/shared_context.rb +1 -1
- data/lib/rspec/core/version.rb +1 -1
- data/spec/autotest/failed_results_re_spec.rb +4 -4
- data/spec/autotest/rspec_spec.rb +25 -20
- data/spec/command_line/order_spec.rb +21 -21
- data/spec/rspec/core/command_line_spec.rb +6 -6
- data/spec/rspec/core/configuration_options_spec.rb +86 -72
- data/spec/rspec/core/configuration_spec.rb +161 -156
- data/spec/rspec/core/deprecations_spec.rb +4 -4
- data/spec/rspec/core/drb_command_line_spec.rb +9 -9
- data/spec/rspec/core/drb_options_spec.rb +46 -33
- data/spec/rspec/core/dsl_spec.rb +3 -3
- data/spec/rspec/core/example_group_spec.rb +156 -124
- data/spec/rspec/core/example_spec.rb +68 -52
- data/spec/rspec/core/filter_manager_spec.rb +36 -36
- data/spec/rspec/core/formatters/base_formatter_spec.rb +9 -9
- data/spec/rspec/core/formatters/base_text_formatter_spec.rb +104 -42
- data/spec/rspec/core/formatters/documentation_formatter_spec.rb +4 -4
- data/spec/rspec/core/formatters/helpers_spec.rb +13 -13
- data/spec/rspec/core/formatters/html_formatted-1.8.7-jruby.html +5 -17
- data/spec/rspec/core/formatters/html_formatted-1.8.7-rbx.html +159 -44
- data/spec/rspec/core/formatters/html_formatted-1.8.7.html +14 -14
- data/spec/rspec/core/formatters/html_formatted-1.9.2.html +20 -20
- data/spec/rspec/core/formatters/html_formatted-1.9.3-jruby.html +5 -5
- data/spec/rspec/core/formatters/html_formatted-1.9.3-rbx.html +159 -44
- data/spec/rspec/core/formatters/html_formatted-1.9.3.html +20 -20
- data/spec/rspec/core/formatters/{html_formatted-1.9.2-jruby.html → html_formatted-2.0.0.html} +24 -18
- data/spec/rspec/core/formatters/html_formatter_spec.rb +9 -5
- data/spec/rspec/core/formatters/json_formatter_spec.rb +9 -9
- data/spec/rspec/core/formatters/progress_formatter_spec.rb +4 -4
- data/spec/rspec/core/formatters/snippet_extractor_spec.rb +3 -3
- data/spec/rspec/core/formatters/text_mate_formatted-1.8.7-jruby.html +5 -17
- data/spec/rspec/core/formatters/text_mate_formatted-1.8.7-rbx.html +159 -44
- data/spec/rspec/core/formatters/text_mate_formatted-1.8.7.html +16 -16
- data/spec/rspec/core/formatters/text_mate_formatted-1.9.2.html +23 -23
- data/spec/rspec/core/formatters/text_mate_formatted-1.9.3-jruby.html +5 -17
- data/spec/rspec/core/formatters/text_mate_formatted-1.9.3-rbx.html +159 -44
- data/spec/rspec/core/formatters/text_mate_formatted-1.9.3.html +23 -23
- data/spec/rspec/core/formatters/{text_mate_formatted-1.9.2-jruby.html → text_mate_formatted-2.0.0.html} +25 -19
- data/spec/rspec/core/formatters/text_mate_formatter_spec.rb +8 -4
- data/spec/rspec/core/hooks_filtering_spec.rb +16 -16
- data/spec/rspec/core/hooks_spec.rb +11 -11
- data/spec/rspec/core/kernel_extensions_spec.rb +1 -1
- data/spec/rspec/core/memoized_helpers_spec.rb +458 -0
- data/spec/rspec/core/metadata_spec.rb +74 -74
- data/spec/rspec/core/option_parser_spec.rb +27 -27
- data/spec/rspec/core/pending_example_spec.rb +32 -32
- data/spec/rspec/core/project_initializer_spec.rb +8 -8
- data/spec/rspec/core/rake_task_spec.rb +16 -16
- data/spec/rspec/core/reporter_spec.rb +3 -3
- data/spec/rspec/core/resources/formatter_specs.rb +4 -4
- data/spec/rspec/core/ruby_project_spec.rb +4 -2
- data/spec/rspec/core/shared_context_spec.rb +34 -7
- data/spec/rspec/core/shared_example_group_spec.rb +14 -14
- data/spec/rspec/core/world_spec.rb +9 -9
- data/spec/rspec/core_spec.rb +5 -5
- data/spec/spec_helper.rb +4 -0
- data/spec/support/shared_example_groups.rb +4 -4
- data/spec/support/spec_files.rb +2 -2
- metadata +158 -150
- data/lib/rspec/core/let.rb +0 -110
- data/lib/rspec/core/subject.rb +0 -223
- data/spec/rspec/core/let_spec.rb +0 -55
- data/spec/rspec/core/subject_spec.rb +0 -255
|
@@ -38,7 +38,7 @@ module RSpec
|
|
|
38
38
|
end
|
|
39
39
|
|
|
40
40
|
def failure_output(example, exception)
|
|
41
|
-
|
|
41
|
+
failure_color("#{current_indentation}#{example.description.strip} (FAILED - #{next_failure_index})")
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def next_failure_index
|
|
@@ -47,11 +47,11 @@ module RSpec
|
|
|
47
47
|
end
|
|
48
48
|
|
|
49
49
|
def passed_output(example)
|
|
50
|
-
|
|
50
|
+
success_color("#{current_indentation}#{example.description.strip}")
|
|
51
51
|
end
|
|
52
52
|
|
|
53
53
|
def pending_output(example, message)
|
|
54
|
-
|
|
54
|
+
pending_color("#{current_indentation}#{example.description.strip} (PENDING: #{message})")
|
|
55
55
|
end
|
|
56
56
|
|
|
57
57
|
def current_indentation
|
|
@@ -7,17 +7,17 @@ module RSpec
|
|
|
7
7
|
|
|
8
8
|
def example_passed(example)
|
|
9
9
|
super(example)
|
|
10
|
-
output.print
|
|
10
|
+
output.print success_color('.')
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def example_pending(example)
|
|
14
14
|
super(example)
|
|
15
|
-
output.print
|
|
15
|
+
output.print pending_color('*')
|
|
16
16
|
end
|
|
17
17
|
|
|
18
18
|
def example_failed(example)
|
|
19
19
|
super(example)
|
|
20
|
-
output.print
|
|
20
|
+
output.print failure_color('F')
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def start_dump
|
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
module RSpec
|
|
2
|
+
module Core
|
|
3
|
+
module MemoizedHelpers
|
|
4
|
+
# @note `subject` was contributed by Joe Ferris to support the one-liner
|
|
5
|
+
# syntax embraced by shoulda matchers:
|
|
6
|
+
#
|
|
7
|
+
# describe Widget do
|
|
8
|
+
# it { should validate_presence_of(:name) }
|
|
9
|
+
# end
|
|
10
|
+
#
|
|
11
|
+
# While the examples below demonstrate how to use `subject`
|
|
12
|
+
# explicitly in examples, we recommend that you define a method with
|
|
13
|
+
# an intention revealing name instead.
|
|
14
|
+
#
|
|
15
|
+
# @example
|
|
16
|
+
#
|
|
17
|
+
# # explicit declaration of subject
|
|
18
|
+
# describe Person do
|
|
19
|
+
# subject { Person.new(:birthdate => 19.years.ago) }
|
|
20
|
+
# it "should be eligible to vote" do
|
|
21
|
+
# subject.should be_eligible_to_vote
|
|
22
|
+
# # ^ ^ explicit reference to subject not recommended
|
|
23
|
+
# end
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# # implicit subject => { Person.new }
|
|
27
|
+
# describe Person do
|
|
28
|
+
# it "should be eligible to vote" do
|
|
29
|
+
# subject.should be_eligible_to_vote
|
|
30
|
+
# # ^ ^ explicit reference to subject not recommended
|
|
31
|
+
# end
|
|
32
|
+
# end
|
|
33
|
+
#
|
|
34
|
+
# # one-liner syntax - should is invoked on subject
|
|
35
|
+
# describe Person do
|
|
36
|
+
# it { should be_eligible_to_vote }
|
|
37
|
+
# end
|
|
38
|
+
#
|
|
39
|
+
# @see #should
|
|
40
|
+
def subject
|
|
41
|
+
raise NotImplementedError, 'This definition is here for documentation purposes only'
|
|
42
|
+
' - it is overriden anyway below when this module gets included.'
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# When `should` is called with no explicit receiver, the call is
|
|
46
|
+
# delegated to the object returned by `subject`. Combined with an
|
|
47
|
+
# implicit subject this supports very concise expressions.
|
|
48
|
+
#
|
|
49
|
+
# @example
|
|
50
|
+
#
|
|
51
|
+
# describe Person do
|
|
52
|
+
# it { should be_eligible_to_vote }
|
|
53
|
+
# end
|
|
54
|
+
#
|
|
55
|
+
# @see #subject
|
|
56
|
+
def should(matcher=nil, message=nil)
|
|
57
|
+
RSpec::Expectations::PositiveExpectationHandler.handle_matcher(subject, matcher, message)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Just like `should`, `should_not` delegates to the subject (implicit or
|
|
61
|
+
# explicit) of the example group.
|
|
62
|
+
#
|
|
63
|
+
# @example
|
|
64
|
+
#
|
|
65
|
+
# describe Person do
|
|
66
|
+
# it { should_not be_eligible_to_vote }
|
|
67
|
+
# end
|
|
68
|
+
#
|
|
69
|
+
# @see #subject
|
|
70
|
+
def should_not(matcher=nil, message=nil)
|
|
71
|
+
RSpec::Expectations::NegativeExpectationHandler.handle_matcher(subject, matcher, message)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
private
|
|
75
|
+
|
|
76
|
+
# @private
|
|
77
|
+
def __memoized
|
|
78
|
+
@__memoized ||= {}
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def self.included(mod)
|
|
82
|
+
mod.extend(ClassMethods)
|
|
83
|
+
|
|
84
|
+
# This logic defines an implicit subject
|
|
85
|
+
mod.subject do
|
|
86
|
+
described = described_class || self.class.description
|
|
87
|
+
Class === described ? described.new : described
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
module ClassMethods
|
|
92
|
+
# Generates a method whose return value is memoized after the first
|
|
93
|
+
# call. Useful for reducing duplication between examples that assign
|
|
94
|
+
# values to the same local variable.
|
|
95
|
+
#
|
|
96
|
+
# @note `let` _can_ enhance readability when used sparingly (1,2, or
|
|
97
|
+
# maybe 3 declarations) in any given example group, but that can
|
|
98
|
+
# quickly degrade with overuse. YMMV.
|
|
99
|
+
#
|
|
100
|
+
# @note `let` uses an `||=` conditional that has the potential to
|
|
101
|
+
# behave in surprising ways in examples that spawn separate threads,
|
|
102
|
+
# though we have yet to see this in practice. You've been warned.
|
|
103
|
+
#
|
|
104
|
+
# @example
|
|
105
|
+
#
|
|
106
|
+
# describe Thing do
|
|
107
|
+
# let(:thing) { Thing.new }
|
|
108
|
+
#
|
|
109
|
+
# it "does something" do
|
|
110
|
+
# # first invocation, executes block, memoizes and returns result
|
|
111
|
+
# thing.do_something
|
|
112
|
+
#
|
|
113
|
+
# # second invocation, returns the memoized value
|
|
114
|
+
# thing.should be_something
|
|
115
|
+
# end
|
|
116
|
+
# end
|
|
117
|
+
def let(name, &block)
|
|
118
|
+
# We have to pass the block directly to `define_method` to
|
|
119
|
+
# allow it to use method constructs like `super` and `return`.
|
|
120
|
+
::RSpec::Core::MemoizedHelpers.module_for(self).define_method(name, &block)
|
|
121
|
+
|
|
122
|
+
# Apply the memoization. The method has been defined in an ancestor
|
|
123
|
+
# module so we can use `super` here to get the value.
|
|
124
|
+
define_method(name) do
|
|
125
|
+
__memoized.fetch(name) { |k| __memoized[k] = super() }
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Just like `let`, except the block is invoked by an implicit `before`
|
|
130
|
+
# hook. This serves a dual purpose of setting up state and providing a
|
|
131
|
+
# memoized reference to that state.
|
|
132
|
+
#
|
|
133
|
+
# @example
|
|
134
|
+
#
|
|
135
|
+
# class Thing
|
|
136
|
+
# def self.count
|
|
137
|
+
# @count ||= 0
|
|
138
|
+
# end
|
|
139
|
+
#
|
|
140
|
+
# def self.count=(val)
|
|
141
|
+
# @count += val
|
|
142
|
+
# end
|
|
143
|
+
#
|
|
144
|
+
# def self.reset_count
|
|
145
|
+
# @count = 0
|
|
146
|
+
# end
|
|
147
|
+
#
|
|
148
|
+
# def initialize
|
|
149
|
+
# self.class.count += 1
|
|
150
|
+
# end
|
|
151
|
+
# end
|
|
152
|
+
#
|
|
153
|
+
# describe Thing do
|
|
154
|
+
# after(:each) { Thing.reset_count }
|
|
155
|
+
#
|
|
156
|
+
# context "using let" do
|
|
157
|
+
# let(:thing) { Thing.new }
|
|
158
|
+
#
|
|
159
|
+
# it "is not invoked implicitly" do
|
|
160
|
+
# Thing.count.should eq(0)
|
|
161
|
+
# end
|
|
162
|
+
#
|
|
163
|
+
# it "can be invoked explicitly" do
|
|
164
|
+
# thing
|
|
165
|
+
# Thing.count.should eq(1)
|
|
166
|
+
# end
|
|
167
|
+
# end
|
|
168
|
+
#
|
|
169
|
+
# context "using let!" do
|
|
170
|
+
# let!(:thing) { Thing.new }
|
|
171
|
+
#
|
|
172
|
+
# it "is invoked implicitly" do
|
|
173
|
+
# Thing.count.should eq(1)
|
|
174
|
+
# end
|
|
175
|
+
#
|
|
176
|
+
# it "returns memoized version on first invocation" do
|
|
177
|
+
# thing
|
|
178
|
+
# Thing.count.should eq(1)
|
|
179
|
+
# end
|
|
180
|
+
# end
|
|
181
|
+
# end
|
|
182
|
+
def let!(name, &block)
|
|
183
|
+
let(name, &block)
|
|
184
|
+
before { __send__(name) }
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
# Declares a `subject` for an example group which can then be the
|
|
188
|
+
# implicit receiver (through delegation) of calls to `should`.
|
|
189
|
+
#
|
|
190
|
+
# Given a `name`, defines a method with that name which returns the
|
|
191
|
+
# `subject`. This lets you declare the subject once and access it
|
|
192
|
+
# implicitly in one-liners and explicitly using an intention revealing
|
|
193
|
+
# name.
|
|
194
|
+
#
|
|
195
|
+
# @param [String,Symbol] name used to define an accessor with an
|
|
196
|
+
# intention revealing name
|
|
197
|
+
# @param block defines the value to be returned by `subject` in examples
|
|
198
|
+
#
|
|
199
|
+
# @example
|
|
200
|
+
#
|
|
201
|
+
# describe CheckingAccount, "with $50" do
|
|
202
|
+
# subject { CheckingAccount.new(Money.new(50, :USD)) }
|
|
203
|
+
# it { should have_a_balance_of(Money.new(50, :USD)) }
|
|
204
|
+
# it { should_not be_overdrawn }
|
|
205
|
+
# end
|
|
206
|
+
#
|
|
207
|
+
# describe CheckingAccount, "with a non-zero starting balance" do
|
|
208
|
+
# subject(:account) { CheckingAccount.new(Money.new(50, :USD)) }
|
|
209
|
+
# it { should_not be_overdrawn }
|
|
210
|
+
# it "has a balance equal to the starting balance" do
|
|
211
|
+
# account.balance.should eq(Money.new(50, :USD))
|
|
212
|
+
# end
|
|
213
|
+
# end
|
|
214
|
+
#
|
|
215
|
+
# @see MemoizedHelpers#should
|
|
216
|
+
def subject(name=nil, &block)
|
|
217
|
+
let(:subject, &block)
|
|
218
|
+
alias_method name, :subject if name
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# Just like `subject`, except the block is invoked by an implicit `before`
|
|
222
|
+
# hook. This serves a dual purpose of setting up state and providing a
|
|
223
|
+
# memoized reference to that state.
|
|
224
|
+
#
|
|
225
|
+
# @example
|
|
226
|
+
#
|
|
227
|
+
# class Thing
|
|
228
|
+
# def self.count
|
|
229
|
+
# @count ||= 0
|
|
230
|
+
# end
|
|
231
|
+
#
|
|
232
|
+
# def self.count=(val)
|
|
233
|
+
# @count += val
|
|
234
|
+
# end
|
|
235
|
+
#
|
|
236
|
+
# def self.reset_count
|
|
237
|
+
# @count = 0
|
|
238
|
+
# end
|
|
239
|
+
#
|
|
240
|
+
# def initialize
|
|
241
|
+
# self.class.count += 1
|
|
242
|
+
# end
|
|
243
|
+
# end
|
|
244
|
+
#
|
|
245
|
+
# describe Thing do
|
|
246
|
+
# after(:each) { Thing.reset_count }
|
|
247
|
+
#
|
|
248
|
+
# context "using subject" do
|
|
249
|
+
# subject { Thing.new }
|
|
250
|
+
#
|
|
251
|
+
# it "is not invoked implicitly" do
|
|
252
|
+
# Thing.count.should eq(0)
|
|
253
|
+
# end
|
|
254
|
+
#
|
|
255
|
+
# it "can be invoked explicitly" do
|
|
256
|
+
# subject
|
|
257
|
+
# Thing.count.should eq(1)
|
|
258
|
+
# end
|
|
259
|
+
# end
|
|
260
|
+
#
|
|
261
|
+
# context "using subject!" do
|
|
262
|
+
# subject!(:thing) { Thing.new }
|
|
263
|
+
#
|
|
264
|
+
# it "is invoked implicitly" do
|
|
265
|
+
# Thing.count.should eq(1)
|
|
266
|
+
# end
|
|
267
|
+
#
|
|
268
|
+
# it "returns memoized version on first invocation" do
|
|
269
|
+
# subject
|
|
270
|
+
# Thing.count.should eq(1)
|
|
271
|
+
# end
|
|
272
|
+
# end
|
|
273
|
+
# end
|
|
274
|
+
def subject!(name=nil, &block)
|
|
275
|
+
subject(name, &block)
|
|
276
|
+
before { subject }
|
|
277
|
+
end
|
|
278
|
+
|
|
279
|
+
# Creates a nested example group named by the submitted `attribute`,
|
|
280
|
+
# and then generates an example using the submitted block.
|
|
281
|
+
#
|
|
282
|
+
# @example
|
|
283
|
+
#
|
|
284
|
+
# # This ...
|
|
285
|
+
# describe Array do
|
|
286
|
+
# its(:size) { should eq(0) }
|
|
287
|
+
# end
|
|
288
|
+
#
|
|
289
|
+
# # ... generates the same runtime structure as this:
|
|
290
|
+
# describe Array do
|
|
291
|
+
# describe "size" do
|
|
292
|
+
# it "should eq(0)" do
|
|
293
|
+
# subject.size.should eq(0)
|
|
294
|
+
# end
|
|
295
|
+
# end
|
|
296
|
+
# end
|
|
297
|
+
#
|
|
298
|
+
# The attribute can be a `Symbol` or a `String`. Given a `String`
|
|
299
|
+
# with dots, the result is as though you concatenated that `String`
|
|
300
|
+
# onto the subject in an expression.
|
|
301
|
+
#
|
|
302
|
+
# @example
|
|
303
|
+
#
|
|
304
|
+
# describe Person do
|
|
305
|
+
# subject do
|
|
306
|
+
# Person.new.tap do |person|
|
|
307
|
+
# person.phone_numbers << "555-1212"
|
|
308
|
+
# end
|
|
309
|
+
# end
|
|
310
|
+
#
|
|
311
|
+
# its("phone_numbers.first") { should eq("555-1212") }
|
|
312
|
+
# end
|
|
313
|
+
#
|
|
314
|
+
# When the subject is a `Hash`, you can refer to the Hash keys by
|
|
315
|
+
# specifying a `Symbol` or `String` in an array.
|
|
316
|
+
#
|
|
317
|
+
# @example
|
|
318
|
+
#
|
|
319
|
+
# describe "a configuration Hash" do
|
|
320
|
+
# subject do
|
|
321
|
+
# { :max_users => 3,
|
|
322
|
+
# 'admin' => :all_permissions }
|
|
323
|
+
# end
|
|
324
|
+
#
|
|
325
|
+
# its([:max_users]) { should eq(3) }
|
|
326
|
+
# its(['admin']) { should eq(:all_permissions) }
|
|
327
|
+
#
|
|
328
|
+
# # You can still access to its regular methods this way:
|
|
329
|
+
# its(:keys) { should include(:max_users) }
|
|
330
|
+
# its(:count) { should eq(2) }
|
|
331
|
+
# end
|
|
332
|
+
#
|
|
333
|
+
# Note that this method does not modify `subject` in any way, so if you
|
|
334
|
+
# refer to `subject` in `let` or `before` blocks, you're still
|
|
335
|
+
# referring to the outer subject.
|
|
336
|
+
#
|
|
337
|
+
# @example
|
|
338
|
+
#
|
|
339
|
+
# describe Person do
|
|
340
|
+
# subject { Person.new }
|
|
341
|
+
# before { subject.age = 25 }
|
|
342
|
+
# its(:age) { should eq(25) }
|
|
343
|
+
# end
|
|
344
|
+
def its(attribute, &block)
|
|
345
|
+
describe(attribute) do
|
|
346
|
+
if Array === attribute
|
|
347
|
+
let(:__its_subject) { subject[*attribute] }
|
|
348
|
+
else
|
|
349
|
+
let(:__its_subject) do
|
|
350
|
+
attribute_chain = attribute.to_s.split('.')
|
|
351
|
+
attribute_chain.inject(subject) do |inner_subject, attr|
|
|
352
|
+
inner_subject.send(attr)
|
|
353
|
+
end
|
|
354
|
+
end
|
|
355
|
+
end
|
|
356
|
+
|
|
357
|
+
def should(matcher=nil, message=nil)
|
|
358
|
+
RSpec::Expectations::PositiveExpectationHandler.handle_matcher(__its_subject, matcher, message)
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
def should_not(matcher=nil, message=nil)
|
|
362
|
+
RSpec::Expectations::NegativeExpectationHandler.handle_matcher(__its_subject, matcher, message)
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
example(&block)
|
|
366
|
+
end
|
|
367
|
+
end
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
# @api private
|
|
371
|
+
#
|
|
372
|
+
# Gets the LetDefinitions module. The module is mixed into
|
|
373
|
+
# the example group and is used to hold all let definitions.
|
|
374
|
+
# This is done so that the block passed to `let` can be
|
|
375
|
+
# forwarded directly on to `define_method`, so that all method
|
|
376
|
+
# constructs (including `super` and `return`) can be used in
|
|
377
|
+
# a `let` block.
|
|
378
|
+
#
|
|
379
|
+
# The memoization is provided by a method definition on the
|
|
380
|
+
# example group that supers to the LetDefinitions definition
|
|
381
|
+
# in order to get the value to memoize.
|
|
382
|
+
def self.module_for(example_group)
|
|
383
|
+
get_constant_or_yield(example_group, :LetDefinitions) do
|
|
384
|
+
# Expose `define_method` as a public method, so we can
|
|
385
|
+
# easily use it below.
|
|
386
|
+
mod = Module.new { public_class_method :define_method }
|
|
387
|
+
example_group.__send__(:include, mod)
|
|
388
|
+
example_group.const_set(:LetDefinitions, mod)
|
|
389
|
+
mod
|
|
390
|
+
end
|
|
391
|
+
end
|
|
392
|
+
|
|
393
|
+
if Module.method(:const_defined?).arity == 1 # for 1.8
|
|
394
|
+
# @api private
|
|
395
|
+
#
|
|
396
|
+
# Gets the named constant or yields.
|
|
397
|
+
# On 1.8, const_defined? / const_get do not take into
|
|
398
|
+
# account the inheritance hierarchy.
|
|
399
|
+
def self.get_constant_or_yield(example_group, name)
|
|
400
|
+
if example_group.const_defined?(name)
|
|
401
|
+
example_group.const_get(name)
|
|
402
|
+
else
|
|
403
|
+
yield
|
|
404
|
+
end
|
|
405
|
+
end
|
|
406
|
+
else
|
|
407
|
+
# @api private
|
|
408
|
+
#
|
|
409
|
+
# Gets the named constant or yields.
|
|
410
|
+
# On 1.9, const_defined? / const_get take into account the
|
|
411
|
+
# the inheritance by default, and accept an argument to
|
|
412
|
+
# disable this behavior. It's important that we don't
|
|
413
|
+
# consider inheritance here; each example group level that
|
|
414
|
+
# uses a `let` should get its own `LetDefinitions` module.
|
|
415
|
+
def self.get_constant_or_yield(example_group, name)
|
|
416
|
+
if example_group.const_defined?(name, (check_ancestors = false))
|
|
417
|
+
example_group.const_get(name, check_ancestors)
|
|
418
|
+
else
|
|
419
|
+
yield
|
|
420
|
+
end
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
end
|
|
424
|
+
end
|
|
425
|
+
end
|