opal-rspec-cj 0.4.4

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 (176) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +3 -0
  3. data/.gitmodules +15 -0
  4. data/.travis.yml +13 -0
  5. data/.yardopts +5 -0
  6. data/CHANGELOG.md +25 -0
  7. data/Gemfile +8 -0
  8. data/README.md +147 -0
  9. data/Rakefile +26 -0
  10. data/config.ru +10 -0
  11. data/example/Gemfile +4 -0
  12. data/example/README.md +13 -0
  13. data/example/Rakefile +8 -0
  14. data/example/opal/user.rb +11 -0
  15. data/example/spec/user_spec.rb +15 -0
  16. data/lib/opal-rspec.rb +2 -0
  17. data/lib/opal/rspec.rb +20 -0
  18. data/lib/opal/rspec/rake_task.rb +63 -0
  19. data/lib/opal/rspec/version.rb +5 -0
  20. data/opal-rspec.gemspec +21 -0
  21. data/opal/opal-rspec.rb +1 -0
  22. data/opal/opal/rspec.rb +25 -0
  23. data/opal/opal/rspec/async.rb +289 -0
  24. data/opal/opal/rspec/browser_formatter.rb +188 -0
  25. data/opal/opal/rspec/fixes.rb +116 -0
  26. data/opal/opal/rspec/requires.rb +45 -0
  27. data/opal/opal/rspec/runner.rb +69 -0
  28. data/opal/opal/rspec/sprockets_runner.rb.erb +11 -0
  29. data/opal/opal/rspec/text_formatter.rb +74 -0
  30. data/spec/async_spec.rb +38 -0
  31. data/spec/example_spec.rb +163 -0
  32. data/spec/matchers_spec.rb +201 -0
  33. data/spec/mock_spec.rb +63 -0
  34. data/spec/named_subject_spec.rb +11 -0
  35. data/spec/should_syntax_spec.rb +17 -0
  36. data/vendor/spec_runner.js +50 -0
  37. data/vendor_lib/rspec-expectations.rb +1 -0
  38. data/vendor_lib/rspec.rb +3 -0
  39. data/vendor_lib/rspec/autorun.rb +2 -0
  40. data/vendor_lib/rspec/core.rb +203 -0
  41. data/vendor_lib/rspec/core/backport_random.rb +302 -0
  42. data/vendor_lib/rspec/core/backtrace_formatter.rb +65 -0
  43. data/vendor_lib/rspec/core/command_line.rb +36 -0
  44. data/vendor_lib/rspec/core/configuration.rb +1129 -0
  45. data/vendor_lib/rspec/core/configuration_options.rb +143 -0
  46. data/vendor_lib/rspec/core/drb_command_line.rb +26 -0
  47. data/vendor_lib/rspec/core/drb_options.rb +87 -0
  48. data/vendor_lib/rspec/core/dsl.rb +26 -0
  49. data/vendor_lib/rspec/core/example.rb +312 -0
  50. data/vendor_lib/rspec/core/example_group.rb +540 -0
  51. data/vendor_lib/rspec/core/filter_manager.rb +224 -0
  52. data/vendor_lib/rspec/core/flat_map.rb +17 -0
  53. data/vendor_lib/rspec/core/formatters.rb +54 -0
  54. data/vendor_lib/rspec/core/formatters/base_formatter.rb +291 -0
  55. data/vendor_lib/rspec/core/formatters/base_text_formatter.rb +307 -0
  56. data/vendor_lib/rspec/core/formatters/deprecation_formatter.rb +193 -0
  57. data/vendor_lib/rspec/core/formatters/documentation_formatter.rb +67 -0
  58. data/vendor_lib/rspec/core/formatters/helpers.rb +82 -0
  59. data/vendor_lib/rspec/core/formatters/html_formatter.rb +155 -0
  60. data/vendor_lib/rspec/core/formatters/html_printer.rb +408 -0
  61. data/vendor_lib/rspec/core/formatters/json_formatter.rb +99 -0
  62. data/vendor_lib/rspec/core/formatters/progress_formatter.rb +32 -0
  63. data/vendor_lib/rspec/core/formatters/snippet_extractor.rb +101 -0
  64. data/vendor_lib/rspec/core/hooks.rb +535 -0
  65. data/vendor_lib/rspec/core/memoized_helpers.rb +431 -0
  66. data/vendor_lib/rspec/core/metadata.rb +313 -0
  67. data/vendor_lib/rspec/core/mocking/with_absolutely_nothing.rb +11 -0
  68. data/vendor_lib/rspec/core/mocking/with_flexmock.rb +27 -0
  69. data/vendor_lib/rspec/core/mocking/with_mocha.rb +52 -0
  70. data/vendor_lib/rspec/core/mocking/with_rr.rb +27 -0
  71. data/vendor_lib/rspec/core/mocking/with_rspec.rb +27 -0
  72. data/vendor_lib/rspec/core/option_parser.rb +234 -0
  73. data/vendor_lib/rspec/core/ordering.rb +154 -0
  74. data/vendor_lib/rspec/core/pending.rb +110 -0
  75. data/vendor_lib/rspec/core/project_initializer.rb +88 -0
  76. data/vendor_lib/rspec/core/rake_task.rb +128 -0
  77. data/vendor_lib/rspec/core/reporter.rb +132 -0
  78. data/vendor_lib/rspec/core/ruby_project.rb +44 -0
  79. data/vendor_lib/rspec/core/runner.rb +97 -0
  80. data/vendor_lib/rspec/core/shared_context.rb +53 -0
  81. data/vendor_lib/rspec/core/shared_example_group.rb +146 -0
  82. data/vendor_lib/rspec/core/shared_example_group/collection.rb +27 -0
  83. data/vendor_lib/rspec/core/version.rb +7 -0
  84. data/vendor_lib/rspec/core/warnings.rb +22 -0
  85. data/vendor_lib/rspec/core/world.rb +131 -0
  86. data/vendor_lib/rspec/expectations.rb +75 -0
  87. data/vendor_lib/rspec/expectations/differ.rb +154 -0
  88. data/vendor_lib/rspec/expectations/errors.rb +9 -0
  89. data/vendor_lib/rspec/expectations/expectation_target.rb +87 -0
  90. data/vendor_lib/rspec/expectations/extensions.rb +1 -0
  91. data/vendor_lib/rspec/expectations/extensions/object.rb +29 -0
  92. data/vendor_lib/rspec/expectations/fail_with.rb +79 -0
  93. data/vendor_lib/rspec/expectations/handler.rb +68 -0
  94. data/vendor_lib/rspec/expectations/syntax.rb +182 -0
  95. data/vendor_lib/rspec/expectations/version.rb +8 -0
  96. data/vendor_lib/rspec/matchers.rb +633 -0
  97. data/vendor_lib/rspec/matchers/built_in.rb +39 -0
  98. data/vendor_lib/rspec/matchers/built_in/base_matcher.rb +68 -0
  99. data/vendor_lib/rspec/matchers/built_in/be.rb +213 -0
  100. data/vendor_lib/rspec/matchers/built_in/be_instance_of.rb +15 -0
  101. data/vendor_lib/rspec/matchers/built_in/be_kind_of.rb +11 -0
  102. data/vendor_lib/rspec/matchers/built_in/be_within.rb +55 -0
  103. data/vendor_lib/rspec/matchers/built_in/change.rb +141 -0
  104. data/vendor_lib/rspec/matchers/built_in/cover.rb +21 -0
  105. data/vendor_lib/rspec/matchers/built_in/eq.rb +22 -0
  106. data/vendor_lib/rspec/matchers/built_in/eql.rb +23 -0
  107. data/vendor_lib/rspec/matchers/built_in/equal.rb +48 -0
  108. data/vendor_lib/rspec/matchers/built_in/exist.rb +26 -0
  109. data/vendor_lib/rspec/matchers/built_in/has.rb +48 -0
  110. data/vendor_lib/rspec/matchers/built_in/include.rb +61 -0
  111. data/vendor_lib/rspec/matchers/built_in/match.rb +17 -0
  112. data/vendor_lib/rspec/matchers/built_in/match_array.rb +51 -0
  113. data/vendor_lib/rspec/matchers/built_in/raise_error.rb +154 -0
  114. data/vendor_lib/rspec/matchers/built_in/respond_to.rb +74 -0
  115. data/vendor_lib/rspec/matchers/built_in/satisfy.rb +30 -0
  116. data/vendor_lib/rspec/matchers/built_in/start_and_end_with.rb +48 -0
  117. data/vendor_lib/rspec/matchers/built_in/throw_symbol.rb +94 -0
  118. data/vendor_lib/rspec/matchers/built_in/yield.rb +297 -0
  119. data/vendor_lib/rspec/matchers/compatibility.rb +14 -0
  120. data/vendor_lib/rspec/matchers/configuration.rb +113 -0
  121. data/vendor_lib/rspec/matchers/dsl.rb +23 -0
  122. data/vendor_lib/rspec/matchers/generated_descriptions.rb +35 -0
  123. data/vendor_lib/rspec/matchers/matcher.rb +301 -0
  124. data/vendor_lib/rspec/matchers/method_missing.rb +12 -0
  125. data/vendor_lib/rspec/matchers/operator_matcher.rb +99 -0
  126. data/vendor_lib/rspec/matchers/pretty.rb +70 -0
  127. data/vendor_lib/rspec/matchers/test_unit_integration.rb +11 -0
  128. data/vendor_lib/rspec/mocks.rb +100 -0
  129. data/vendor_lib/rspec/mocks/any_instance/chain.rb +92 -0
  130. data/vendor_lib/rspec/mocks/any_instance/expectation_chain.rb +47 -0
  131. data/vendor_lib/rspec/mocks/any_instance/message_chains.rb +75 -0
  132. data/vendor_lib/rspec/mocks/any_instance/recorder.rb +200 -0
  133. data/vendor_lib/rspec/mocks/any_instance/stub_chain.rb +45 -0
  134. data/vendor_lib/rspec/mocks/any_instance/stub_chain_chain.rb +23 -0
  135. data/vendor_lib/rspec/mocks/argument_list_matcher.rb +104 -0
  136. data/vendor_lib/rspec/mocks/argument_matchers.rb +264 -0
  137. data/vendor_lib/rspec/mocks/arity_calculator.rb +66 -0
  138. data/vendor_lib/rspec/mocks/configuration.rb +111 -0
  139. data/vendor_lib/rspec/mocks/error_generator.rb +203 -0
  140. data/vendor_lib/rspec/mocks/errors.rb +12 -0
  141. data/vendor_lib/rspec/mocks/example_methods.rb +201 -0
  142. data/vendor_lib/rspec/mocks/extensions/marshal.rb +17 -0
  143. data/vendor_lib/rspec/mocks/framework.rb +36 -0
  144. data/vendor_lib/rspec/mocks/instance_method_stasher.rb +112 -0
  145. data/vendor_lib/rspec/mocks/matchers/have_received.rb +99 -0
  146. data/vendor_lib/rspec/mocks/matchers/receive.rb +112 -0
  147. data/vendor_lib/rspec/mocks/matchers/receive_messages.rb +72 -0
  148. data/vendor_lib/rspec/mocks/message_expectation.rb +643 -0
  149. data/vendor_lib/rspec/mocks/method_double.rb +209 -0
  150. data/vendor_lib/rspec/mocks/method_reference.rb +95 -0
  151. data/vendor_lib/rspec/mocks/mock.rb +7 -0
  152. data/vendor_lib/rspec/mocks/mutate_const.rb +406 -0
  153. data/vendor_lib/rspec/mocks/object_reference.rb +90 -0
  154. data/vendor_lib/rspec/mocks/order_group.rb +82 -0
  155. data/vendor_lib/rspec/mocks/proxy.rb +269 -0
  156. data/vendor_lib/rspec/mocks/proxy_for_nil.rb +37 -0
  157. data/vendor_lib/rspec/mocks/space.rb +95 -0
  158. data/vendor_lib/rspec/mocks/standalone.rb +3 -0
  159. data/vendor_lib/rspec/mocks/stub_chain.rb +51 -0
  160. data/vendor_lib/rspec/mocks/syntax.rb +374 -0
  161. data/vendor_lib/rspec/mocks/targets.rb +90 -0
  162. data/vendor_lib/rspec/mocks/test_double.rb +109 -0
  163. data/vendor_lib/rspec/mocks/verifying_double.rb +77 -0
  164. data/vendor_lib/rspec/mocks/verifying_message_expecation.rb +60 -0
  165. data/vendor_lib/rspec/mocks/verifying_proxy.rb +151 -0
  166. data/vendor_lib/rspec/mocks/version.rb +7 -0
  167. data/vendor_lib/rspec/support.rb +6 -0
  168. data/vendor_lib/rspec/support/caller_filter.rb +56 -0
  169. data/vendor_lib/rspec/support/spec.rb +14 -0
  170. data/vendor_lib/rspec/support/spec/deprecation_helpers.rb +29 -0
  171. data/vendor_lib/rspec/support/spec/in_sub_process.rb +40 -0
  172. data/vendor_lib/rspec/support/spec/stderr_splitter.rb +50 -0
  173. data/vendor_lib/rspec/support/version.rb +7 -0
  174. data/vendor_lib/rspec/support/warnings.rb +41 -0
  175. data/vendor_lib/rspec/version.rb +5 -0
  176. metadata +268 -0
@@ -0,0 +1,431 @@
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
+ # @note Because `subject` is designed to create state that is reset between
40
+ # each example, and `before(:all)` is designed to setup state that is
41
+ # shared across _all_ examples in an example group, `subject` is _not_
42
+ # intended to be used in a `before(:all)` hook. RSpec 2.13.1 prints
43
+ # a warning when you reference a `subject` from `before(:all)` and we plan
44
+ # to have it raise an error in RSpec 3.
45
+ #
46
+ # @see #should
47
+ def subject
48
+ __memoized.fetch(:subject) do
49
+ __memoized[:subject] = begin
50
+ described = described_class || self.class.description
51
+ Class === described ? described.new : described
52
+ end
53
+ end
54
+ end
55
+
56
+ # When `should` is called with no explicit receiver, the call is
57
+ # delegated to the object returned by `subject`. Combined with an
58
+ # implicit subject this supports very concise expressions.
59
+ #
60
+ # @example
61
+ #
62
+ # describe Person do
63
+ # it { should be_eligible_to_vote }
64
+ # end
65
+ #
66
+ # @see #subject
67
+ def should(matcher=nil, message=nil)
68
+ RSpec::Expectations::PositiveExpectationHandler.handle_matcher(subject, matcher, message)
69
+ end
70
+
71
+ # Just like `should`, `should_not` delegates to the subject (implicit or
72
+ # explicit) of the example group.
73
+ #
74
+ # @example
75
+ #
76
+ # describe Person do
77
+ # it { should_not be_eligible_to_vote }
78
+ # end
79
+ #
80
+ # @see #subject
81
+ def should_not(matcher=nil, message=nil)
82
+ RSpec::Expectations::NegativeExpectationHandler.handle_matcher(subject, matcher, message)
83
+ end
84
+
85
+ private
86
+
87
+ # @private
88
+ def __memoized
89
+ @__memoized ||= {}
90
+ end
91
+
92
+ # Used internally to customize the behavior of the
93
+ # memoized hash when used in a `before(:all)` hook.
94
+ #
95
+ # @private
96
+ class AllHookMemoizedHash
97
+ def self.isolate_for_all_hook(example_group_instance)
98
+ hash = self
99
+
100
+ example_group_instance.instance_eval do
101
+ @__memoized = hash
102
+
103
+ begin
104
+ yield
105
+ ensure
106
+ @__memoized = nil
107
+ end
108
+ end
109
+ end
110
+
111
+ def self.fetch(key, &block)
112
+ description = if key == :subject
113
+ "subject"
114
+ else
115
+ "let declaration `#{key}`"
116
+ end
117
+
118
+ raise <<-EOS
119
+ #{description} accessed in #{article} #{hook_expression} hook at:
120
+ #{CallerFilter.first_non_rspec_line}
121
+
122
+ `let` and `subject` declarations are not intended to be called
123
+ in #{article} #{hook_expression} hook, as they exist to define state that
124
+ is reset between each example, while #{hook_expression} exists to
125
+ #{hook_intention}.
126
+ EOS
127
+ end
128
+
129
+ class Before < self
130
+ def self.hook_expression
131
+ "`before(:all)`"
132
+ end
133
+
134
+ def self.article
135
+ "a"
136
+ end
137
+
138
+ def self.hook_intention
139
+ "define state that is shared across examples in an example group"
140
+ end
141
+ end
142
+
143
+ class After < self
144
+ def self.hook_expression
145
+ "`after(:all)`"
146
+ end
147
+
148
+ def self.article
149
+ "an"
150
+ end
151
+
152
+ def self.hook_intention
153
+ "cleanup state that is shared across examples in an example group"
154
+ end
155
+ end
156
+ end
157
+
158
+ def self.included(mod)
159
+ mod.extend(ClassMethods)
160
+ end
161
+
162
+ module ClassMethods
163
+ # Generates a method whose return value is memoized after the first
164
+ # call. Useful for reducing duplication between examples that assign
165
+ # values to the same local variable.
166
+ #
167
+ # @note `let` _can_ enhance readability when used sparingly (1,2, or
168
+ # maybe 3 declarations) in any given example group, but that can
169
+ # quickly degrade with overuse. YMMV.
170
+ #
171
+ # @note `let` uses an `||=` conditional that has the potential to
172
+ # behave in surprising ways in examples that spawn separate threads,
173
+ # though we have yet to see this in practice. You've been warned.
174
+ #
175
+ # @note Because `let` is designed to create state that is reset between
176
+ # each example, and `before(:all)` is designed to setup state that is
177
+ # shared across _all_ examples in an example group, `let` is _not_
178
+ # intended to be used in a `before(:all)` hook. RSpec 2.13.1 prints
179
+ # a warning when you reference a `let` from `before(:all)` and we plan
180
+ # to have it raise an error in RSpec 3.
181
+ #
182
+ # @example
183
+ #
184
+ # describe Thing do
185
+ # let(:thing) { Thing.new }
186
+ #
187
+ # it "does something" do
188
+ # # first invocation, executes block, memoizes and returns result
189
+ # thing.do_something
190
+ #
191
+ # # second invocation, returns the memoized value
192
+ # thing.should be_something
193
+ # end
194
+ # end
195
+ def let(name, &block)
196
+ # We have to pass the block directly to `define_method` to
197
+ # allow it to use method constructs like `super` and `return`.
198
+ raise "#let or #subject called without a block" if block.nil?
199
+ MemoizedHelpers.module_for(self).send(:define_method, name, &block)
200
+
201
+ # Apply the memoization. The method has been defined in an ancestor
202
+ # module so we can use `super` here to get the value.
203
+ if block.arity == 1
204
+ define_method(name) { __memoized.fetch(name) { |k| __memoized[k] = super(RSpec.current_example, &nil) } }
205
+ else
206
+ define_method(name) { __memoized.fetch(name) { |k| __memoized[k] = super(&nil) } }
207
+ end
208
+ end
209
+
210
+ # Just like `let`, except the block is invoked by an implicit `before`
211
+ # hook. This serves a dual purpose of setting up state and providing a
212
+ # memoized reference to that state.
213
+ #
214
+ # @example
215
+ #
216
+ # class Thing
217
+ # def self.count
218
+ # @count ||= 0
219
+ # end
220
+ #
221
+ # def self.count=(val)
222
+ # @count += val
223
+ # end
224
+ #
225
+ # def self.reset_count
226
+ # @count = 0
227
+ # end
228
+ #
229
+ # def initialize
230
+ # self.class.count += 1
231
+ # end
232
+ # end
233
+ #
234
+ # describe Thing do
235
+ # after(:each) { Thing.reset_count }
236
+ #
237
+ # context "using let" do
238
+ # let(:thing) { Thing.new }
239
+ #
240
+ # it "is not invoked implicitly" do
241
+ # Thing.count.should eq(0)
242
+ # end
243
+ #
244
+ # it "can be invoked explicitly" do
245
+ # thing
246
+ # Thing.count.should eq(1)
247
+ # end
248
+ # end
249
+ #
250
+ # context "using let!" do
251
+ # let!(:thing) { Thing.new }
252
+ #
253
+ # it "is invoked implicitly" do
254
+ # Thing.count.should eq(1)
255
+ # end
256
+ #
257
+ # it "returns memoized version on first invocation" do
258
+ # thing
259
+ # Thing.count.should eq(1)
260
+ # end
261
+ # end
262
+ # end
263
+ def let!(name, &block)
264
+ let(name, &block)
265
+ before { __send__(name) }
266
+ end
267
+
268
+ # Declares a `subject` for an example group which can then be the
269
+ # implicit receiver (through delegation) of calls to `should`.
270
+ #
271
+ # Given a `name`, defines a method with that name which returns the
272
+ # `subject`. This lets you declare the subject once and access it
273
+ # implicitly in one-liners and explicitly using an intention revealing
274
+ # name.
275
+ #
276
+ # @param [String,Symbol] name used to define an accessor with an
277
+ # intention revealing name
278
+ # @param block defines the value to be returned by `subject` in examples
279
+ #
280
+ # @example
281
+ #
282
+ # describe CheckingAccount, "with $50" do
283
+ # subject { CheckingAccount.new(Money.new(50, :USD)) }
284
+ # it { should have_a_balance_of(Money.new(50, :USD)) }
285
+ # it { should_not be_overdrawn }
286
+ # end
287
+ #
288
+ # describe CheckingAccount, "with a non-zero starting balance" do
289
+ # subject(:account) { CheckingAccount.new(Money.new(50, :USD)) }
290
+ # it { should_not be_overdrawn }
291
+ # it "has a balance equal to the starting balance" do
292
+ # account.balance.should eq(Money.new(50, :USD))
293
+ # end
294
+ # end
295
+ #
296
+ # @see MemoizedHelpers#should
297
+ def subject(name=nil, &block)
298
+ if name
299
+ let(name, &block)
300
+ alias_method :subject, name
301
+
302
+ self::NamedSubjectPreventSuper.send(:define_method, name) do
303
+ raise NotImplementedError, "`super` in named subjects is not supported"
304
+ end
305
+ else
306
+ let(:subject, &block)
307
+ end
308
+ end
309
+
310
+ # Just like `subject`, except the block is invoked by an implicit `before`
311
+ # hook. This serves a dual purpose of setting up state and providing a
312
+ # memoized reference to that state.
313
+ #
314
+ # @example
315
+ #
316
+ # class Thing
317
+ # def self.count
318
+ # @count ||= 0
319
+ # end
320
+ #
321
+ # def self.count=(val)
322
+ # @count += val
323
+ # end
324
+ #
325
+ # def self.reset_count
326
+ # @count = 0
327
+ # end
328
+ #
329
+ # def initialize
330
+ # self.class.count += 1
331
+ # end
332
+ # end
333
+ #
334
+ # describe Thing do
335
+ # after(:each) { Thing.reset_count }
336
+ #
337
+ # context "using subject" do
338
+ # subject { Thing.new }
339
+ #
340
+ # it "is not invoked implicitly" do
341
+ # Thing.count.should eq(0)
342
+ # end
343
+ #
344
+ # it "can be invoked explicitly" do
345
+ # subject
346
+ # Thing.count.should eq(1)
347
+ # end
348
+ # end
349
+ #
350
+ # context "using subject!" do
351
+ # subject!(:thing) { Thing.new }
352
+ #
353
+ # it "is invoked implicitly" do
354
+ # Thing.count.should eq(1)
355
+ # end
356
+ #
357
+ # it "returns memoized version on first invocation" do
358
+ # subject
359
+ # Thing.count.should eq(1)
360
+ # end
361
+ # end
362
+ # end
363
+ def subject!(name=nil, &block)
364
+ subject(name, &block)
365
+ before { subject }
366
+ end
367
+ end
368
+
369
+ # @api private
370
+ #
371
+ # Gets the LetDefinitions module. The module is mixed into
372
+ # the example group and is used to hold all let definitions.
373
+ # This is done so that the block passed to `let` can be
374
+ # forwarded directly on to `define_method`, so that all method
375
+ # constructs (including `super` and `return`) can be used in
376
+ # a `let` block.
377
+ #
378
+ # The memoization is provided by a method definition on the
379
+ # example group that supers to the LetDefinitions definition
380
+ # in order to get the value to memoize.
381
+ def self.module_for(example_group)
382
+ get_constant_or_yield(example_group, :LetDefinitions) do
383
+ mod = Module.new do
384
+ include Module.new {
385
+ example_group.const_set(:NamedSubjectPreventSuper, self)
386
+ }
387
+ end
388
+
389
+ example_group.const_set(:LetDefinitions, mod)
390
+ mod
391
+ end
392
+ end
393
+
394
+ # @api private
395
+ def self.define_helpers_on(example_group)
396
+ example_group.send(:include, module_for(example_group))
397
+ end
398
+
399
+ if Module.method(:const_defined?).arity == 1 # for 1.8
400
+ # @api private
401
+ #
402
+ # Gets the named constant or yields.
403
+ # On 1.8, const_defined? / const_get do not take into
404
+ # account the inheritance hierarchy.
405
+ def self.get_constant_or_yield(example_group, name)
406
+ if example_group.const_defined?(name)
407
+ example_group.const_get(name)
408
+ else
409
+ yield
410
+ end
411
+ end
412
+ else
413
+ # @api private
414
+ #
415
+ # Gets the named constant or yields.
416
+ # On 1.9, const_defined? / const_get take into account the
417
+ # the inheritance by default, and accept an argument to
418
+ # disable this behavior. It's important that we don't
419
+ # consider inheritance here; each example group level that
420
+ # uses a `let` should get its own `LetDefinitions` module.
421
+ def self.get_constant_or_yield(example_group, name)
422
+ if example_group.const_defined?(name, (check_ancestors = false))
423
+ example_group.const_get(name, check_ancestors)
424
+ else
425
+ yield
426
+ end
427
+ end
428
+ end
429
+ end
430
+ end
431
+ end
@@ -0,0 +1,313 @@
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
+ class Metadata < Hash
28
+
29
+ def self.relative_path(line)
30
+ line = line.sub(File.expand_path("."), ".")
31
+ line = line.sub(/\A([^:]+:\d+)$/, '\\1')
32
+ return nil if line == '-e:1'
33
+ line
34
+ rescue SecurityError
35
+ nil
36
+ end
37
+
38
+ # @private
39
+ # Used internally to build a hash from an args array.
40
+ # Symbols are converted into hash keys with a value of `true`.
41
+ # This is done to support simple tagging using a symbol, rather
42
+ # than needing to do `:symbol => true`.
43
+ def self.build_hash_from(args)
44
+ hash = args.last.is_a?(Hash) ? args.pop : {}
45
+
46
+ while args.last.is_a?(Symbol)
47
+ hash[args.pop] = true
48
+ end
49
+
50
+ hash
51
+ end
52
+
53
+ module MetadataHash
54
+
55
+ # @private
56
+ # Supports lazy evaluation of some values. Extended by
57
+ # ExampleMetadataHash and GroupMetadataHash, which get mixed in to
58
+ # Metadata for ExampleGroups and Examples (respectively).
59
+ def [](key)
60
+ store_computed(key) unless has_key?(key)
61
+ super
62
+ end
63
+
64
+ def fetch(key, *args)
65
+ store_computed(key) unless has_key?(key)
66
+ super
67
+ end
68
+
69
+ private
70
+
71
+ def store_computed(key)
72
+ case key
73
+ when :location
74
+ store(:location, location)
75
+ when :file_path, :line_number
76
+ file_path, line_number = file_and_line_number
77
+ store(:file_path, file_path)
78
+ store(:line_number, line_number)
79
+ when :execution_result
80
+ store(:execution_result, {})
81
+ when :describes, :described_class
82
+ klass = described_class
83
+ store(:described_class, klass)
84
+ # TODO (2011-11-07 DC) deprecate :describes as a key
85
+ store(:describes, klass)
86
+ when :full_description
87
+ store(:full_description, full_description)
88
+ when :description
89
+ store(:description, build_description_from(*self[:description_args]))
90
+ when :description_args
91
+ store(:description_args, [])
92
+ end
93
+ end
94
+
95
+ def location
96
+ "#{self[:file_path]}:#{self[:line_number]}"
97
+ end
98
+
99
+ def file_and_line_number
100
+ first_caller_from_outside_rspec =~ /(.+?):(\d+)(|:\d+)/
101
+ return [Metadata::relative_path($1), $2.to_i]
102
+ end
103
+
104
+ def first_caller_from_outside_rspec
105
+ self[:caller].detect {|l| l !~ /\/lib\/rspec\/core/}
106
+ end
107
+
108
+ def method_description_after_module?(parent_part, child_part)
109
+ return false unless parent_part.is_a?(Module)
110
+ child_part =~ /^(#|::|\.)/
111
+ end
112
+
113
+ def build_description_from(first_part = '', *parts)
114
+ description, _ = parts.inject([first_part.to_s, first_part]) do |(desc, last_part), this_part|
115
+ this_part = this_part.to_s
116
+ this_part = (' ' + this_part) unless method_description_after_module?(last_part, this_part)
117
+ [(desc + this_part), this_part]
118
+ end
119
+
120
+ description
121
+ end
122
+ end
123
+
124
+ # Mixed in to Metadata for an Example (extends MetadataHash) to support
125
+ # lazy evaluation of some values.
126
+ module ExampleMetadataHash
127
+ include MetadataHash
128
+
129
+ def described_class
130
+ self[:example_group].described_class
131
+ end
132
+
133
+ def full_description
134
+ build_description_from(self[:example_group][:full_description], *self[:description_args])
135
+ end
136
+ end
137
+
138
+ # Mixed in to Metadata for an ExampleGroup (extends MetadataHash) to
139
+ # support lazy evaluation of some values.
140
+ module GroupMetadataHash
141
+ include MetadataHash
142
+
143
+ def described_class
144
+ container_stack.each do |g|
145
+ [:described_class, :describes].each do |key|
146
+ if g.has_key?(key)
147
+ value = g[key]
148
+ return value unless value.nil?
149
+ end
150
+ end
151
+ end
152
+
153
+ container_stack.reverse.each do |g|
154
+ candidate = g[:description_args].first
155
+ return candidate unless String === candidate || Symbol === candidate
156
+ end
157
+
158
+ nil
159
+ end
160
+
161
+ def full_description
162
+ build_description_from(*FlatMap.flat_map(container_stack.reverse) {|a| a[:description_args]})
163
+ end
164
+
165
+ def container_stack
166
+ @container_stack ||= begin
167
+ groups = [group = self]
168
+ while group.has_key?(:example_group)
169
+ groups << group[:example_group]
170
+ group = group[:example_group]
171
+ end
172
+ groups
173
+ end
174
+ end
175
+ end
176
+
177
+ def initialize(parent_group_metadata=nil)
178
+ if parent_group_metadata
179
+ update(parent_group_metadata)
180
+ store(:example_group, {:example_group => parent_group_metadata[:example_group].extend(GroupMetadataHash)}.extend(GroupMetadataHash))
181
+ else
182
+ store(:example_group, {}.extend(GroupMetadataHash))
183
+ end
184
+
185
+ yield self if block_given?
186
+ end
187
+
188
+ # @private
189
+ def process(*args)
190
+ user_metadata = args.last.is_a?(Hash) ? args.pop : {}
191
+ ensure_valid_keys(user_metadata)
192
+
193
+ self[:example_group].store(:description_args, args)
194
+ self[:example_group].store(:caller, user_metadata.delete(:caller) || caller)
195
+
196
+ update(user_metadata)
197
+ end
198
+
199
+ # @private
200
+ def for_example(description, user_metadata)
201
+ dup.extend(ExampleMetadataHash).configure_for_example(description, user_metadata)
202
+ end
203
+
204
+ # @private
205
+ def any_apply?(filters)
206
+ filters.any? {|k,v| filter_applies?(k,v)}
207
+ end
208
+
209
+ # @private
210
+ def all_apply?(filters)
211
+ filters.all? {|k,v| filter_applies?(k,v)}
212
+ end
213
+
214
+ # @private
215
+ def filter_applies?(key, value, metadata=self)
216
+ return metadata.filter_applies_to_any_value?(key, value) if Array === metadata[key] && !(Proc === value)
217
+ return metadata.line_number_filter_applies?(value) if key == :line_numbers
218
+ return metadata.location_filter_applies?(value) if key == :locations
219
+ return metadata.filters_apply?(key, value) if Hash === value
220
+
221
+ return false unless metadata.has_key?(key)
222
+
223
+ case value
224
+ when Regexp
225
+ metadata[key] =~ value
226
+ when Proc
227
+ case value.arity
228
+ when 0 then value.call
229
+ when 2 then value.call(metadata[key], metadata)
230
+ else value.call(metadata[key])
231
+ end
232
+ else
233
+ metadata[key].to_s == value.to_s
234
+ end
235
+ end
236
+
237
+ # @private
238
+ def filters_apply?(key, value)
239
+ value.all? {|k, v| filter_applies?(k, v, self[key])}
240
+ end
241
+
242
+ # @private
243
+ def filter_applies_to_any_value?(key, value)
244
+ self[key].any? {|v| filter_applies?(key, v, {key => value})}
245
+ end
246
+
247
+ # @private
248
+ def location_filter_applies?(locations)
249
+ # it ignores location filters for other files
250
+ line_number = example_group_declaration_line(locations)
251
+ line_number ? line_number_filter_applies?(line_number) : true
252
+ end
253
+
254
+ # @private
255
+ def line_number_filter_applies?(line_numbers)
256
+ preceding_declaration_lines = line_numbers.map {|n| RSpec.world.preceding_declaration_line(n)}
257
+ !(relevant_line_numbers & preceding_declaration_lines).empty?
258
+ end
259
+
260
+ protected
261
+
262
+ def configure_for_example(description, user_metadata)
263
+ store(:description_args, [description]) if description
264
+ store(:caller, user_metadata.delete(:caller) || caller)
265
+ update(user_metadata)
266
+ end
267
+
268
+ private
269
+
270
+ RESERVED_KEYS = [
271
+ :description,
272
+ :example_group,
273
+ :execution_result,
274
+ :file_path,
275
+ :full_description,
276
+ :line_number,
277
+ :location
278
+ ]
279
+
280
+ def ensure_valid_keys(user_metadata)
281
+ RESERVED_KEYS.each do |key|
282
+ if user_metadata.has_key?(key)
283
+ raise <<-EOM
284
+ #{"*"*50}
285
+ :#{key} is not allowed
286
+
287
+ RSpec reserves some hash keys for its own internal use,
288
+ including :#{key}, which is used on:
289
+
290
+ #{CallerFilter.first_non_rspec_line}.
291
+
292
+ Here are all of RSpec's reserved hash keys:
293
+
294
+ #{RESERVED_KEYS.join("\n ")}
295
+ #{"*"*50}
296
+ EOM
297
+ end
298
+ end
299
+ end
300
+
301
+ def example_group_declaration_line(locations)
302
+ locations[File.expand_path(self[:example_group][:file_path])] if self[:example_group]
303
+ end
304
+
305
+ # TODO - make this a method on metadata - the problem is
306
+ # metadata[:example_group] is not always a kind of GroupMetadataHash.
307
+ def relevant_line_numbers(metadata=self)
308
+ [metadata[:line_number]] + (metadata[:example_group] ? relevant_line_numbers(metadata[:example_group]) : [])
309
+ end
310
+
311
+ end
312
+ end
313
+ end