rspec-given 2.2.1 → 2.3.0.beta.1

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/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