rspec-given 2.2.1 → 2.3.0.beta.1

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile CHANGED
@@ -1,3 +1,10 @@
1
1
  source 'https://rubygems.org'
2
2
  gem 'rspec', '>= 2.12'
3
3
  gem 'rake', '>= 0.9.2.2'
4
+ gem 'sorcerer'
5
+
6
+ group :dev do
7
+ gem 'bluecloth'
8
+ gem 'ghpreview'
9
+ gem 'flog'
10
+ end
data/Gemfile.lock CHANGED
@@ -1,8 +1,19 @@
1
1
  GEM
2
2
  remote: https://rubygems.org/
3
3
  specs:
4
+ bluecloth (2.2.0)
4
5
  diff-lcs (1.1.3)
6
+ flog (3.2.1)
7
+ ruby_parser (~> 3.1, > 3.1.0)
8
+ sexp_processor (~> 4.0)
9
+ ghpreview (0.0.2)
10
+ httpclient
11
+ listen
12
+ rb-fsevent
13
+ httpclient (2.3.0.1)
14
+ listen (0.6.0)
5
15
  rake (0.9.2.2)
16
+ rb-fsevent (0.9.2)
6
17
  rspec (2.12.0)
7
18
  rspec-core (~> 2.12.0)
8
19
  rspec-expectations (~> 2.12.0)
@@ -11,10 +22,18 @@ GEM
11
22
  rspec-expectations (2.12.0)
12
23
  diff-lcs (~> 1.1.3)
13
24
  rspec-mocks (2.12.0)
25
+ ruby_parser (3.1.1)
26
+ sexp_processor (~> 4.1)
27
+ sexp_processor (4.1.3)
28
+ sorcerer (0.3.7)
14
29
 
15
30
  PLATFORMS
16
31
  ruby
17
32
 
18
33
  DEPENDENCIES
34
+ bluecloth
35
+ flog
36
+ ghpreview
19
37
  rake (>= 0.9.2.2)
20
38
  rspec (>= 2.12)
39
+ sorcerer
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # rspec-given
2
2
 
3
- Covering rspec-given, version 2.2.1.
3
+ Covering rspec-given, version 3.0.0.beta.1.
4
4
 
5
5
  rspec-given is an RSpec extension to allow Given/When/Then notation in
6
6
  RSpec specifications. It is a natural extension of the experimental
@@ -232,7 +232,7 @@ Let me repeat that: <b>_Then_ clauses should not have any side
232
232
  effects!</b> _Then_ clauses with side effects are erroneous. _Then_
233
233
  clauses need to be idempotent, so that running them once, twice, a
234
234
  hundred times, or never does not change the state of the program. (The
235
- same is true of _And_ clauses).
235
+ same is true of _And_ and _Invariant_ clauses).
236
236
 
237
237
  In RSpec terms, a _Then_ clause forms a RSpec Example that runs in the
238
238
  context of an Example Group (defined by a describe or context clause).
@@ -261,7 +261,7 @@ The _And_ clause is similar to _Then_, but does not form its own RSpec
261
261
  example. This means that _And_ clauses reuse the setup from a sibling
262
262
  _Then_ clause. Using a single _Then_ an multiple _And_ clauses in an
263
263
  example group means the setup for that group is run only once (for the
264
- _Then_ clause) and reused for all the _And_s. This can be a
264
+ _Then_ clause) and reused for all the _And_ clauses. This can be a
265
265
  significant speed savings where the setup for an example group is
266
266
  expensive.
267
267
 
@@ -325,6 +325,194 @@ Notes:
325
325
  represented in the RSpec formatted output (e.g. the '--format html'
326
326
  option).
327
327
 
328
+ ## Natural Assertions
329
+
330
+ **NOTE:** <em>Natural assertions are an experimental feature of
331
+ RSpec/Given. They are currently disabled by default.</em>
332
+
333
+ RSpec/Given now supports the use of "natural assertions" in _Then_,
334
+ _And_, and _Invariant_ blocks. Natural assertions are just Ruby
335
+ conditionals, without the _should_ or _expect_ methods that RSpec
336
+ provides. Here are the Then/And examples from above, but written using
337
+ natural assertions:
338
+
339
+ ```ruby
340
+ Then { pop_result == :top_item }
341
+ And { stack.top == :second_item }
342
+ And { stack.depth == original_depth - 1 }
343
+ ```
344
+
345
+ Natural assertions must be enabled, either globally or on a per
346
+ context basis, to be recognized.
347
+
348
+ ### Failure Messages with Natural Assertions
349
+
350
+ Since natural assertions do not depend upon matchers, you don't get
351
+ customized error messages from them. What you _do_ get is a complete
352
+ analsysis of the expression that failed.
353
+
354
+ For example, given the following failing specification:
355
+
356
+ ```ruby
357
+ RSpec::Given.use_natural_assertions
358
+
359
+ describe "Natural Assertions" do
360
+ Given(:foo) { 1 }
361
+ Given(:bar) { 2 }
362
+ Then { foo + bar == 2 }
363
+ end
364
+ ```
365
+
366
+ You would get:
367
+
368
+ ```
369
+ 1) Natural Assertions
370
+ Failure/Error: Then { foo + bar == 2 }
371
+ Then expression failed at /Users/jim/working/git/rspec-given/examples/failing/sample_spec.rb:6
372
+ expected: 3
373
+ to equal: 2
374
+ false <- foo + bar == 2
375
+ 3 <- foo + bar
376
+ 1 <- foo
377
+ 2 <- bar
378
+ # ./examples/failing/sample_spec.rb:6:in `block in Then'
379
+ ```
380
+
381
+ Notice how the failing expression "<code>foo+bar == 2</code>" was
382
+ broken down into subexpressions and values for each subexpression.
383
+ This gives you all the information you need to figure out exactly what
384
+ part of the expression is causing the failure.
385
+
386
+ Natural expressions will give additional information (e.g. "expected:
387
+ 3 to equal: 2") for top level expressions involving any of the
388
+ comparison operators (==, !=, <, <=, >, >=) or matching operators (=~,
389
+ !~).
390
+
391
+ ### Caveats on Natural Assertions
392
+
393
+ Keep the following in mind when using Natural Assertions.
394
+
395
+ * Only a single assertion per _Then_. Only the final expression of the
396
+ _Then_ block will be considered when determining pass/fail for the
397
+ assertion. If you _want_ to express a complex condition for the
398
+ _Then_, you need to use ||, && or some other logical operation to
399
+ join the conditions into a single expression (and the failure
400
+ message will breakdown the values for each part).
401
+
402
+ * Natural assertions must be **idempotent** (that means they don't
403
+ change anything). Since the natural assertion error message contains
404
+ the values of all the subexpressions, the expression and its
405
+ subexpressions will be evaluated multiple times. If the block is not
406
+ idempotent, you will get changing answers as the subexpressions are
407
+ evaluated.
408
+
409
+ That last point is important. If you write code like this:
410
+
411
+ ```ruby
412
+ # DO NOT WRITE CODE LIKE THIS
413
+ context "Incorrect non-idempotent conditions" do
414
+ Given(:ary) { [1, 2, 3] }
415
+ Then { ary.delete(1) == nil }
416
+ end
417
+ ```
418
+
419
+ Then the assertion will fail (because <code>ary.delete(1)</code> will
420
+ initially return 1). But when the error message is formated, the
421
+ system reports that <code>ary.delete(1)</code> returns nil. You will
422
+ scratch your head over that for a good while.
423
+
424
+ Instead, move the state changing code into a When block, then just
425
+ assert what you need about the result from the when block. Something
426
+ like this is good:
427
+
428
+ ```ruby
429
+ context "Correct idempotent conditions" do
430
+ Given(:ary) { [1, 2, 3] }
431
+ When(:result) { ary.delete(1) }
432
+ Then { result == nil }
433
+ end
434
+ ```
435
+
436
+ ### Mixing Natural Assertions and RSpec Assertions
437
+
438
+ Natural assertions and RSpec assertions for the most part can be
439
+ intermixed in a single test suite, even within a single context.
440
+ Because there are a few corner cases that might cause problems, they
441
+ must be explicitly enabled before they will be considered.
442
+
443
+ To enable natural assertions in a context, call the
444
+ _use_natural_assertions_ method in that context. For example:
445
+
446
+ ```ruby
447
+ context "Outer" do
448
+ use_natural_assertions
449
+
450
+ context "Inner" do
451
+ end
452
+
453
+ context "Disabled" do
454
+ use_natural_assertions false
455
+ end
456
+ end
457
+ ```
458
+
459
+ Both the _Outer_ and _Inner_ contexts will use natural assertions. The
460
+ _Disabled_ context overrides the setting inherited from _Outer_ and
461
+ will not process natural assertions.
462
+
463
+ See the **configuration** section below to see how to enable natural
464
+ assertions project wide.
465
+
466
+ ### Processing Natural Assertions
467
+
468
+ When natural assertions are enabled, they are only used if:
469
+
470
+ 1. The block does not throw an RSpec assertion failure (or any other
471
+ exception for that matter).
472
+
473
+ 1. The block returns false (blocks that return true pass the
474
+ assertion and don't need a failure message).
475
+
476
+ 1. The block does not directly use RSpec's _should_ or _expect_
477
+ method.
478
+
479
+ Detecting that last point (the use of _should_ and _expect_) is done
480
+ by examining the source of the block. However it is not always
481
+ possible to correctly determine if _should_ and _expect_ are used
482
+ merely from the source. Fortunately you can always override the
483
+ automatic detection by either forcing natural assertions (with
484
+ :always) or disabling them (with false).
485
+
486
+ ```ruby
487
+ # Then uses a non-RSpec version of "should" on
488
+ context "sample" do
489
+ use_natural_assertions :always
490
+ Then { obj.should } # Non-rspec should is called
491
+ end
492
+ ```
493
+
494
+ ```ruby
495
+ # Then does not call should directly, so disable natural assertions
496
+ context "sample" do
497
+ use_natural_assertions false
498
+ Then { check_validations(obj) } # check_validations calls "should" internally
499
+ end
500
+
501
+ # You can also just not use a Then for that condition.
502
+ context "another sample" do
503
+ it "checks validations" do
504
+ check_validations(obj)
505
+ end
506
+ end
507
+ ```
508
+
509
+ ### Further Reading
510
+
511
+ Natural assertions were inspired by the [wrong assertion
512
+ library](http://rubygems.org/gems/wrong) by [Alex
513
+ Chaffee](http://rubygems.org/profiles/alexch) and [Steve
514
+ Conover](http://rubygems.org/profiles/sconoversf).
515
+
328
516
  ## Configuration
329
517
 
330
518
  Just require 'rspec/given' in the spec helper of your project and it
@@ -340,28 +528,18 @@ unconditionally, then add the following line to your spec helper file:
340
528
  RSpec::Given.source_caching_disabled = true
341
529
  ```
342
530
 
343
- # Future Directions
344
-
345
- I really like the way the Given framework is working out. I feel my
346
- tests are much more like specifications when I use it. However, I'm
347
- not entirely happy with it.
348
-
349
- I would like to remove the need for the ".should" in all the _Then_
350
- clauses. In other words, instead of saying:
531
+ Natural assertions are disabled by default. To globally configure
532
+ natural assertions, add one of the following lines to your spec_helper
533
+ file:
351
534
 
352
535
  ```ruby
353
- Then { x.should == y }
536
+ RSpec::Given.use_natural_assertions # Enable natural assertions
537
+ RSpec::Given.use_natural_assertions true # Same as above
538
+ RSpec::Given.use_natural_assertions false # Disable natural assertions
539
+ RSpec::Given.use_natural_assertions :always # Always process natural assertions
540
+ # ... even when should/expect are detected
354
541
  ```
355
542
 
356
- we could say:
357
-
358
- ```ruby
359
- Then { x == y }
360
- ```
361
-
362
- I think the [wrong assertion library](http://rubygems.org/gems/wrong)
363
- has laid some groundwork in this area.
364
-
365
543
  # Links
366
544
 
367
545
  * Github: [https://github.com/jimweirich/rspec-given](https://github.com/jimweirich/rspec-given)
data/Rakefile CHANGED
@@ -66,10 +66,19 @@ task :examples1 => [:verify_rspec1] do
66
66
  sh "spec examples/stack/stack_spec1.rb"
67
67
  end
68
68
 
69
+ EXAMPLES = FileList['examples/**/*_spec.rb'].exclude('examples/failing/*.rb')
70
+ FAILING_EXAMPLES = FileList['examples/failing/**/*_spec.rb']
71
+
69
72
  desc "Run the examples in RSpec 2"
70
73
  task :examples2 => [:verify_rspec2] do
71
74
  puts "Running examples (with RSpec2)"
72
- sh "rspec examples"
75
+ sh "rspec #{EXAMPLES}"
76
+ end
77
+
78
+ desc "Run failing examples"
79
+ task :failing => [:verify_rspec2] do
80
+ puts "Running failing examples (with RSpec2)"
81
+ sh "rspec #{FAILING_EXAMPLES}"
73
82
  end
74
83
 
75
84
  task :verify_rspec1 do
@@ -89,17 +98,8 @@ end
89
98
  directory 'html'
90
99
 
91
100
  desc "Display the README file"
92
- task :readme => "html/README.html" do
93
- sh "open html/README.html"
94
- end
95
-
96
- desc "format the README file"
97
- task "html/README.html" => ['html', 'README.md'] do
98
- open("README.md") do |source|
99
- open('html/README.html', 'w') do |out|
100
- out.write(BlueCloth.new(source.read).to_html)
101
- end
102
- end
101
+ task :readme => ["README.md"] do
102
+ sh "ghpreview README.md"
103
103
  end
104
104
 
105
105
  desc "Generate an RDoc README"
@@ -0,0 +1,36 @@
1
+ require 'rspec/given'
2
+ require 'rspec/given/natural_assertion'
3
+
4
+ describe "Natural Assertions" do
5
+ use_natural_assertions
6
+
7
+ Given(:foo) { 1 }
8
+ Given(:expected) { 2 }
9
+ Given(:ary) { [1] }
10
+ Given(:empty) { [] }
11
+ Given(:null) { nil }
12
+ Then { foo+foo+2*foo == expected }
13
+ Then { nil == "HI" && true && :symbol && 1}
14
+ Then { foo.should == 2 }
15
+ Then { foo != 1 }
16
+ Then { foo.should_not == 1 }
17
+ Then { foo.should be_nil }
18
+ Then { ary.empty? }
19
+ Then { !null.nil? }
20
+ Then { fail "OUCH" }
21
+ Then { ! empty.empty? }
22
+ Then {
23
+ (puts "Ha ha world", ! true)
24
+ }
25
+
26
+ context "Incorrect non-idempotent conditions" do
27
+ Given(:ary) { [1, 2, 3] }
28
+ Then { ary.delete(1) == nil }
29
+ end
30
+
31
+ context "Correct idempotent conditions" do
32
+ Given(:ary) { [1, 2, 3] }
33
+ When(:result) { ary.delete(1) }
34
+ Then { result == nil }
35
+ end
36
+ end
@@ -0,0 +1,7 @@
1
+ require 'rspec/given'
2
+
3
+ describe "Natural Assertions" do
4
+ Given(:foo) { 1 }
5
+ Given(:bar) { 2 }
6
+ Then { foo + bar == 2 }
7
+ end
@@ -0,0 +1,9 @@
1
+ require 'rspec/given'
2
+ require 'example_helper'
3
+
4
+ describe "Then" do
5
+ context "empty thens with natural assertions" do
6
+ use_natural_assertions
7
+ Then { }
8
+ end
9
+ end
data/lib/rspec/given.rb CHANGED
@@ -12,12 +12,7 @@ end
12
12
  if RSpec::Given.using_old_rspec?
13
13
  require 'rspec/given/rspec1_given'
14
14
  else
15
- require 'rspec/given/version'
16
- require 'rspec/given/module_methods'
17
- require 'rspec/given/file_cache'
18
- require 'rspec/given/line_extractor'
19
- require 'rspec/given/extensions'
20
- require 'rspec/given/configure'
21
- require 'rspec/given/failure'
15
+ require 'rspec/given/core'
22
16
  require 'rspec/given/have_failed'
17
+ require 'rspec/given/configure'
23
18
  end
@@ -8,5 +8,7 @@ RSpec.configure do |c|
8
8
  c.include(RSpec::Given::InstanceExtensions)
9
9
  c.include(RSpec::Given::HaveFailed)
10
10
 
11
+ c.backtrace_clean_patterns << /lib\/rspec\/given/
12
+
11
13
  RSpec::Given.detect_formatters(c)
12
14
  end
@@ -0,0 +1,9 @@
1
+
2
+ # Require all the core features
3
+
4
+ require 'rspec/given/version'
5
+ require 'rspec/given/module_methods'
6
+ require 'rspec/given/file_cache'
7
+ require 'rspec/given/line_extractor'
8
+ require 'rspec/given/extensions'
9
+ require 'rspec/given/failure'
@@ -1,5 +1,6 @@
1
1
  require 'rspec/given/failure'
2
2
  require 'rspec/given/module_methods'
3
+ require 'rspec/given/natural_assertion'
3
4
 
4
5
  module RSpec
5
6
  module Given
@@ -9,25 +10,47 @@ module RSpec
9
10
  # implementation-specific.
10
11
  module InstanceExtensions # :nodoc:
11
12
 
13
+ # List of containing contexts in order from innermost to
14
+ # outermost.
15
+ def _rg_inner_contexts # :nodoc:
16
+ self.class.ancestors.select { |context|
17
+ context.respond_to?(:_rg_givens)
18
+ }
19
+ end
20
+
12
21
  # List of containing contexts in order from outermost to
13
22
  # innermost.
14
23
  def _rg_contexts # :nodoc:
15
- self.class.ancestors.select { |context|
16
- context.respond_to?(:_rg_givens)
17
- }.reverse
24
+ _rg_inner_contexts.reverse
25
+ end
26
+
27
+ def _rg_info(keyword)
28
+ _rg_inner_contexts.each do |context|
29
+ h = context._rg_context_info
30
+ if h.has_key?(keyword)
31
+ return h[keyword]
32
+ end
33
+ end
34
+ nil
35
+ end
36
+
37
+ def _rg_natural_assertions?(nassert)
38
+ info_value = _rg_info(:natural_assertions_enabled)
39
+ use_na = info_value.nil? ? RSpec::Given.natural_assertions_enabled? : info_value
40
+ (! nassert.using_rspec_assertion? && use_na) || (use_na == :always)
18
41
  end
19
42
 
20
43
  # Establish all the Given preconditions the current and
21
44
  # surrounding describe/context blocks, starting with the
22
45
  # outermost context.
23
46
  def _rg_establish_givens # :nodoc:
24
- return if defined?(@_rg_ran)
47
+ return if defined?(@_rg_ran) && @_rg_ran
48
+ @_rg_ran = true
25
49
  _rg_contexts.each do |context|
26
50
  context._rg_givens.each do |block|
27
51
  instance_eval(&block)
28
52
  end
29
53
  end
30
- @_rg_ran = true
31
54
  end
32
55
 
33
56
  # Check all the invariants in the current and surrounding
@@ -35,7 +58,7 @@ module RSpec
35
58
  def _rg_check_invariants # :nodoc:
36
59
  _rg_contexts.each do |context|
37
60
  context._rg_invariants.each do |block|
38
- instance_eval(&block)
61
+ _rg_evaluate(block)
39
62
  end
40
63
  end
41
64
  end
@@ -43,7 +66,7 @@ module RSpec
43
66
  def _rg_check_ands # :nodoc:
44
67
  return if self.class._rg_context_info[:and_ran]
45
68
  self.class._rg_and_blocks.each do |block|
46
- instance_eval(&block)
69
+ _rg_evaluate(block)
47
70
  end
48
71
  self.class._rg_context_info[:and_ran] = true
49
72
  end
@@ -52,9 +75,18 @@ module RSpec
52
75
  def _rg_then(&block) # :nodoc:
53
76
  _rg_establish_givens
54
77
  _rg_check_invariants
55
- instance_eval(&block)
78
+ _rg_evaluate(block)
56
79
  _rg_check_ands
57
80
  end
81
+
82
+ def _rg_evaluate(block)
83
+ unless instance_eval(&block)
84
+ nassert = NaturalAssertion.new(block, binding, self.class._rg_lines)
85
+ if ! nassert.using_rspec_assertion? && nassert.has_content?
86
+ ::RSpec::Expectations.fail_with nassert.message
87
+ end
88
+ end
89
+ end
58
90
  end
59
91
 
60
92
  module ClassExtensions
@@ -76,7 +108,7 @@ module RSpec
76
108
  end
77
109
 
78
110
  def _rg_context_info
79
- @_rg_contet_info ||= {}
111
+ @_rg_context_info ||= {}
80
112
  end
81
113
 
82
114
  def _rg_lines
@@ -106,8 +138,7 @@ module RSpec
106
138
  # Scenario "a scenario description" do ... end
107
139
  #
108
140
  def Scenario(description, &block)
109
- line = eval("__LINE__", block.binding)
110
- file = eval("__FILE__", block.binding)
141
+ file, line = eval("[__LINE__, __FILE__]", block.binding)
111
142
  puts "WARNING: Scenario is deprecated, please use either describe or context (#{file}:#{line})"
112
143
  context(description, &block)
113
144
  end
@@ -119,8 +150,8 @@ module RSpec
119
150
  # every time the specification is executed.
120
151
  #
121
152
  # :call-seq:
122
- # Given(:name, &block)
123
- # Given(&block)
153
+ # Given(:name) { ... code ... }
154
+ # Given { ... code ... }
124
155
  #
125
156
  def Given(*args, &block)
126
157
  if args.first.is_a?(Symbol)
@@ -136,6 +167,7 @@ module RSpec
136
167
  #
137
168
  # :call-seq:
138
169
  # Given!(:name) { ... code ... }
170
+ #
139
171
  def Given!(name, &block)
140
172
  let!(name, &block)
141
173
  _rg_givens << _rg_trigger_given(name)
@@ -144,8 +176,8 @@ module RSpec
144
176
  # Declare the code that is under test.
145
177
  #
146
178
  # :call-seq:
147
- # When(:named_result, &block)
148
- # When(&block)
179
+ # When(:named_result) { ... code_under_test ... }
180
+ # When { ... code_under_test ... }
149
181
  #
150
182
  def When(*args, &block)
151
183
  if args.first.is_a?(Symbol)
@@ -170,10 +202,13 @@ module RSpec
170
202
  # Then supplies an assertion that should be true after all the
171
203
  # Given and When blocks have been run. All invariants in scope
172
204
  # will be checked before the Then block is run.
205
+ #
206
+ # :call-seq:
207
+ # Then { ... assertion ... }
208
+ #
173
209
  def Then(&block)
174
- b = block.binding
175
- file = eval "__FILE__", b
176
- line = eval "__LINE__", b
210
+ env = block.binding
211
+ file, line = eval "[__FILE__, __LINE__]", env
177
212
  description = _rg_lines.line(file, line) unless RSpec::Given.source_caching_disabled
178
213
  if description
179
214
  cmd = "it(description)"
@@ -194,6 +229,10 @@ module RSpec
194
229
  fail "And defined without a Then" unless _rg_context_info[:then_defined]
195
230
  _rg_and_blocks << block
196
231
  end
232
+
233
+ def use_natural_assertions(enabled=true)
234
+ _rg_context_info[:natural_assertions_enabled] = enabled
235
+ end
197
236
  end
198
237
  end
199
238
  end
@@ -12,5 +12,13 @@ module RSpec
12
12
  format_active = c.formatters.any? { |f| f.class.name !~ /ProgressFormatter/ }
13
13
  RSpec::Given.source_caching_disabled = ! format_active
14
14
  end
15
+
16
+ def self.use_natural_assertions(enabled=true)
17
+ @natural_assertions_enabled = enabled
18
+ end
19
+
20
+ def self.natural_assertions_enabled?
21
+ @natural_assertions_enabled
22
+ end
15
23
  end
16
24
  end
@@ -0,0 +1,178 @@
1
+ require 'ripper'
2
+ require 'sorcerer'
3
+
4
+ module RSpec
5
+ module Given
6
+
7
+ InvalidThenError = Class.new(StandardError)
8
+
9
+ class EvalErr
10
+ def initialize(str)
11
+ @string = str
12
+ end
13
+ def size
14
+ inspect.size
15
+ end
16
+ def to_s
17
+ @string
18
+ end
19
+ def inspect
20
+ @string
21
+ end
22
+ end
23
+
24
+ class NaturalAssertion
25
+
26
+ def initialize(block, env, line_extractor)
27
+ @block = block
28
+ @env = env
29
+ @line_extractor = line_extractor
30
+ set_file_and_line
31
+ end
32
+
33
+ VOID_SEXP = [:void_stmt]
34
+
35
+ def using_rspec_assertion?
36
+ using_should? || using_expect?
37
+ end
38
+
39
+ def has_content?
40
+ assertion_sexp != VOID_SEXP
41
+ end
42
+
43
+ def message
44
+ @output = "Then expression failed at #{source_line}\n"
45
+ explain_failure
46
+ display_pairs(expression_value_pairs)
47
+ @output << "\n"
48
+ @output
49
+ end
50
+
51
+ private
52
+
53
+ def using_should?
54
+ source =~ /\.\s*should(_not)?\b/
55
+ end
56
+
57
+ def using_expect?
58
+ source =~ /\bexpect\s*[({].*[)}]\s*\.\s*(not_)?to\b/
59
+ end
60
+
61
+ BINARY_EXPLAINATIONS = {
62
+ :== => "to equal",
63
+ :!= => "to not equal",
64
+ :< => "to be less than",
65
+ :<= => "to be less or equal to",
66
+ :> => "to be greater than",
67
+ :>= => "to be greater or equal to",
68
+ :=~ => "to match",
69
+ :!~ => "to not match",
70
+ }
71
+
72
+ def explain_failure
73
+ if assertion_sexp.first == :binary && msg = BINARY_EXPLAINATIONS[assertion_sexp[2]]
74
+ @output << explain_expected("expected", assertion_sexp[1], msg, assertion_sexp[3])
75
+ end
76
+ end
77
+
78
+ def explain_expected(expect_msg, expect_sexp, got_msg, got_sexp)
79
+ width = [expect_msg.size, got_msg.size].max
80
+ sprintf("%#{width}s: %s\n%#{width}s: %s\n",
81
+ expect_msg, eval_sexp(expect_sexp),
82
+ got_msg, eval_sexp(got_sexp))
83
+ end
84
+
85
+ def expression_value_pairs
86
+ assertion_subexpressions.map { |exp|
87
+ [exp, eval_in(exp, @env)]
88
+ }
89
+ end
90
+
91
+ def assertion_subexpressions
92
+ Sorcerer.subexpressions(assertion_sexp).reverse.uniq.reverse
93
+ end
94
+
95
+ def assertion_sexp
96
+ @assertion_sexp ||= extract_test_expression(Ripper::SexpBuilder.new(source).parse)
97
+ end
98
+
99
+ def source
100
+ @line_extractor.line(@code_file, @code_line)
101
+ end
102
+
103
+ def set_file_and_line
104
+ @code_file, @code_line = eval "[__FILE__, __LINE__]", @block.binding
105
+ @code_line = @code_line.to_i
106
+ end
107
+
108
+ def extract_test_expression(sexp)
109
+ brace_block = extract_brace_block(sexp)
110
+ extract_first_statement(brace_block)
111
+ end
112
+
113
+ def extract_brace_block(sexp)
114
+ unless then_block?(sexp)
115
+ source = Sorcerer.source(sexp)
116
+ fail InvalidThenError, "Unexpected code at #{source_line}\n#{source}"
117
+ end
118
+ sexp[1][2][2]
119
+ end
120
+
121
+ def then_block?(sexp)
122
+ sexp.first == :program &&
123
+ sexp[1].first == :stmts_add &&
124
+ sexp[1][2].first == :method_add_block &&
125
+ (sexp[1][2][2].first == :brace_block || sexp[1][2][2].first == :do_block)
126
+ end
127
+
128
+ def extract_first_statement(block_sexp)
129
+ unless contains_one_statement?(block_sexp)
130
+ source = Sorcerer.source(block_sexp)
131
+ fail InvalidThenError, "Multiple statements in Then block at #{source_line}\n#{source}"
132
+ end
133
+ extract_statement_from_block(block_sexp)
134
+ end
135
+
136
+ def contains_one_statement?(block_sexp)
137
+ block_sexp[2].first == :stmts_add &&
138
+ block_sexp[2][1].first == :stmts_new
139
+ end
140
+
141
+ def extract_statement_from_block(block_sexp)
142
+ block_sexp[2][2]
143
+ end
144
+
145
+ def eval_sexp(sexp)
146
+ expr = Sorcerer.source(sexp)
147
+ eval_in(expr, @env)
148
+ end
149
+
150
+ def eval_in(exp, binding)
151
+ eval(exp, binding).inspect
152
+ rescue StandardError => ex
153
+ EvalErr.new("#{ex.class}: #{ex.message}")
154
+ end
155
+
156
+ WRAP_WIDTH = 20
157
+
158
+ def display_pairs(pairs)
159
+ width = suggest_width(pairs)
160
+ pairs.each do |x, v|
161
+ fmt = (v.size > WRAP_WIDTH) ?
162
+ " %-#{width+2}s\n #{' '*(width+2)} <- %s\n" :
163
+ " %-#{width+2}s <- %s\n"
164
+ @output << sprintf(fmt, v, x)
165
+ end
166
+ end
167
+
168
+ def suggest_width(pairs)
169
+ pairs.map { |x,v| v.size }.select { |n| n < WRAP_WIDTH }.max || 10
170
+ end
171
+
172
+ def source_line
173
+ "#{@code_file}:#{@code_line}"
174
+ end
175
+ end
176
+
177
+ end
178
+ end
@@ -2,8 +2,10 @@ module RSpec
2
2
  module Given
3
3
  VERSION_NUMBERS = [
4
4
  VERSION_MAJOR = 2,
5
- VERSION_MINOR = 2,
6
- VERSION_BUILD = 1,
5
+ VERSION_MINOR = 3,
6
+ VERSION_BUILD = 0,
7
+ 'beta',
8
+ VERSION_BETA = 1,
7
9
  ]
8
10
  VERSION = VERSION_NUMBERS.join(".")
9
11
  end
metadata CHANGED
@@ -1,15 +1,15 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-given
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.2.1
5
- prerelease:
4
+ version: 2.3.0.beta.1
5
+ prerelease: 6
6
6
  platform: ruby
7
7
  authors:
8
8
  - Jim Weirich
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-24 00:00:00.000000000 Z
12
+ date: 2012-12-29 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -27,6 +27,22 @@ dependencies:
27
27
  - - ! '>'
28
28
  - !ruby/object:Gem::Version
29
29
  version: 1.2.8
30
+ - !ruby/object:Gem::Dependency
31
+ name: sorcerer
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.3.7
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.3.7
30
46
  - !ruby/object:Gem::Dependency
31
47
  name: bluecloth
32
48
  requirement: !ruby/object:Gem::Requirement
@@ -59,6 +75,22 @@ dependencies:
59
75
  - - ! '>'
60
76
  - !ruby/object:Gem::Version
61
77
  version: 2.4.2
78
+ - !ruby/object:Gem::Dependency
79
+ name: ghpreview
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: 0.0.1
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: 0.0.1
62
94
  description: ! 'Given is an RSpec extension that allows explicit definition of the
63
95
 
64
96
  pre and post-conditions for code under test.
@@ -76,20 +108,25 @@ files:
76
108
  - README.md
77
109
  - lib/rspec-given.rb
78
110
  - lib/rspec/given/configure.rb
111
+ - lib/rspec/given/core.rb
79
112
  - lib/rspec/given/extensions.rb
80
113
  - lib/rspec/given/failure.rb
81
114
  - lib/rspec/given/file_cache.rb
82
115
  - lib/rspec/given/have_failed.rb
83
116
  - lib/rspec/given/line_extractor.rb
84
117
  - lib/rspec/given/module_methods.rb
118
+ - lib/rspec/given/natural_assertion.rb
85
119
  - lib/rspec/given/rspec1_given.rb
86
120
  - lib/rspec/given/version.rb
87
121
  - lib/rspec/given.rb
88
122
  - examples/example_helper.rb
123
+ - examples/failing/natural_failing_spec.rb
124
+ - examples/failing/sample_spec.rb
89
125
  - examples/integration/and_spec.rb
90
126
  - examples/integration/focused_line_spec.rb
91
127
  - examples/integration/given_spec.rb
92
128
  - examples/integration/invariant_spec.rb
129
+ - examples/integration/then_spec.rb
93
130
  - examples/other/line_example.rb
94
131
  - examples/stack/stack.rb
95
132
  - examples/stack/stack_spec.rb
@@ -111,16 +148,13 @@ required_ruby_version: !ruby/object:Gem::Requirement
111
148
  requirements:
112
149
  - - ! '>='
113
150
  - !ruby/object:Gem::Version
114
- version: '0'
115
- segments:
116
- - 0
117
- hash: 3954842768326946335
151
+ version: 1.9.2
118
152
  required_rubygems_version: !ruby/object:Gem::Requirement
119
153
  none: false
120
154
  requirements:
121
- - - ! '>='
155
+ - - ! '>'
122
156
  - !ruby/object:Gem::Version
123
- version: '0'
157
+ version: 1.3.1
124
158
  requirements: []
125
159
  rubyforge_project: given
126
160
  rubygems_version: 1.8.24