schema_validations 2.2.0 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/.github/workflows/prs.yml +134 -0
- data/.gitignore +1 -0
- data/.simplecov +20 -0
- data/.travis.yml +6 -3
- data/Gemfile +5 -0
- data/README.md +25 -6
- data/Rakefile +2 -0
- data/gemfiles/Gemfile.base +1 -1
- data/gemfiles/activerecord-5.2/Gemfile.base +4 -0
- data/gemfiles/activerecord-5.2/Gemfile.mysql2 +10 -0
- data/gemfiles/activerecord-5.2/Gemfile.postgresql +10 -0
- data/gemfiles/{activerecord-5.0 → activerecord-5.2}/Gemfile.sqlite3 +3 -3
- data/gemfiles/activerecord-6.0/Gemfile.base +4 -0
- data/gemfiles/activerecord-6.0/Gemfile.mysql2 +10 -0
- data/gemfiles/activerecord-6.0/Gemfile.postgresql +10 -0
- data/gemfiles/{activerecord-4.2 → activerecord-6.0}/Gemfile.sqlite3 +3 -3
- data/init.rb +2 -0
- data/lib/schema_validations/active_record/type.rb +2 -0
- data/lib/schema_validations/active_record/validations.rb +8 -1
- data/lib/schema_validations/railtie.rb +2 -0
- data/lib/schema_validations/validators/not_nil_validator.rb +13 -0
- data/lib/schema_validations/version.rb +3 -1
- data/lib/schema_validations.rb +3 -0
- data/schema_dev.yml +5 -3
- data/schema_validations.gemspec +23 -27
- data/spec/spec_helper.rb +22 -18
- data/spec/support/active_model.rb +2 -0
- data/spec/validations_spec.rb +138 -73
- metadata +38 -94
- data/gemfiles/activerecord-4.2/Gemfile.base +0 -3
- data/gemfiles/activerecord-4.2/Gemfile.mysql2 +0 -10
- data/gemfiles/activerecord-4.2/Gemfile.postgresql +0 -10
- data/gemfiles/activerecord-5.0/Gemfile.base +0 -3
- data/gemfiles/activerecord-5.0/Gemfile.mysql2 +0 -10
- data/gemfiles/activerecord-5.0/Gemfile.postgresql +0 -10
- data/spec/schema_validations.sqlite3 +0 -0
data/spec/validations_spec.rb
CHANGED
@@ -1,38 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
4
|
|
3
5
|
describe "Validations" do
|
4
6
|
|
5
7
|
before(:each) do
|
6
|
-
|
8
|
+
define_schema do
|
7
9
|
|
8
|
-
create_table :articles, :
|
9
|
-
t.string :title, :
|
10
|
-
t.text :content, :
|
10
|
+
create_table :articles, force: true do |t|
|
11
|
+
t.string :title, limit: 50
|
12
|
+
t.text :content, null: false
|
11
13
|
t.integer :state
|
12
14
|
t.integer :votes
|
13
|
-
t.float :average_mark, :
|
14
|
-
t.boolean :active, :
|
15
|
-
t.decimal :max10, :
|
16
|
-
t.decimal :arbitrary, :
|
17
|
-
t.decimal :max100, :
|
15
|
+
t.float :average_mark, null: false
|
16
|
+
t.boolean :active, null: false
|
17
|
+
t.decimal :max10, precision: 2, scale: 1
|
18
|
+
t.decimal :arbitrary, precision: nil, scale: nil
|
19
|
+
t.decimal :max100, precision: 2, scale: nil
|
18
20
|
end
|
19
|
-
add_index :articles, :title, :
|
20
|
-
add_index :articles, [:state, :active], :
|
21
|
+
add_index :articles, :title, unique: true
|
22
|
+
add_index :articles, [:state, :active], unique: true
|
21
23
|
|
22
|
-
create_table :reviews, :
|
23
|
-
t.integer :article_id, :
|
24
|
-
t.string :author, :
|
25
|
-
t.string :content, :
|
24
|
+
create_table :reviews, force: true do |t|
|
25
|
+
t.integer :article_id, null: false
|
26
|
+
t.string :author, null: false
|
27
|
+
t.string :content, limit: 200
|
26
28
|
t.string :type
|
27
|
-
t.timestamps :
|
29
|
+
t.timestamps null: false
|
28
30
|
end
|
29
|
-
add_index :reviews, :article_id, :
|
31
|
+
add_index :reviews, :article_id, unique: true
|
30
32
|
|
31
|
-
create_table :article_reviews, :
|
33
|
+
create_table :article_reviews, force: true do |t|
|
32
34
|
t.integer :article_id
|
33
35
|
t.integer :review_id
|
34
36
|
end
|
35
|
-
add_index :article_reviews, [:article_id, :review_id], :
|
37
|
+
add_index :article_reviews, [:article_id, :review_id], unique: true
|
36
38
|
end
|
37
39
|
end
|
38
40
|
|
@@ -43,8 +45,8 @@ describe "Validations" do
|
|
43
45
|
|
44
46
|
class Review < ActiveRecord::Base
|
45
47
|
belongs_to :article
|
46
|
-
belongs_to :news_article, :
|
47
|
-
schema_validations :
|
48
|
+
belongs_to :news_article, class_name: 'Article', foreign_key: :article_id
|
49
|
+
schema_validations except: :content
|
48
50
|
end
|
49
51
|
|
50
52
|
class ArticleReview < ActiveRecord::Base
|
@@ -81,15 +83,15 @@ describe "Validations" do
|
|
81
83
|
end
|
82
84
|
|
83
85
|
it "should check title length" do
|
84
|
-
expect(Article.new(:
|
86
|
+
expect(Article.new(title: 'a' * 100).error_on(:title).size).to eq(1)
|
85
87
|
end
|
86
88
|
|
87
89
|
it "should validate state numericality" do
|
88
|
-
expect(Article.new(:
|
90
|
+
expect(Article.new(state: 'unknown').error_on(:state).size).to eq(1)
|
89
91
|
end
|
90
92
|
|
91
93
|
it "should validate if state is integer" do
|
92
|
-
expect(Article.new(:
|
94
|
+
expect(Article.new(state: 1.23).error_on(:state).size).to eq(1)
|
93
95
|
end
|
94
96
|
|
95
97
|
it "should validate the range of votes" do
|
@@ -115,28 +117,28 @@ describe "Validations" do
|
|
115
117
|
expect(Article.new(max100: -100).error_on(:max100).size).to eq(1)
|
116
118
|
end
|
117
119
|
|
118
|
-
it "should not validate the range of arbitrary decimal", :
|
120
|
+
it "should not validate the range of arbitrary decimal", mysql: :skip do # mysql provides a default precision
|
119
121
|
expect(Article.new(arbitrary: Float::MAX).error_on(:arbitrary).size).to eq(0)
|
120
122
|
end
|
121
123
|
|
122
124
|
it "should validate average_mark numericality" do
|
123
|
-
expect(Article.new(:
|
125
|
+
expect(Article.new(average_mark: "high").error_on(:average_mark).size).to eq(1)
|
124
126
|
end
|
125
127
|
|
126
128
|
it "should validate boolean fields" do
|
127
|
-
expect(Article.new(:
|
129
|
+
expect(Article.new(active: nil).error_on(:active).size).to eq(1)
|
128
130
|
end
|
129
131
|
|
130
132
|
it "should validate title uniqueness" do
|
131
133
|
article1 = Article.create(valid_article_attributes)
|
132
|
-
article2 = Article.new(:
|
134
|
+
article2 = Article.new(title: valid_article_attributes[:title])
|
133
135
|
expect(article2.error_on(:title).size).to eq(1)
|
134
136
|
article1.destroy
|
135
137
|
end
|
136
138
|
|
137
139
|
it "should validate state uniqueness in scope of 'active' value" do
|
138
140
|
article1 = Article.create(valid_article_attributes)
|
139
|
-
article2 = Article.new(valid_article_attributes.merge(:
|
141
|
+
article2 = Article.new(valid_article_attributes.merge(title: 'SchemaPlus 2.0 released'))
|
140
142
|
expect(article2).not_to be_valid
|
141
143
|
article2.toggle(:active)
|
142
144
|
expect(article2).to be_valid
|
@@ -151,9 +153,9 @@ describe "Validations" do
|
|
151
153
|
it "should validate uniqueness of belongs_to association" do
|
152
154
|
article = Article.create(valid_article_attributes)
|
153
155
|
expect(article).to be_valid
|
154
|
-
review1 = Review.create(:
|
156
|
+
review1 = Review.create(article: article, author: 'michal')
|
155
157
|
expect(review1).to be_valid
|
156
|
-
review2 = Review.new(:
|
158
|
+
review2 = Review.new(article: article, author: 'michal')
|
157
159
|
expect(review2.error_on(:article_id).size).to be >= 1
|
158
160
|
end
|
159
161
|
|
@@ -162,18 +164,81 @@ describe "Validations" do
|
|
162
164
|
end
|
163
165
|
|
164
166
|
it "should not validate uniqueness when scope is absent" do
|
165
|
-
article_review_1 = ArticleReview.create(:
|
167
|
+
article_review_1 = ArticleReview.create(article_id: 1, review_id: nil)
|
166
168
|
expect(article_review_1).to be_valid
|
167
169
|
|
168
|
-
article_review_2 = ArticleReview.create(:
|
170
|
+
article_review_2 = ArticleReview.create(article_id: 1, review_id: nil)
|
169
171
|
expect(article_review_2).to be_valid
|
170
172
|
|
171
|
-
article_review_3 = ArticleReview.create(:
|
173
|
+
article_review_3 = ArticleReview.create(article_id: nil, review_id: 1)
|
172
174
|
expect(article_review_3).to be_valid
|
173
175
|
|
174
|
-
article_review_4 = ArticleReview.create(:
|
176
|
+
article_review_4 = ArticleReview.create(article_id: nil, review_id: 1)
|
175
177
|
expect(article_review_4).to be_valid
|
176
178
|
end
|
179
|
+
|
180
|
+
context 'when NOT NULL validations' do
|
181
|
+
before(:each) do
|
182
|
+
ActiveRecord::Schema.define do
|
183
|
+
create_table :anti_nulls, force: true do |t|
|
184
|
+
t.string :no_default, null: false
|
185
|
+
t.string :blank_default, default: '', null: false
|
186
|
+
t.string :non_blank_default, default: 'not blank', null: false
|
187
|
+
end
|
188
|
+
end
|
189
|
+
with_auto_validations do
|
190
|
+
class AntiNull < ActiveRecord::Base
|
191
|
+
def self.all_blank
|
192
|
+
@all_blank ||= AntiNull.new(
|
193
|
+
no_default: '',
|
194
|
+
blank_default: '',
|
195
|
+
non_blank_default: ''
|
196
|
+
)
|
197
|
+
end
|
198
|
+
|
199
|
+
def self.all_non_blank
|
200
|
+
@all_non_blank ||= AntiNull.new(
|
201
|
+
no_default: 'foo',
|
202
|
+
blank_default: 'bar',
|
203
|
+
non_blank_default: 'baz'
|
204
|
+
)
|
205
|
+
end
|
206
|
+
|
207
|
+
def self.all_nil
|
208
|
+
@all_nil ||= AntiNull.new(
|
209
|
+
no_default: nil,
|
210
|
+
blank_default: nil,
|
211
|
+
non_blank_default: nil
|
212
|
+
)
|
213
|
+
end
|
214
|
+
|
215
|
+
def self.non_null_with(**fields)
|
216
|
+
opts = { no_default: 'foo' }.merge!(fields)
|
217
|
+
AntiNull.new **opts
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
|
223
|
+
end
|
224
|
+
|
225
|
+
it 'should fail validation on empty fields only if the default value is not blank' do
|
226
|
+
expect(AntiNull.all_nil.error_on(:no_default).size).to eq(1)
|
227
|
+
expect(AntiNull.all_nil.error_on(:blank_default).size).to eq(1)
|
228
|
+
expect(AntiNull.all_nil.error_on(:non_blank_default).size).to eq(1)
|
229
|
+
end
|
230
|
+
|
231
|
+
it 'should fail validation on empty fields only if the default value is not blank' do
|
232
|
+
expect(AntiNull.all_blank.error_on(:no_default).size).to eq(1)
|
233
|
+
expect(AntiNull.all_blank.error_on(:non_blank_default).size).to eq(1)
|
234
|
+
expect(AntiNull.all_blank.error_on(:blank_default)).to be_empty
|
235
|
+
end
|
236
|
+
|
237
|
+
it 'should not fail if fields are neither nil nor empty' do
|
238
|
+
expect(AntiNull.all_non_blank).to be_valid
|
239
|
+
end
|
240
|
+
|
241
|
+
end
|
177
242
|
end
|
178
243
|
|
179
244
|
context "auto-created but changed" do
|
@@ -182,62 +247,62 @@ describe "Validations" do
|
|
182
247
|
class Article < ActiveRecord::Base ; end
|
183
248
|
class Review < ActiveRecord::Base
|
184
249
|
belongs_to :article
|
185
|
-
belongs_to :news_article, :
|
250
|
+
belongs_to :news_article, class_name: 'Article', foreign_key: :article_id
|
186
251
|
end
|
187
252
|
end
|
188
253
|
@too_big_content = 'a' * 1000
|
189
254
|
end
|
190
255
|
|
191
256
|
it "would normally have an error" do
|
192
|
-
@review = Review.new(:
|
257
|
+
@review = Review.new(content: @too_big_content)
|
193
258
|
expect(@review.error_on(:content).size).to eq(1)
|
194
259
|
expect(@review.error_on(:author).size).to eq(1)
|
195
260
|
end
|
196
261
|
|
197
262
|
it "shouldn't validate fields passed to :except option" do
|
198
|
-
Review.schema_validations :
|
199
|
-
@review = Review.new(:
|
263
|
+
Review.schema_validations except: :content
|
264
|
+
@review = Review.new(content: @too_big_content)
|
200
265
|
expect(@review.errors_on(:content).size).to eq(0)
|
201
266
|
expect(@review.error_on(:author).size).to eq(1)
|
202
267
|
end
|
203
268
|
|
204
269
|
it "shouldn't validate the fields in default whitelist" do
|
205
|
-
Review.schema_validations :
|
270
|
+
Review.schema_validations except: :content
|
206
271
|
expect(Review.new.error_on(:updated_at).size).to eq(0)
|
207
272
|
expect(Review.new.error_on(:created_at).size).to eq(0)
|
208
273
|
end
|
209
274
|
|
210
275
|
it "shouldn't validate the fields in whitelist" do
|
211
|
-
Review.schema_validations :
|
276
|
+
Review.schema_validations except: :content, whitelist: [:updated_at]
|
212
277
|
expect(Review.new.error_on(:updated_at).size).to eq(0)
|
213
278
|
expect(Review.new.error_on(:created_at).size).to eq(1)
|
214
279
|
end
|
215
280
|
|
216
281
|
it "shouldn't validate types passed to :except_type option using full validation" do
|
217
|
-
Review.schema_validations :
|
218
|
-
@review = Review.new(:
|
282
|
+
Review.schema_validations except_type: :validates_length_of
|
283
|
+
@review = Review.new(content: @too_big_content)
|
219
284
|
expect(@review.errors_on(:content).size).to eq(0)
|
220
285
|
expect(@review.error_on(:author).size).to eq(1)
|
221
286
|
end
|
222
287
|
|
223
288
|
it "shouldn't validate types passed to :except_type option using shorthand" do
|
224
|
-
Review.schema_validations :
|
225
|
-
@review = Review.new(:
|
289
|
+
Review.schema_validations except_type: :length
|
290
|
+
@review = Review.new(content: @too_big_content)
|
226
291
|
expect(@review.errors_on(:content).size).to eq(0)
|
227
292
|
expect(@review.error_on(:author).size).to eq(1)
|
228
293
|
end
|
229
294
|
|
230
295
|
it "should only validate type passed to :only_type option" do
|
231
|
-
Review.schema_validations :
|
232
|
-
@review = Review.new(:
|
296
|
+
Review.schema_validations only_type: :length
|
297
|
+
@review = Review.new(content: @too_big_content)
|
233
298
|
expect(@review.error_on(:content).size).to eq(1)
|
234
299
|
expect(@review.errors_on(:author).size).to eq(0)
|
235
300
|
end
|
236
301
|
|
237
302
|
|
238
303
|
it "shouldn't create validations if locally disabled" do
|
239
|
-
Review.schema_validations :
|
240
|
-
@review = Review.new(:
|
304
|
+
Review.schema_validations auto_create: false
|
305
|
+
@review = Review.new(content: @too_big_content)
|
241
306
|
expect(@review.errors_on(:content).size).to eq(0)
|
242
307
|
expect(@review.error_on(:author).size).to eq(0)
|
243
308
|
end
|
@@ -251,23 +316,23 @@ describe "Validations" do
|
|
251
316
|
before(:each) do
|
252
317
|
class Review < ActiveRecord::Base
|
253
318
|
belongs_to :article
|
254
|
-
belongs_to :news_article, :
|
319
|
+
belongs_to :news_article, class_name: 'Article', foreign_key: :article_id
|
255
320
|
end
|
256
321
|
@too_big_content = 'a' * 1000
|
257
322
|
end
|
258
323
|
|
259
324
|
it "should not create validation" do
|
260
|
-
expect(Review.new(:
|
325
|
+
expect(Review.new(content: @too_big_title).errors_on(:content).size).to eq(0)
|
261
326
|
end
|
262
327
|
|
263
328
|
it "should create validation if locally enabled explicitly" do
|
264
|
-
Review.schema_validations :
|
265
|
-
expect(Review.new(:
|
329
|
+
Review.schema_validations auto_create: true
|
330
|
+
expect(Review.new(content: @too_big_content).error_on(:content).size).to eq(1)
|
266
331
|
end
|
267
332
|
|
268
333
|
it "should create validation if locally enabled implicitly" do
|
269
334
|
Review.schema_validations
|
270
|
-
expect(Review.new(:
|
335
|
+
expect(Review.new(content: @too_big_content).error_on(:content).size).to eq(1)
|
271
336
|
end
|
272
337
|
|
273
338
|
end
|
@@ -275,18 +340,18 @@ describe "Validations" do
|
|
275
340
|
context "manually invoked" do
|
276
341
|
before(:each) do
|
277
342
|
class Article < ActiveRecord::Base ; end
|
278
|
-
Article.schema_validations :
|
343
|
+
Article.schema_validations only: [:title, :state]
|
279
344
|
|
280
345
|
class Review < ActiveRecord::Base
|
281
346
|
belongs_to :dummy_association
|
282
|
-
schema_validations :
|
347
|
+
schema_validations except: :content
|
283
348
|
end
|
284
349
|
end
|
285
350
|
|
286
351
|
it "should validate fields passed to :only option" do
|
287
352
|
too_big_title = 'a' * 100
|
288
353
|
wrong_state = 'unknown'
|
289
|
-
article = Article.new(:
|
354
|
+
article = Article.new(title: too_big_title, state: wrong_state)
|
290
355
|
expect(article.error_on(:title).size).to eq(1)
|
291
356
|
expect(article.error_on(:state).size).to eq(1)
|
292
357
|
end
|
@@ -317,7 +382,7 @@ describe "Validations" do
|
|
317
382
|
belongs_to :article
|
318
383
|
end
|
319
384
|
@columns = Review.content_columns.dup
|
320
|
-
Review.schema_validations :
|
385
|
+
Review.schema_validations only: [:title]
|
321
386
|
end
|
322
387
|
|
323
388
|
it "shouldn't validate associations not included in :only option" do
|
@@ -352,9 +417,9 @@ describe "Validations" do
|
|
352
417
|
context "when used with enum" do
|
353
418
|
it "does not validate numericality" do
|
354
419
|
class Article < ActiveRecord::Base
|
355
|
-
enum :
|
420
|
+
enum state: [:happy, :sad]
|
356
421
|
end
|
357
|
-
expect(Article.new(valid_article_attributes.merge(:
|
422
|
+
expect(Article.new(valid_article_attributes.merge(state: :happy))).to be_valid
|
358
423
|
end
|
359
424
|
end if ActiveRecord::Base.respond_to? :enum
|
360
425
|
|
@@ -366,11 +431,11 @@ describe "Validations" do
|
|
366
431
|
context 'without scope' do
|
367
432
|
before do
|
368
433
|
ActiveRecord::Schema.define do
|
369
|
-
create_table :books, :
|
434
|
+
create_table :books, force: true do |t|
|
370
435
|
t.string :title
|
371
436
|
end
|
372
437
|
|
373
|
-
add_index :books, :title, :
|
438
|
+
add_index :books, :title, unique: true
|
374
439
|
end
|
375
440
|
|
376
441
|
with_auto_validations do
|
@@ -390,12 +455,12 @@ describe "Validations" do
|
|
390
455
|
context 'within a scope' do
|
391
456
|
before do
|
392
457
|
ActiveRecord::Schema.define do
|
393
|
-
create_table :folders, :
|
458
|
+
create_table :folders, force: true do |t|
|
394
459
|
t.integer :parent_id
|
395
460
|
t.string :name
|
396
461
|
end
|
397
462
|
|
398
|
-
add_index :folders, [:parent_id, :name], :
|
463
|
+
add_index :folders, [:parent_id, :name], unique: true
|
399
464
|
end
|
400
465
|
|
401
466
|
with_auto_validations do
|
@@ -408,10 +473,10 @@ describe "Validations" do
|
|
408
473
|
it "should validate the uniqueness in a case insensitive manner" do
|
409
474
|
mixed_case_name = 'Schema Validations'
|
410
475
|
parent_folder = Folder.create
|
411
|
-
Folder.create(:
|
476
|
+
Folder.create(parent: parent_folder, name: mixed_case_name)
|
412
477
|
|
413
|
-
expect(Folder.new(:
|
414
|
-
expect(Folder.new(:
|
478
|
+
expect(Folder.new(parent: parent_folder, name: mixed_case_name)).not_to be_valid
|
479
|
+
expect(Folder.new(parent: parent_folder, name: mixed_case_name.downcase)).not_to be_valid
|
415
480
|
end
|
416
481
|
end
|
417
482
|
end
|
@@ -419,7 +484,7 @@ describe "Validations" do
|
|
419
484
|
context 'with optimistic locking' do
|
420
485
|
before do
|
421
486
|
ActiveRecord::Schema.define do
|
422
|
-
create_table :optimistics, :
|
487
|
+
create_table :optimistics, force: true do |t|
|
423
488
|
t.integer :lock_version
|
424
489
|
end
|
425
490
|
end
|
@@ -448,11 +513,11 @@ describe "Validations" do
|
|
448
513
|
|
449
514
|
def valid_article_attributes
|
450
515
|
{
|
451
|
-
:
|
452
|
-
:
|
453
|
-
:
|
454
|
-
:
|
455
|
-
:
|
516
|
+
title: 'SchemaPlus released!',
|
517
|
+
content: "Database matters. Get full use of it but don't write unecessary code. Get SchemaPlus!",
|
518
|
+
state: 3,
|
519
|
+
average_mark: 9.78,
|
520
|
+
active: true
|
456
521
|
}
|
457
522
|
end
|
458
523
|
|