shoulda-matchers 3.1.0 → 3.1.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|