shoulda-matchers 3.1.0 → 3.1.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.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +3 -3
- data/MIT-LICENSE +1 -1
- data/NEWS.md +52 -0
- data/README.md +27 -34
- data/custom_plan.rb +22 -6
- data/gemfiles/4.0.0.gemfile +1 -1
- data/gemfiles/4.0.0.gemfile.lock +3 -3
- data/gemfiles/4.0.1.gemfile +1 -1
- data/gemfiles/4.0.1.gemfile.lock +3 -3
- data/gemfiles/4.1.gemfile +1 -1
- data/gemfiles/4.1.gemfile.lock +3 -3
- data/gemfiles/4.2.gemfile +1 -1
- data/gemfiles/4.2.gemfile.lock +3 -3
- data/lib/shoulda/matchers/action_controller/route_params.rb +1 -1
- data/lib/shoulda/matchers/active_model/allow_value_matcher.rb +1 -1
- data/lib/shoulda/matchers/active_model/validate_inclusion_of_matcher.rb +1 -0
- data/lib/shoulda/matchers/active_record/validate_uniqueness_of_matcher.rb +171 -66
- data/lib/shoulda/matchers/version.rb +1 -1
- data/spec/unit/shoulda/matchers/active_model/allow_value_matcher_spec.rb +9 -1
- data/spec/unit/shoulda/matchers/active_record/validate_uniqueness_of_matcher_spec.rb +233 -39
- metadata +12 -12
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f5c8e2ffc80ab293ff70c3b4635ba8bcbcb1f9ac
|
4
|
+
data.tar.gz: f9c362c8e26ec64f5275eb7e0f61dbe9e472de8d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 32dd5209bade6bc80dd860bd7de9f4ba7b772c37633ff555110663abf2dd250d7807b6e5d19dd6160ba834a3dff41d9f90e2100d25e65143feab0a29117527b8
|
7
|
+
data.tar.gz: 38aa6e350fce49d688d6874e0afc50489310821ef27bb8f54a5c2787f43967d6f1359f0989f8fbb5201819eaa8b1a794f832218749bcc8d0c7ea77d0d19cf534
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -28,7 +28,7 @@ GEM
|
|
28
28
|
pygments.rb (0.3.7)
|
29
29
|
posix-spawn (~> 0.3.6)
|
30
30
|
yajl-ruby (~> 1.1.0)
|
31
|
-
rake (10.
|
31
|
+
rake (10.5.0)
|
32
32
|
redcarpet (3.0.0)
|
33
33
|
rspec (3.4.0)
|
34
34
|
rspec-core (~> 3.4.0)
|
@@ -60,11 +60,11 @@ DEPENDENCIES
|
|
60
60
|
pry!
|
61
61
|
pry-byebug
|
62
62
|
pygments.rb
|
63
|
-
rake (
|
63
|
+
rake (>= 10.5.0, < 11)
|
64
64
|
redcarpet
|
65
65
|
rspec (~> 3.2)
|
66
66
|
yard
|
67
67
|
zeus
|
68
68
|
|
69
69
|
BUNDLED WITH
|
70
|
-
1.11.
|
70
|
+
1.11.2
|
data/MIT-LICENSE
CHANGED
data/NEWS.md
CHANGED
@@ -1,3 +1,55 @@
|
|
1
|
+
# 3.1.1
|
2
|
+
|
3
|
+
### Bug fixes
|
4
|
+
|
5
|
+
* Some matchers make use of ActiveSupport's `in?` method, but do not include the
|
6
|
+
file where this is defined in ActiveSupport. This causes problems with
|
7
|
+
projects using shoulda-matchers that do not include all of ActiveSupport by
|
8
|
+
default. To fix this, replace `in?` with Ruby's builtin `include?`.
|
9
|
+
|
10
|
+
* *Pull request: [#879]*
|
11
|
+
|
12
|
+
* `validate_uniqueness_of` works by creating a record if it doesn't exist, and
|
13
|
+
then testing against a new record with various attributes set that are equal
|
14
|
+
to (or different than) corresponding attributes in the existing record. In
|
15
|
+
3.1.0 a change was made whereby when the uniqueness matcher is given a new
|
16
|
+
record and creates an existing record out of it, it ensures that the record is
|
17
|
+
valid before continuing on. This created a problem because if the subject,
|
18
|
+
before it was saved, was empty and therefore in an invalid state, it could not
|
19
|
+
effectively be saved. While ideally this should be enforced, doing so would be
|
20
|
+
a backward-incompatible change, so this behavior has been rolled back.
|
21
|
+
([#880], [#884], [#885])
|
22
|
+
|
23
|
+
* *Commit: [45de869]*
|
24
|
+
* *Issues: [#880], [#884], [#885]*
|
25
|
+
|
26
|
+
* Fix an issue with `validate_uniqueness_of` + `scoped_to` when used against a
|
27
|
+
model where the attribute has multiple uniqueness validations and each
|
28
|
+
validation has a different set of scopes. In this case, a test written for the
|
29
|
+
first validation (and its scopes) would pass, but tests for the other
|
30
|
+
validations (and their scopes) would not, as the matcher only considered the
|
31
|
+
first set of scopes as the *actual* set of scopes.
|
32
|
+
|
33
|
+
* *Commit: [28bd9a1]*
|
34
|
+
* *Issues: [#830]*
|
35
|
+
|
36
|
+
### Improvements
|
37
|
+
|
38
|
+
* Update `validate_uniqueness_of` so that if an existing record fails to be
|
39
|
+
created because a column is non-nullable and was not filled in, raise an
|
40
|
+
ExistingRecordInvalid exception with details on how to fix the test.
|
41
|
+
|
42
|
+
* *Commit: [78ccfc5]*
|
43
|
+
|
44
|
+
[#879]: https://github.com/thoughtbot/shoulda-matchers/issues/879
|
45
|
+
[45de869]: https://github.com/thoughtbot/shoulda-matchers/commit/45de8698487d57f559c5bf35818d1c1ee82b0e77
|
46
|
+
[#880]: https://github.com/thoughtbot/shoulda-matchers/issues/880
|
47
|
+
[#884]: https://github.com/thoughtbot/shoulda-matchers/issues/884
|
48
|
+
[#885]: https://github.com/thoughtbot/shoulda-matchers/issues/885
|
49
|
+
[78ccfc5]: https://github.com/thoughtbot/shoulda-matchers/commit/78ccfc50b52fa686c109d614df66744b0da65380
|
50
|
+
[28bd9a1]: https://github.com/thoughtbot/shoulda-matchers/commit/28bd9a10c71af4d541b692d6204163c394ebd33c
|
51
|
+
[#830]: https://github.com/thoughtbot/shoulda-matchers/issues/830
|
52
|
+
|
1
53
|
# 3.1.0
|
2
54
|
|
3
55
|
### Bug fixes
|
data/README.md
CHANGED
@@ -109,7 +109,32 @@ group :test do
|
|
109
109
|
end
|
110
110
|
```
|
111
111
|
|
112
|
-
|
112
|
+
Now you need to tell the gem a couple of things:
|
113
|
+
|
114
|
+
* Which test framework you're using
|
115
|
+
* Which portion of the matchers you want to use
|
116
|
+
|
117
|
+
You can supply this information by using a configuration block. Place the
|
118
|
+
following in `rails_helper.rb`:
|
119
|
+
|
120
|
+
``` ruby
|
121
|
+
Shoulda::Matchers.configure do |config|
|
122
|
+
config.integrate do |with|
|
123
|
+
# Choose a test framework:
|
124
|
+
with.test_framework :rspec
|
125
|
+
with.test_framework :minitest
|
126
|
+
with.test_framework :minitest_4
|
127
|
+
with.test_framework :test_unit
|
128
|
+
|
129
|
+
# Choose one or more libraries:
|
130
|
+
with.library :active_record
|
131
|
+
with.library :active_model
|
132
|
+
with.library :action_controller
|
133
|
+
# Or, choose the following (which implies all of the above):
|
134
|
+
with.library :rails
|
135
|
+
end
|
136
|
+
end
|
137
|
+
```
|
113
138
|
|
114
139
|
Now you can use matchers in your tests. For instance a model test might look
|
115
140
|
like this:
|
@@ -149,7 +174,7 @@ end
|
|
149
174
|
Then you can say:
|
150
175
|
|
151
176
|
``` ruby
|
152
|
-
describe
|
177
|
+
describe MySpecialModel, type: :model do
|
153
178
|
# ...
|
154
179
|
end
|
155
180
|
```
|
@@ -184,8 +209,6 @@ group :test do
|
|
184
209
|
end
|
185
210
|
```
|
186
211
|
|
187
|
-
[Then, configure the gem to integrate with Minitest](#configuration).
|
188
|
-
|
189
212
|
Now you can use matchers in your tests. For instance a model test might look
|
190
213
|
like this:
|
191
214
|
|
@@ -195,36 +218,6 @@ class PersonTest < ActiveSupport::TestCase
|
|
195
218
|
end
|
196
219
|
```
|
197
220
|
|
198
|
-
### Configuration
|
199
|
-
|
200
|
-
Before you can use Shoulda Matchers, you'll need to tell it a couple of things:
|
201
|
-
|
202
|
-
* Which test framework you're using
|
203
|
-
* Which portion of the matchers you want to use
|
204
|
-
|
205
|
-
You can supply this information by using a configuration block. Place the
|
206
|
-
following in `rails_helper.rb` (if you're using RSpec) or `test_helper.rb` (if
|
207
|
-
you're using Minitest):
|
208
|
-
|
209
|
-
``` ruby
|
210
|
-
Shoulda::Matchers.configure do |config|
|
211
|
-
config.integrate do |with|
|
212
|
-
# Choose a test framework:
|
213
|
-
with.test_framework :rspec
|
214
|
-
with.test_framework :minitest
|
215
|
-
with.test_framework :minitest_4
|
216
|
-
with.test_framework :test_unit
|
217
|
-
|
218
|
-
# Choose one or more libraries:
|
219
|
-
with.library :active_record
|
220
|
-
with.library :active_model
|
221
|
-
with.library :action_controller
|
222
|
-
# Or, choose the following (which implies all of the above):
|
223
|
-
with.library :rails
|
224
|
-
end
|
225
|
-
end
|
226
|
-
```
|
227
|
-
|
228
221
|
## Running tests
|
229
222
|
|
230
223
|
### Unit tests
|
data/custom_plan.rb
CHANGED
@@ -1,12 +1,27 @@
|
|
1
|
-
require 'zeus
|
1
|
+
require 'zeus'
|
2
|
+
require 'zeus/plan'
|
2
3
|
require_relative 'spec/support/tests/current_bundle'
|
3
4
|
|
4
|
-
class
|
5
|
-
def
|
6
|
-
|
7
|
-
|
5
|
+
class CouldNotBootZeusError < StandardError
|
6
|
+
def self.create(underlying_error:)
|
7
|
+
new(<<-MESSAGE)
|
8
|
+
Couldn't boot Zeus.
|
9
|
+
|
10
|
+
Bundler tried to load a gem that has already been loaded (but the
|
11
|
+
versions are different).
|
12
|
+
|
13
|
+
Note that Appraisal requires Rake, and so you'll want to make sure that
|
14
|
+
the Gemfile is pointing to the same version of Rake that you have
|
15
|
+
installed locally.
|
16
|
+
|
17
|
+
The original message is as follows:
|
18
|
+
|
19
|
+
#{underlying_error.message}
|
20
|
+
MESSAGE
|
8
21
|
end
|
22
|
+
end
|
9
23
|
|
24
|
+
class CustomPlan < Zeus::Plan
|
10
25
|
def boot
|
11
26
|
ENV['BUNDLE_GEMFILE'] = File.expand_path(
|
12
27
|
"../gemfiles/#{latest_appraisal}.gemfile",
|
@@ -19,10 +34,11 @@ class CustomPlan < Zeus::Plan
|
|
19
34
|
$LOAD_PATH << File.expand_path('../spec', __FILE__)
|
20
35
|
|
21
36
|
require_relative 'spec/support/unit/load_environment'
|
37
|
+
rescue Gem::LoadError => error
|
38
|
+
raise CouldNotBootZeusError.create(underlying_error: error)
|
22
39
|
end
|
23
40
|
|
24
41
|
def after_fork
|
25
|
-
# @rails_plan.reconnect_activerecord
|
26
42
|
end
|
27
43
|
|
28
44
|
def test_environment
|
data/gemfiles/4.0.0.gemfile
CHANGED
data/gemfiles/4.0.0.gemfile.lock
CHANGED
@@ -115,7 +115,7 @@ GEM
|
|
115
115
|
activesupport (= 4.0.0)
|
116
116
|
rake (>= 0.8.7)
|
117
117
|
thor (>= 0.18.1, < 2.0)
|
118
|
-
rake (10.
|
118
|
+
rake (10.5.0)
|
119
119
|
rdoc (4.2.0)
|
120
120
|
redcarpet (3.3.2)
|
121
121
|
rspec (3.4.0)
|
@@ -203,7 +203,7 @@ DEPENDENCIES
|
|
203
203
|
pry-byebug
|
204
204
|
pygments.rb
|
205
205
|
rails (= 4.0.0)
|
206
|
-
rake (
|
206
|
+
rake (>= 10.5.0, < 11)
|
207
207
|
redcarpet
|
208
208
|
rspec (~> 3.2)
|
209
209
|
rspec-rails (>= 3.2.0, < 4)
|
@@ -220,4 +220,4 @@ DEPENDENCIES
|
|
220
220
|
zeus
|
221
221
|
|
222
222
|
BUNDLED WITH
|
223
|
-
1.11.
|
223
|
+
1.11.2
|
data/gemfiles/4.0.1.gemfile
CHANGED
data/gemfiles/4.0.1.gemfile.lock
CHANGED
@@ -117,7 +117,7 @@ GEM
|
|
117
117
|
activesupport (= 4.0.1)
|
118
118
|
rake (>= 0.8.7)
|
119
119
|
thor (>= 0.18.1, < 2.0)
|
120
|
-
rake (10.
|
120
|
+
rake (10.5.0)
|
121
121
|
rdoc (4.2.0)
|
122
122
|
redcarpet (3.3.2)
|
123
123
|
rspec (3.4.0)
|
@@ -205,7 +205,7 @@ DEPENDENCIES
|
|
205
205
|
pry-byebug
|
206
206
|
pygments.rb
|
207
207
|
rails (= 4.0.1)
|
208
|
-
rake (
|
208
|
+
rake (>= 10.5.0, < 11)
|
209
209
|
redcarpet
|
210
210
|
rspec (~> 3.2)
|
211
211
|
rspec-rails (>= 3.2.0, < 4)
|
@@ -222,4 +222,4 @@ DEPENDENCIES
|
|
222
222
|
zeus
|
223
223
|
|
224
224
|
BUNDLED WITH
|
225
|
-
1.11.
|
225
|
+
1.11.2
|
data/gemfiles/4.1.gemfile
CHANGED
data/gemfiles/4.1.gemfile.lock
CHANGED
@@ -113,7 +113,7 @@ GEM
|
|
113
113
|
activesupport (= 4.1.13)
|
114
114
|
rake (>= 0.8.7)
|
115
115
|
thor (>= 0.18.1, < 2.0)
|
116
|
-
rake (10.
|
116
|
+
rake (10.5.0)
|
117
117
|
rdoc (4.2.0)
|
118
118
|
redcarpet (3.3.2)
|
119
119
|
rspec (3.4.0)
|
@@ -200,7 +200,7 @@ DEPENDENCIES
|
|
200
200
|
pry-byebug
|
201
201
|
pygments.rb
|
202
202
|
rails (~> 4.1.0)
|
203
|
-
rake (
|
203
|
+
rake (>= 10.5.0, < 11)
|
204
204
|
redcarpet
|
205
205
|
rspec (~> 3.2)
|
206
206
|
rspec-rails (>= 3.2.0, < 4)
|
@@ -217,4 +217,4 @@ DEPENDENCIES
|
|
217
217
|
zeus
|
218
218
|
|
219
219
|
BUNDLED WITH
|
220
|
-
1.11.
|
220
|
+
1.11.2
|
data/gemfiles/4.2.gemfile
CHANGED
data/gemfiles/4.2.gemfile.lock
CHANGED
@@ -138,7 +138,7 @@ GEM
|
|
138
138
|
activesupport (= 4.2.4)
|
139
139
|
rake (>= 0.8.7)
|
140
140
|
thor (>= 0.18.1, < 2.0)
|
141
|
-
rake (10.
|
141
|
+
rake (10.5.0)
|
142
142
|
rdoc (4.2.0)
|
143
143
|
redcarpet (3.3.2)
|
144
144
|
rspec (3.4.0)
|
@@ -223,7 +223,7 @@ DEPENDENCIES
|
|
223
223
|
pry-byebug
|
224
224
|
pygments.rb
|
225
225
|
rails (~> 4.2.0)
|
226
|
-
rake (
|
226
|
+
rake (>= 10.5.0, < 11)
|
227
227
|
redcarpet
|
228
228
|
rspec (~> 3.2)
|
229
229
|
rspec-rails (>= 3.2.0, < 4)
|
@@ -240,4 +240,4 @@ DEPENDENCIES
|
|
240
240
|
zeus
|
241
241
|
|
242
242
|
BUNDLED WITH
|
243
|
-
1.11.
|
243
|
+
1.11.2
|
@@ -603,7 +603,7 @@ pass, or do something else entirely.
|
|
603
603
|
def inspected_values_to_set
|
604
604
|
Shoulda::Matchers::Util.inspect_values(values_to_set).to_sentence(
|
605
605
|
two_words_connector: " or ",
|
606
|
-
last_word_connector: ", or"
|
606
|
+
last_word_connector: ", or "
|
607
607
|
)
|
608
608
|
end
|
609
609
|
|
@@ -53,23 +53,34 @@ module Shoulda
|
|
53
53
|
# it { should validate_uniqueness_of(:title) }
|
54
54
|
# end
|
55
55
|
#
|
56
|
-
# However, running this test will fail with
|
56
|
+
# However, running this test will fail with an exception such as:
|
57
57
|
#
|
58
|
-
#
|
58
|
+
# Shoulda::Matchers::ActiveRecord::ValidateUniquenessOfMatcher::ExistingRecordInvalid:
|
59
|
+
# validate_uniqueness_of works by matching a new record against an
|
60
|
+
# existing record. If there is no existing record, it will create one
|
61
|
+
# using the record you provide.
|
59
62
|
#
|
60
|
-
#
|
61
|
-
#
|
62
|
-
#
|
63
|
-
#
|
63
|
+
# While doing this, the following error was raised:
|
64
|
+
#
|
65
|
+
# PG::NotNullViolation: ERROR: null value in column "content" violates not-null constraint
|
66
|
+
# DETAIL: Failing row contains (1, null, null).
|
67
|
+
# : INSERT INTO "posts" DEFAULT VALUES RETURNING "id"
|
68
|
+
#
|
69
|
+
# The best way to fix this is to provide the matcher with a record where
|
70
|
+
# any required attributes are filled in with valid values beforehand.
|
71
|
+
#
|
72
|
+
# (The exact error message will differ depending on which database you're
|
73
|
+
# using, but you get the idea.)
|
64
74
|
#
|
65
75
|
# This happens because `validate_uniqueness_of` tries to create a new post
|
66
76
|
# but cannot do so because of the `content` attribute: though unrelated to
|
67
|
-
# this test, it nevertheless needs to be filled in.
|
68
|
-
#
|
77
|
+
# this test, it nevertheless needs to be filled in. As indicated at the
|
78
|
+
# end of the error message, the solution is to build a custom Post object
|
79
|
+
# ahead of time with `content` filled in:
|
69
80
|
#
|
70
81
|
# describe Post do
|
71
82
|
# describe "validations" do
|
72
|
-
# subject { Post.new(content:
|
83
|
+
# subject { Post.new(content: "Here is the content") }
|
73
84
|
# it { should validate_uniqueness_of(:title) }
|
74
85
|
# end
|
75
86
|
# end
|
@@ -259,8 +270,8 @@ module Shoulda
|
|
259
270
|
@failure_reason = nil
|
260
271
|
@failure_reason_when_negated = nil
|
261
272
|
@attribute_setters = {
|
262
|
-
existing_record:
|
263
|
-
new_record:
|
273
|
+
existing_record: AttributeSetters.new,
|
274
|
+
new_record: AttributeSetters.new
|
264
275
|
}
|
265
276
|
end
|
266
277
|
|
@@ -313,10 +324,9 @@ module Shoulda
|
|
313
324
|
@given_record = given_record
|
314
325
|
@all_records = model.all
|
315
326
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
scopes_match? &&
|
327
|
+
validate_attribute_present_on_model? &&
|
328
|
+
validate_scopes_present_on_model? &&
|
329
|
+
validate_scopes_match? &&
|
320
330
|
validate_two_records_with_same_non_blank_value_cannot_coexist? &&
|
321
331
|
validate_case_sensitivity? &&
|
322
332
|
validate_after_scope_change? &&
|
@@ -370,14 +380,14 @@ module Shoulda
|
|
370
380
|
end
|
371
381
|
end
|
372
382
|
|
373
|
-
def
|
374
|
-
model._validators[@attribute].
|
383
|
+
def validations
|
384
|
+
model._validators[@attribute].select do |validator|
|
375
385
|
validator.is_a?(::ActiveRecord::Validations::UniquenessValidator)
|
376
386
|
end
|
377
387
|
end
|
378
388
|
|
379
|
-
def
|
380
|
-
if
|
389
|
+
def validate_scopes_match?
|
390
|
+
if scopes_match?
|
381
391
|
true
|
382
392
|
else
|
383
393
|
@failure_reason = 'Expected the validation'
|
@@ -388,7 +398,7 @@ module Shoulda
|
|
388
398
|
@failure_reason << " to be scoped to #{inspected_expected_scopes}"
|
389
399
|
end
|
390
400
|
|
391
|
-
if
|
401
|
+
if actual_sets_of_scopes.empty?
|
392
402
|
@failure_reason << ', but it was not scoped to anything.'
|
393
403
|
else
|
394
404
|
@failure_reason << ', but it was scoped to '
|
@@ -399,24 +409,42 @@ module Shoulda
|
|
399
409
|
end
|
400
410
|
end
|
401
411
|
|
402
|
-
def
|
403
|
-
|
412
|
+
def scopes_match?
|
413
|
+
actual_sets_of_scopes.empty? && expected_scopes.empty? ||
|
414
|
+
actual_sets_of_scopes.any? { |scopes| scopes == expected_scopes }
|
404
415
|
end
|
405
416
|
|
406
417
|
def inspected_expected_scopes
|
407
418
|
expected_scopes.map(&:inspect).to_sentence
|
408
419
|
end
|
409
420
|
|
410
|
-
def
|
411
|
-
|
412
|
-
|
421
|
+
def inspected_actual_scopes
|
422
|
+
inspected_actual_sets_of_scopes.to_sentence(
|
423
|
+
words_connector: " or ",
|
424
|
+
last_word_connector: ", or"
|
425
|
+
)
|
426
|
+
end
|
427
|
+
|
428
|
+
def inspected_actual_sets_of_scopes
|
429
|
+
inspected_sets_of_scopes = actual_sets_of_scopes.map do |scopes|
|
430
|
+
scopes.map(&:inspect)
|
431
|
+
end
|
432
|
+
|
433
|
+
if inspected_sets_of_scopes.many?
|
434
|
+
inspected_sets_of_scopes.map { |x| "(#{x.to_sentence})" }
|
413
435
|
else
|
414
|
-
|
436
|
+
inspected_sets_of_scopes.map(&:to_sentence)
|
415
437
|
end
|
416
438
|
end
|
417
439
|
|
418
|
-
def
|
419
|
-
|
440
|
+
def expected_scopes
|
441
|
+
Array.wrap(@options[:scopes])
|
442
|
+
end
|
443
|
+
|
444
|
+
def actual_sets_of_scopes
|
445
|
+
validations.map do |validation|
|
446
|
+
Array.wrap(validation.options[:scope])
|
447
|
+
end.reject(&:empty?)
|
420
448
|
end
|
421
449
|
|
422
450
|
def allows_nil?
|
@@ -437,18 +465,6 @@ module Shoulda
|
|
437
465
|
end
|
438
466
|
end
|
439
467
|
|
440
|
-
def existing_record_valid?
|
441
|
-
if existing_record.valid?
|
442
|
-
true
|
443
|
-
else
|
444
|
-
@failure_reason =
|
445
|
-
"The record you provided could not be created, " +
|
446
|
-
"as it failed with the following validation errors:\n\n" +
|
447
|
-
format_validation_errors(existing_record.errors)
|
448
|
-
false
|
449
|
-
end
|
450
|
-
end
|
451
|
-
|
452
468
|
def existing_record
|
453
469
|
unless defined?(@existing_record)
|
454
470
|
find_or_create_existing_record
|
@@ -479,20 +495,10 @@ module Shoulda
|
|
479
495
|
def create_existing_record
|
480
496
|
@given_record.tap do |existing_record|
|
481
497
|
ensure_secure_password_set(existing_record)
|
482
|
-
existing_record.save
|
483
|
-
end
|
484
|
-
end
|
485
|
-
|
486
|
-
def update_existing_record!(value)
|
487
|
-
if existing_value_read != value
|
488
|
-
set_attribute_on!(
|
489
|
-
:existing_record,
|
490
|
-
existing_record,
|
491
|
-
@attribute,
|
492
|
-
value
|
493
|
-
)
|
494
|
-
existing_record.save!
|
498
|
+
existing_record.save(validate: false)
|
495
499
|
end
|
500
|
+
rescue ::ActiveRecord::StatementInvalid => error
|
501
|
+
raise ExistingRecordInvalid.create(underlying_exception: error)
|
496
502
|
end
|
497
503
|
|
498
504
|
def ensure_secure_password_set(instance)
|
@@ -502,6 +508,15 @@ module Shoulda
|
|
502
508
|
end
|
503
509
|
end
|
504
510
|
|
511
|
+
def update_existing_record!(value)
|
512
|
+
if existing_value_read != value
|
513
|
+
set_attribute_on_existing_record!(@attribute, value)
|
514
|
+
# It would be nice if we could ensure that the record was valid,
|
515
|
+
# but that would break users' existing tests
|
516
|
+
existing_record.save(validate: false)
|
517
|
+
end
|
518
|
+
end
|
519
|
+
|
505
520
|
def arbitrary_non_blank_value
|
506
521
|
limit = column_limit_for(@attribute)
|
507
522
|
non_blank_value = 'an arbitrary value'
|
@@ -532,8 +547,8 @@ module Shoulda
|
|
532
547
|
@new_record
|
533
548
|
end
|
534
549
|
|
535
|
-
def
|
536
|
-
if
|
550
|
+
def validate_attribute_present_on_model?
|
551
|
+
if attribute_present_on_model?
|
537
552
|
true
|
538
553
|
else
|
539
554
|
@failure_reason =
|
@@ -542,7 +557,12 @@ module Shoulda
|
|
542
557
|
end
|
543
558
|
end
|
544
559
|
|
545
|
-
def
|
560
|
+
def attribute_present_on_model?
|
561
|
+
model.method_defined?("#{attribute}=") ||
|
562
|
+
model.columns_hash.key?(attribute.to_s)
|
563
|
+
end
|
564
|
+
|
565
|
+
def validate_scopes_present_on_model?
|
546
566
|
if all_scopes_present_on_model?
|
547
567
|
true
|
548
568
|
else
|
@@ -697,7 +717,7 @@ module Shoulda
|
|
697
717
|
end
|
698
718
|
|
699
719
|
def boolean_value?(value)
|
700
|
-
|
720
|
+
[true, false].include?(value)
|
701
721
|
end
|
702
722
|
|
703
723
|
def defined_as_enum?(scope)
|
@@ -748,6 +768,11 @@ module Shoulda
|
|
748
768
|
@attribute_setters[:existing_record].last
|
749
769
|
end
|
750
770
|
|
771
|
+
def attribute_setters_for_new_record
|
772
|
+
@attribute_setters[:new_record] +
|
773
|
+
[last_attribute_setter_used_on_new_record]
|
774
|
+
end
|
775
|
+
|
751
776
|
def attribute_names_under_test
|
752
777
|
[@attribute] + expected_scopes
|
753
778
|
end
|
@@ -793,7 +818,7 @@ module Shoulda
|
|
793
818
|
prefix << "After taking the given #{model.name}"
|
794
819
|
|
795
820
|
if attribute_setter_for_existing_record
|
796
|
-
prefix << ', setting
|
821
|
+
prefix << ', setting '
|
797
822
|
prefix << description_for_attribute_setter(
|
798
823
|
attribute_setter_for_existing_record
|
799
824
|
)
|
@@ -806,7 +831,7 @@ module Shoulda
|
|
806
831
|
else
|
807
832
|
if attribute_setter_for_existing_record
|
808
833
|
prefix << "Given an existing #{model.name},"
|
809
|
-
prefix << ' after setting
|
834
|
+
prefix << ' after setting '
|
810
835
|
prefix << description_for_attribute_setter(
|
811
836
|
attribute_setter_for_existing_record
|
812
837
|
)
|
@@ -821,12 +846,9 @@ module Shoulda
|
|
821
846
|
end
|
822
847
|
end
|
823
848
|
|
824
|
-
prefix << " making a new #{model.name} and setting
|
849
|
+
prefix << " making a new #{model.name} and setting "
|
825
850
|
|
826
|
-
prefix <<
|
827
|
-
last_attribute_setter_used_on_new_record,
|
828
|
-
same_as_existing: existing_and_new_values_are_same?
|
829
|
-
)
|
851
|
+
prefix << descriptions_for_attribute_setters_for_new_record
|
830
852
|
|
831
853
|
prefix << ", the matcher expected the new #{model.name} to be"
|
832
854
|
|
@@ -849,7 +871,7 @@ different altogether.
|
|
849
871
|
end
|
850
872
|
|
851
873
|
def description_for_attribute_setter(attribute_setter, same_as_existing: nil)
|
852
|
-
description = ":#{attribute_setter.attribute_name} to "
|
874
|
+
description = "its :#{attribute_setter.attribute_name} to "
|
853
875
|
|
854
876
|
if same_as_existing == false
|
855
877
|
description << 'a different value, '
|
@@ -874,6 +896,23 @@ different altogether.
|
|
874
896
|
description
|
875
897
|
end
|
876
898
|
|
899
|
+
def descriptions_for_attribute_setters_for_new_record
|
900
|
+
attribute_setter_descriptions_for_new_record.to_sentence
|
901
|
+
end
|
902
|
+
|
903
|
+
def attribute_setter_descriptions_for_new_record
|
904
|
+
attribute_setters_for_new_record.map do |attribute_setter|
|
905
|
+
same_as_existing = (
|
906
|
+
attribute_setter.value_written ==
|
907
|
+
existing_value_written
|
908
|
+
)
|
909
|
+
description_for_attribute_setter(
|
910
|
+
attribute_setter,
|
911
|
+
same_as_existing: same_as_existing
|
912
|
+
)
|
913
|
+
end
|
914
|
+
end
|
915
|
+
|
877
916
|
def existing_and_new_values_are_same?
|
878
917
|
last_value_set_on_new_record == existing_value_written
|
879
918
|
end
|
@@ -886,6 +925,50 @@ different altogether.
|
|
886
925
|
last_submatcher_run.last_value_set
|
887
926
|
end
|
888
927
|
|
928
|
+
# @private
|
929
|
+
class AttributeSetters
|
930
|
+
include Enumerable
|
931
|
+
|
932
|
+
def initialize
|
933
|
+
@attribute_setters = []
|
934
|
+
end
|
935
|
+
|
936
|
+
def <<(given_attribute_setter)
|
937
|
+
index = find_index_of(given_attribute_setter)
|
938
|
+
|
939
|
+
if index
|
940
|
+
@attribute_setters[index] = given_attribute_setter
|
941
|
+
else
|
942
|
+
@attribute_setters << given_attribute_setter
|
943
|
+
end
|
944
|
+
end
|
945
|
+
|
946
|
+
def +(other_attribute_setters)
|
947
|
+
dup.tap do |attribute_setters|
|
948
|
+
other_attribute_setters.each do |attribute_setter|
|
949
|
+
attribute_setters << attribute_setter
|
950
|
+
end
|
951
|
+
end
|
952
|
+
end
|
953
|
+
|
954
|
+
def each(&block)
|
955
|
+
@attribute_setters.each(&block)
|
956
|
+
end
|
957
|
+
|
958
|
+
def last
|
959
|
+
@attribute_setters.last
|
960
|
+
end
|
961
|
+
|
962
|
+
private
|
963
|
+
|
964
|
+
def find_index_of(given_attribute_setter)
|
965
|
+
@attribute_setters.find_index do |attribute_setter|
|
966
|
+
attribute_setter.attribute_name ==
|
967
|
+
given_attribute_setter.attribute_name
|
968
|
+
end
|
969
|
+
end
|
970
|
+
end
|
971
|
+
|
889
972
|
# @private
|
890
973
|
class NonCaseSwappableValueError < Shoulda::Matchers::Error
|
891
974
|
attr_accessor :model, :attribute, :value
|
@@ -895,7 +978,7 @@ different altogether.
|
|
895
978
|
Your #{model.name} model has a uniqueness validation on :#{attribute} which is
|
896
979
|
declared to be case-sensitive, but the value the uniqueness matcher used,
|
897
980
|
#{value.inspect}, doesn't contain any alpha characters, so using it to
|
898
|
-
|
981
|
+
test the case-sensitivity part of the validation is ineffective. There are
|
899
982
|
two possible solutions for this depending on what you're trying to do here:
|
900
983
|
|
901
984
|
a) If you meant for the validation to be case-sensitive, then you need to give
|
@@ -912,6 +995,28 @@ http://matchers.shoulda.io/docs/v#{Shoulda::Matchers::VERSION}/file.NonCaseSwapp
|
|
912
995
|
MESSAGE
|
913
996
|
end
|
914
997
|
end
|
998
|
+
|
999
|
+
# @private
|
1000
|
+
class ExistingRecordInvalid < Shoulda::Matchers::Error
|
1001
|
+
include Shoulda::Matchers::ActiveModel::Helpers
|
1002
|
+
|
1003
|
+
attr_accessor :underlying_exception
|
1004
|
+
|
1005
|
+
def message
|
1006
|
+
<<-MESSAGE.strip
|
1007
|
+
validate_uniqueness_of works by matching a new record against an
|
1008
|
+
existing record. If there is no existing record, it will create one
|
1009
|
+
using the record you provide.
|
1010
|
+
|
1011
|
+
While doing this, the following error was raised:
|
1012
|
+
|
1013
|
+
#{Shoulda::Matchers::Util.indent(underlying_exception.message, 2)}
|
1014
|
+
|
1015
|
+
The best way to fix this is to provide the matcher with a record where
|
1016
|
+
any required attributes are filled in with valid values beforehand.
|
1017
|
+
MESSAGE
|
1018
|
+
end
|
1019
|
+
end
|
915
1020
|
end
|
916
1021
|
end
|
917
1022
|
end
|
@@ -10,7 +10,7 @@ end
|
|
10
10
|
|
11
11
|
describe Shoulda::Matchers::ActiveModel::AllowValueMatcher, type: :model do
|
12
12
|
context "#description" do
|
13
|
-
it 'describes itself with
|
13
|
+
it 'describes itself with two values' do
|
14
14
|
matcher = allow_value('foo', 'bar').for(:baz)
|
15
15
|
|
16
16
|
expect(matcher.description).to eq(
|
@@ -18,6 +18,14 @@ describe Shoulda::Matchers::ActiveModel::AllowValueMatcher, type: :model do
|
|
18
18
|
)
|
19
19
|
end
|
20
20
|
|
21
|
+
it 'describes itself with more than two values' do
|
22
|
+
matcher = allow_value('foo', 'bar', 'qux').for(:baz)
|
23
|
+
|
24
|
+
expect(matcher.description).to eq(
|
25
|
+
'allow :baz to be ‹"foo"›, ‹"bar"›, or ‹"qux"›'
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
21
29
|
it 'describes itself with a single value' do
|
22
30
|
matcher = allow_value('foo').for(:baz)
|
23
31
|
|
@@ -227,6 +227,94 @@ within the scope of :non_existent1 and :non_existent2.
|
|
227
227
|
end
|
228
228
|
end
|
229
229
|
|
230
|
+
context 'when there is more than one validation on the same attribute with different scopes' do
|
231
|
+
context 'when a record exists beforehand, where all scopes are set' do
|
232
|
+
if column_type != :boolean
|
233
|
+
context 'when each validation has the same (default) message' do
|
234
|
+
it 'accepts' do
|
235
|
+
pending 'this needs another qualifier to properly fix'
|
236
|
+
|
237
|
+
model = define_model(
|
238
|
+
'Example',
|
239
|
+
attribute_name => :string,
|
240
|
+
scope1: column_type,
|
241
|
+
scope2: column_type
|
242
|
+
) do |m|
|
243
|
+
m.validates_uniqueness_of(attribute_name, scope: [:scope1])
|
244
|
+
m.validates_uniqueness_of(attribute_name, scope: [:scope2])
|
245
|
+
end
|
246
|
+
|
247
|
+
model.create!(
|
248
|
+
attribute_name => dummy_value_for(:string),
|
249
|
+
scope1: dummy_value_for(column_type),
|
250
|
+
scope2: dummy_value_for(column_type)
|
251
|
+
)
|
252
|
+
|
253
|
+
expect(model.new).to validate_uniqueness.scoped_to(:scope1)
|
254
|
+
expect(model.new).to validate_uniqueness.scoped_to(:scope2)
|
255
|
+
end
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
context 'when each validation has a different message' do
|
260
|
+
it 'accepts' do
|
261
|
+
model = define_model(
|
262
|
+
'Example',
|
263
|
+
attribute_name => :string,
|
264
|
+
scope1: column_type,
|
265
|
+
scope2: column_type
|
266
|
+
) do |m|
|
267
|
+
m.validates_uniqueness_of(
|
268
|
+
attribute_name,
|
269
|
+
scope: [:scope1],
|
270
|
+
message: 'first message'
|
271
|
+
)
|
272
|
+
m.validates_uniqueness_of(
|
273
|
+
attribute_name,
|
274
|
+
scope: [:scope2],
|
275
|
+
message: 'second message'
|
276
|
+
)
|
277
|
+
end
|
278
|
+
|
279
|
+
model.create!(
|
280
|
+
attribute_name => dummy_value_for(:string),
|
281
|
+
scope1: dummy_value_for(column_type),
|
282
|
+
scope2: dummy_value_for(column_type)
|
283
|
+
)
|
284
|
+
|
285
|
+
expect(model.new).
|
286
|
+
to validate_uniqueness.
|
287
|
+
scoped_to(:scope1).
|
288
|
+
with_message('first message')
|
289
|
+
|
290
|
+
expect(model.new).
|
291
|
+
to validate_uniqueness.
|
292
|
+
scoped_to(:scope2).
|
293
|
+
with_message('second message')
|
294
|
+
end
|
295
|
+
end
|
296
|
+
end
|
297
|
+
|
298
|
+
context 'when no record exists beforehand' do
|
299
|
+
it 'accepts' do
|
300
|
+
pending 'this needs another qualifier to properly fix'
|
301
|
+
|
302
|
+
model = define_model(
|
303
|
+
'Example',
|
304
|
+
attribute_name => :string,
|
305
|
+
scope1: column_type,
|
306
|
+
scope2: column_type
|
307
|
+
) do |m|
|
308
|
+
m.validates_uniqueness_of(attribute_name, scope: [:scope1])
|
309
|
+
m.validates_uniqueness_of(attribute_name, scope: [:scope2])
|
310
|
+
end
|
311
|
+
|
312
|
+
expect(model.new).to validate_uniqueness.scoped_to(:scope1)
|
313
|
+
expect(model.new).to validate_uniqueness.scoped_to(:scope2)
|
314
|
+
end
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
230
318
|
define_method(:build_attribute) do |attribute_options|
|
231
319
|
attribute_options.deep_merge(
|
232
320
|
column_type: column_type,
|
@@ -268,7 +356,7 @@ Example did not properly validate that :attr is case-sensitively unique.
|
|
268
356
|
end
|
269
357
|
end
|
270
358
|
|
271
|
-
context 'when the record
|
359
|
+
context 'when the existing record was created beforehand' do
|
272
360
|
context 'when the subject is a new record' do
|
273
361
|
it 'accepts' do
|
274
362
|
create_record_validating_uniqueness
|
@@ -277,63 +365,169 @@ Example did not properly validate that :attr is case-sensitively unique.
|
|
277
365
|
end
|
278
366
|
end
|
279
367
|
|
280
|
-
context 'when the subject is
|
368
|
+
context 'when the subject is itself the existing record' do
|
281
369
|
it 'accepts' do
|
282
370
|
expect(existing_record_validating_uniqueness).to validate_uniqueness
|
283
371
|
end
|
284
372
|
end
|
373
|
+
end
|
285
374
|
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
375
|
+
context 'when the existing record was not created beforehand' do
|
376
|
+
context 'and the subject is empty' do
|
377
|
+
context 'and the attribute being tested is required' do
|
378
|
+
it 'can save the subject without the attribute being set' do
|
379
|
+
options = { attribute_name: :attr }
|
380
|
+
model = define_model_validating_uniqueness(options) do |m|
|
381
|
+
m.validates_presence_of :attr
|
382
|
+
end
|
293
383
|
|
294
|
-
|
295
|
-
|
384
|
+
record = model.new
|
385
|
+
|
386
|
+
expect(record).to validate_uniqueness
|
296
387
|
end
|
388
|
+
end
|
297
389
|
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
390
|
+
context 'and the attribute being tested are required along with other attributes' do
|
391
|
+
it 'can save the subject without the attributes being set' do
|
392
|
+
options = {
|
393
|
+
attribute_name: :attr,
|
394
|
+
additional_attributes: [:required_attribute]
|
395
|
+
}
|
396
|
+
model = define_model_validating_uniqueness(options) do |m|
|
397
|
+
m.validates_presence_of :attr
|
398
|
+
m.validates_presence_of :required_attribute
|
399
|
+
end
|
304
400
|
|
305
|
-
|
401
|
+
expect(model.new).to validate_uniqueness
|
402
|
+
end
|
306
403
|
end
|
307
|
-
end
|
308
|
-
end
|
309
404
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
405
|
+
context 'and the attribute being tested has other validations on it' do
|
406
|
+
it 'can save the subject without it being completely valid' do
|
407
|
+
options = { attribute_name: :attr }
|
408
|
+
|
409
|
+
model = define_model_validating_uniqueness(options) do |m|
|
410
|
+
m.validates_presence_of :attr
|
411
|
+
m.validates_numericality_of :attr
|
412
|
+
end
|
413
|
+
|
414
|
+
expect(model.new).to validate_uniqueness
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
418
|
+
context 'and the table has non-nullable columns other than the attribute being validated' do
|
419
|
+
context 'which are set beforehand' do
|
420
|
+
it 'can save the subject' do
|
421
|
+
options = {
|
422
|
+
additional_attributes: [
|
423
|
+
{ name: :required_attribute, options: { null: false } }
|
424
|
+
]
|
425
|
+
}
|
426
|
+
model = define_model_validating_uniqueness(options)
|
427
|
+
record = model.new
|
428
|
+
record.required_attribute = 'something'
|
429
|
+
|
430
|
+
expect(record).to validate_uniqueness
|
431
|
+
end
|
432
|
+
end
|
433
|
+
|
434
|
+
context 'which are not set beforehand' do
|
435
|
+
it 'raises a useful exception' do
|
436
|
+
options = {
|
437
|
+
additional_attributes: [
|
438
|
+
{ name: :required_attribute, options: { null: false } }
|
439
|
+
]
|
440
|
+
}
|
441
|
+
model = define_model_validating_uniqueness(options)
|
442
|
+
|
443
|
+
assertion = lambda do
|
444
|
+
expect(model.new).to validate_uniqueness
|
445
|
+
end
|
446
|
+
|
447
|
+
expect(&assertion).to raise_error(
|
448
|
+
described_class::ExistingRecordInvalid
|
449
|
+
)
|
450
|
+
end
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
context 'and the model has required attributes other than the attribute being validated' do
|
455
|
+
it 'can save the subject without the attributes being set' do
|
456
|
+
options = {
|
457
|
+
additional_attributes: [:required_attribute]
|
458
|
+
}
|
459
|
+
model = define_model_validating_uniqueness(options) do |m|
|
460
|
+
m.validates_presence_of :required_attribute
|
461
|
+
end
|
462
|
+
|
463
|
+
expect(model.new).to validate_uniqueness
|
464
|
+
end
|
465
|
+
end
|
318
466
|
end
|
319
467
|
|
320
|
-
context 'and the
|
321
|
-
it '
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
468
|
+
context 'and the subject is not empty' do
|
469
|
+
it 'creates the record automatically from the subject' do
|
470
|
+
model = define_model_validating_uniqueness
|
471
|
+
assertion = -> {
|
472
|
+
record = build_record_from(model)
|
473
|
+
expect(record).to validate_uniqueness
|
326
474
|
}
|
327
|
-
model
|
328
|
-
|
475
|
+
expect(&assertion).to change(model, :count).from(0).to(1)
|
476
|
+
end
|
477
|
+
|
478
|
+
context 'and the table has required attributes other than the attribute being validated, set beforehand' do
|
479
|
+
it 'can save the subject' do
|
480
|
+
options = {
|
481
|
+
additional_attributes: [
|
482
|
+
{ name: :required_attribute, options: { null: false } }
|
483
|
+
]
|
484
|
+
}
|
485
|
+
model = define_model_validating_uniqueness(options)
|
486
|
+
|
487
|
+
record = build_record_from(model, required_attribute: 'something')
|
488
|
+
expect(record).to validate_uniqueness
|
329
489
|
end
|
490
|
+
end
|
330
491
|
|
331
|
-
|
332
|
-
|
492
|
+
context 'and the model has required attributes other than the attribute being validated, set beforehand' do
|
493
|
+
it 'can save the subject' do
|
494
|
+
options = {
|
495
|
+
additional_attributes: [:required_attribute]
|
496
|
+
}
|
497
|
+
model = define_model_validating_uniqueness(options) do |m|
|
498
|
+
m.validates_presence_of :required_attribute
|
499
|
+
end
|
500
|
+
|
501
|
+
record = build_record_from(model, required_attribute: 'something')
|
502
|
+
expect(record).to validate_uniqueness
|
503
|
+
end
|
333
504
|
end
|
334
505
|
end
|
335
506
|
end
|
336
507
|
|
508
|
+
context 'when the validation has no scope and a scope is specified' do
|
509
|
+
it 'rejects with an appropriate failure message' do
|
510
|
+
model = define_model_validating_uniqueness(
|
511
|
+
additional_attributes: [:other]
|
512
|
+
)
|
513
|
+
create_record_from(model)
|
514
|
+
record = build_record_from(model)
|
515
|
+
|
516
|
+
assertion = lambda do
|
517
|
+
expect(record).to validate_uniqueness.scoped_to(:other)
|
518
|
+
end
|
519
|
+
|
520
|
+
message = <<-MESSAGE
|
521
|
+
Example did not properly validate that :attr is case-sensitively unique
|
522
|
+
within the scope of :other.
|
523
|
+
Expected the validation to be scoped to :other, but it was not scoped
|
524
|
+
to anything.
|
525
|
+
MESSAGE
|
526
|
+
|
527
|
+
expect(&assertion).to fail_with_message(message)
|
528
|
+
end
|
529
|
+
end
|
530
|
+
|
337
531
|
context 'and the validation has a custom message' do
|
338
532
|
context 'when no message is specified' do
|
339
533
|
it 'rejects with an appropriate failure message' do
|
@@ -1307,7 +1501,7 @@ Example did not properly validate that :name is case-sensitively unique.
|
|
1307
1501
|
SecureRandom.uuid
|
1308
1502
|
elsif value.is_a?(Time)
|
1309
1503
|
value + 1
|
1310
|
-
elsif
|
1504
|
+
elsif [true, false].include?(value)
|
1311
1505
|
!value
|
1312
1506
|
elsif value.respond_to?(:next)
|
1313
1507
|
value.next
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shoulda-matchers
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.1.
|
4
|
+
version: 3.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tammer Saleh
|
@@ -14,20 +14,20 @@ authors:
|
|
14
14
|
autorequire:
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
|
-
date: 2016-01-
|
17
|
+
date: 2016-01-28 00:00:00.000000000 Z
|
18
18
|
dependencies:
|
19
19
|
- !ruby/object:Gem::Dependency
|
20
20
|
name: activesupport
|
21
21
|
requirement: !ruby/object:Gem::Requirement
|
22
22
|
requirements:
|
23
|
-
- -
|
23
|
+
- - ">="
|
24
24
|
- !ruby/object:Gem::Version
|
25
25
|
version: 4.0.0
|
26
26
|
type: :runtime
|
27
27
|
prerelease: false
|
28
28
|
version_requirements: !ruby/object:Gem::Requirement
|
29
29
|
requirements:
|
30
|
-
- -
|
30
|
+
- - ">="
|
31
31
|
- !ruby/object:Gem::Version
|
32
32
|
version: 4.0.0
|
33
33
|
description: Making tests easy on the fingers and eyes
|
@@ -36,11 +36,11 @@ executables: []
|
|
36
36
|
extensions: []
|
37
37
|
extra_rdoc_files: []
|
38
38
|
files:
|
39
|
-
- .gitignore
|
40
|
-
- .hound.yml
|
41
|
-
- .hound_config/ruby.yml
|
42
|
-
- .travis.yml
|
43
|
-
- .yardopts
|
39
|
+
- ".gitignore"
|
40
|
+
- ".hound.yml"
|
41
|
+
- ".hound_config/ruby.yml"
|
42
|
+
- ".travis.yml"
|
43
|
+
- ".yardopts"
|
44
44
|
- Appraisals
|
45
45
|
- CONTRIBUTING.md
|
46
46
|
- Gemfile
|
@@ -355,17 +355,17 @@ require_paths:
|
|
355
355
|
- lib
|
356
356
|
required_ruby_version: !ruby/object:Gem::Requirement
|
357
357
|
requirements:
|
358
|
-
- -
|
358
|
+
- - ">="
|
359
359
|
- !ruby/object:Gem::Version
|
360
360
|
version: 2.0.0
|
361
361
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
362
362
|
requirements:
|
363
|
-
- -
|
363
|
+
- - ">="
|
364
364
|
- !ruby/object:Gem::Version
|
365
365
|
version: '0'
|
366
366
|
requirements: []
|
367
367
|
rubyforge_project:
|
368
|
-
rubygems_version: 2.
|
368
|
+
rubygems_version: 2.5.1
|
369
369
|
signing_key:
|
370
370
|
specification_version: 4
|
371
371
|
summary: Making tests easy on the fingers and eyes
|