sexy_pg_constraints 0.1.2

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.
@@ -0,0 +1,572 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ # Database spc_test should be created manually.
4
+ ActiveRecord::Base.establish_connection(:adapter => "postgresql", :database => "spc_test")
5
+
6
+ # Setting up sample migrations
7
+ class CreateBooks < ActiveRecord::Migration
8
+ def self.up
9
+ create_table :books do |t|
10
+ t.string :title
11
+ t.string :author
12
+ t.integer :author_id
13
+ t.integer :quantity
14
+ t.string :isbn
15
+ end
16
+ end
17
+
18
+ def self.down
19
+ drop_table :books
20
+ end
21
+ end
22
+
23
+ class CreateAuthors < ActiveRecord::Migration
24
+ def self.up
25
+ create_table :authors do |t|
26
+ t.string :name
27
+ t.string :bio
28
+ end
29
+ end
30
+
31
+ def self.down
32
+ drop_table :authors
33
+ end
34
+ end
35
+
36
+ class Book < ActiveRecord::Base; end
37
+ class Author < ActiveRecord::Base; end
38
+
39
+ class SexyPgConstraintsTest < Test::Unit::TestCase
40
+ def setup
41
+ CreateBooks.up
42
+ CreateAuthors.up
43
+ end
44
+
45
+ def teardown
46
+ CreateBooks.down
47
+ CreateAuthors.down
48
+ end
49
+
50
+ def test_should_create_book
51
+ Book.create
52
+ assert_equal 1, Book.count
53
+ end
54
+
55
+ def test_whitelist
56
+ ActiveRecord::Migration.constrain :books, :author, :whitelist => %w(whitelisted1 whitelisted2 whitelisted3)
57
+
58
+ assert_prohibits :author, :whitelist do |book|
59
+ book.author = 'not_whitelisted'
60
+ end
61
+
62
+ assert_allows do |book|
63
+ book.author = 'whitelisted2'
64
+ end
65
+
66
+ ActiveRecord::Migration.deconstrain :books, :author, :whitelist
67
+
68
+ assert_allows do |book|
69
+ book.author = 'not_whitelisted'
70
+ end
71
+ end
72
+
73
+ def test_blacklist
74
+ ActiveRecord::Migration.constrain :books, :author, :blacklist => %w(blacklisted1 blacklisted2 blacklisted3)
75
+
76
+ assert_prohibits :author, :blacklist do |book|
77
+ book.author = 'blacklisted2'
78
+ end
79
+
80
+ assert_allows do |book|
81
+ book.author = 'not_blacklisted'
82
+ end
83
+
84
+ ActiveRecord::Migration.deconstrain :books, :author, :blacklist
85
+
86
+ assert_allows do |book|
87
+ book.author = 'blacklisted2'
88
+ end
89
+ end
90
+
91
+ def test_not_blank
92
+ ActiveRecord::Migration.constrain :books, :author, :not_blank => true
93
+
94
+ assert_prohibits :author, :not_blank do |book|
95
+ book.author = ' '
96
+ end
97
+
98
+ assert_allows do |book|
99
+ book.author = 'foo'
100
+ end
101
+
102
+ ActiveRecord::Migration.deconstrain :books, :author, :not_blank
103
+
104
+ assert_allows do |book|
105
+ book.author = ' '
106
+ end
107
+ end
108
+
109
+ def test_within_inclusive
110
+ ActiveRecord::Migration.constrain :books, :quantity, :within => 5..11
111
+
112
+ assert_prohibits :quantity, :within do |book|
113
+ book.quantity = 12
114
+ end
115
+
116
+ assert_prohibits :quantity, :within do |book|
117
+ book.quantity = 4
118
+ end
119
+
120
+ assert_allows do |book|
121
+ book.quantity = 7
122
+ end
123
+
124
+ ActiveRecord::Migration.deconstrain :books, :quantity, :within
125
+
126
+ assert_allows do |book|
127
+ book.quantity = 12
128
+ end
129
+ end
130
+
131
+ def test_within_non_inclusive
132
+ ActiveRecord::Migration.constrain :books, :quantity, :within => 5...11
133
+
134
+ assert_prohibits :quantity, :within do |book|
135
+ book.quantity = 11
136
+ end
137
+
138
+ assert_prohibits :quantity, :within do |book|
139
+ book.quantity = 4
140
+ end
141
+
142
+ assert_allows do |book|
143
+ book.quantity = 10
144
+ end
145
+
146
+ ActiveRecord::Migration.deconstrain :books, :quantity, :within
147
+
148
+ assert_allows do |book|
149
+ book.quantity = 11
150
+ end
151
+ end
152
+
153
+ def test_length_within_inclusive
154
+ ActiveRecord::Migration.constrain :books, :title, :length_within => 5..11
155
+
156
+ assert_prohibits :title, :length_within do |book|
157
+ book.title = 'abcdefghijkl'
158
+ end
159
+
160
+ assert_prohibits :title, :length_within do |book|
161
+ book.title = 'abcd'
162
+ end
163
+
164
+ assert_allows do |book|
165
+ book.title = 'abcdefg'
166
+ end
167
+
168
+ ActiveRecord::Migration.deconstrain :books, :title, :length_within
169
+
170
+ assert_allows do |book|
171
+ book.title = 'abcdefghijkl'
172
+ end
173
+ end
174
+
175
+ def test_length_within_non_inclusive
176
+ ActiveRecord::Migration.constrain :books, :title, :length_within => 5...11
177
+
178
+ assert_prohibits :title, :length_within do |book|
179
+ book.title = 'abcdefghijk'
180
+ end
181
+
182
+ assert_prohibits :title, :length_within do |book|
183
+ book.title = 'abcd'
184
+ end
185
+
186
+ assert_allows do |book|
187
+ book.title = 'abcdefg'
188
+ end
189
+
190
+ ActiveRecord::Migration.deconstrain :books, :title, :length_within
191
+
192
+ assert_allows do |book|
193
+ book.title = 'abcdefghijk'
194
+ end
195
+ end
196
+
197
+ def test_email
198
+ ActiveRecord::Migration.constrain :books, :author, :email => true
199
+
200
+ assert_prohibits :author, :email do |book|
201
+ book.author = 'blah@example'
202
+ end
203
+
204
+ assert_allows do |book|
205
+ book.author = 'blah@example.com'
206
+ end
207
+
208
+ ActiveRecord::Migration.deconstrain :books, :author, :email
209
+
210
+ assert_allows do |book|
211
+ book.author = 'blah@example'
212
+ end
213
+ end
214
+
215
+ def test_alphanumeric
216
+ ActiveRecord::Migration.constrain :books, :title, :alphanumeric => true
217
+
218
+ assert_prohibits :title, :alphanumeric do |book|
219
+ book.title = 'asdf@asdf'
220
+ end
221
+
222
+ assert_allows do |book|
223
+ book.title = 'asdf'
224
+ end
225
+
226
+ ActiveRecord::Migration.deconstrain :books, :title, :alphanumeric
227
+
228
+ assert_allows do |book|
229
+ book.title = 'asdf@asdf'
230
+ end
231
+ end
232
+
233
+ def test_positive
234
+ ActiveRecord::Migration.constrain :books, :quantity, :positive => true
235
+
236
+ assert_prohibits :quantity, :positive do |book|
237
+ book.quantity = -1
238
+ end
239
+
240
+ assert_allows do |book|
241
+ book.quantity = 0
242
+ end
243
+
244
+ assert_allows do |book|
245
+ book.quantity = 1
246
+ end
247
+
248
+ ActiveRecord::Migration.deconstrain :books, :quantity, :positive
249
+
250
+ assert_allows do |book|
251
+ book.quantity = -1
252
+ end
253
+ end
254
+
255
+ def test_odd
256
+ ActiveRecord::Migration.constrain :books, :quantity, :odd => true
257
+
258
+ assert_prohibits :quantity, :odd do |book|
259
+ book.quantity = 2
260
+ end
261
+
262
+ assert_allows do |book|
263
+ book.quantity = 1
264
+ end
265
+
266
+ ActiveRecord::Migration.deconstrain :books, :quantity, :odd
267
+
268
+ assert_allows do |book|
269
+ book.quantity = 2
270
+ end
271
+ end
272
+
273
+ def test_even
274
+ ActiveRecord::Migration.constrain :books, :quantity, :even => true
275
+
276
+ assert_prohibits :quantity, :even do |book|
277
+ book.quantity = 1
278
+ end
279
+
280
+ assert_allows do |book|
281
+ book.quantity = 2
282
+ end
283
+
284
+ ActiveRecord::Migration.deconstrain :books, :quantity, :even
285
+
286
+ assert_allows do |book|
287
+ book.quantity = 1
288
+ end
289
+ end
290
+
291
+ def test_unique
292
+ ActiveRecord::Migration.constrain :books, :isbn, :unique => true
293
+
294
+ assert_allows do |book|
295
+ book.isbn = 'foo'
296
+ end
297
+
298
+ assert_prohibits :isbn, :unique, 'unique' do |book|
299
+ book.isbn = 'foo'
300
+ end
301
+
302
+ ActiveRecord::Migration.deconstrain :books, :isbn, :unique
303
+
304
+ assert_allows do |book|
305
+ book.isbn = 'foo'
306
+ end
307
+ end
308
+
309
+ def test_exact_length
310
+ ActiveRecord::Migration.constrain :books, :isbn, :exact_length => 5
311
+
312
+ assert_prohibits :isbn, :exact_length do |book|
313
+ book.isbn = '123456'
314
+ end
315
+
316
+ assert_prohibits :isbn, :exact_length do |book|
317
+ book.isbn = '1234'
318
+ end
319
+
320
+ assert_allows do |book|
321
+ book.isbn = '12345'
322
+ end
323
+
324
+ ActiveRecord::Migration.deconstrain :books, :isbn, :exact_length
325
+
326
+ assert_allows do |book|
327
+ book.isbn = '123456'
328
+ end
329
+ end
330
+
331
+ def test_format_case_insensitive
332
+ ActiveRecord::Migration.constrain :books, :title, :format => /^[a-z]+$/i
333
+
334
+ assert_prohibits :title, :format do |book|
335
+ book.title = 'abc3'
336
+ end
337
+
338
+ assert_prohibits :title, :format do |book|
339
+ book.title = ''
340
+ end
341
+
342
+ assert_allows do |book|
343
+ book.title = 'abc'
344
+ end
345
+
346
+ assert_allows do |book|
347
+ book.title = 'ABc'
348
+ end
349
+
350
+ ActiveRecord::Migration.deconstrain :books, :title, :format
351
+
352
+ assert_allows do |book|
353
+ book.title = 'abc3'
354
+ end
355
+ end
356
+
357
+ def test_format_case_sensitive
358
+ ActiveRecord::Migration.constrain :books, :title, :format => /^[a-z]+$/
359
+
360
+ assert_prohibits :title, :format do |book|
361
+ book.title = 'aBc'
362
+ end
363
+
364
+ assert_allows do |book|
365
+ book.title = 'abc'
366
+ end
367
+
368
+ ActiveRecord::Migration.deconstrain :books, :title, :format
369
+
370
+ assert_allows do |book|
371
+ book.title = 'aBc'
372
+ end
373
+ end
374
+
375
+ def test_reference
376
+ ActiveRecord::Migration.constrain :books, :author_id, :reference => {:authors => :id}
377
+
378
+ assert_prohibits :author_id, :reference, 'foreign key' do |book|
379
+ book.author_id = 1
380
+ end
381
+
382
+ author = Author.new
383
+ author.name = "Mark Twain"
384
+ author.bio = "American writer"
385
+ assert author.save
386
+
387
+ assert_equal 1, author.id
388
+
389
+ assert_allows do |book|
390
+ book.author_id = 1
391
+ end
392
+
393
+ ActiveRecord::Migration.deconstrain :books, :author_id, :reference
394
+
395
+ assert_allows do |book|
396
+ book.author_id = 2
397
+ end
398
+ end
399
+
400
+ def test_reference_with_on_delete
401
+ ActiveRecord::Migration.constrain :books, :author_id, :reference => {:authors => :id, :on_delete => :cascade}
402
+
403
+ author = Author.new
404
+ author.name = "Mark Twain"
405
+ author.bio = "American writer"
406
+ assert author.save
407
+
408
+ assert_equal 1, Author.count
409
+
410
+ assert_allows do |book|
411
+ book.title = "The Adventures of Tom Sawyer"
412
+ book.author_id = 1
413
+ end
414
+
415
+ assert_allows do |book|
416
+ book.title = "The Adventures of Huckleberry Finn"
417
+ book.author_id = 1
418
+ end
419
+
420
+ author.destroy
421
+
422
+ assert_equal 0, Author.count
423
+ assert_equal 0, Book.count
424
+ end
425
+
426
+ def test_block_syntax
427
+ ActiveRecord::Migration.constrain :books do |t|
428
+ t.title :not_blank => true
429
+ t.isbn :exact_length => 15
430
+ t.author :alphanumeric => true
431
+ end
432
+
433
+ assert_prohibits :title, :not_blank do |book|
434
+ book.title = ' '
435
+ end
436
+
437
+ assert_prohibits :isbn, :exact_length do |book|
438
+ book.isbn = 'asdf'
439
+ end
440
+
441
+ assert_prohibits :author, :alphanumeric do |book|
442
+ book.author = 'foo#bar'
443
+ end
444
+
445
+ ActiveRecord::Migration.deconstrain :books do |t|
446
+ t.title :not_blank
447
+ t.isbn :exact_length
448
+ t.author :alphanumeric
449
+ end
450
+
451
+ assert_allows do |book|
452
+ book.title = ' '
453
+ book.isbn = 'asdf'
454
+ book.author = 'foo#bar'
455
+ end
456
+ end
457
+
458
+ def test_multiple_constraints_per_line
459
+ ActiveRecord::Migration.constrain :books do |t|
460
+ t.title :not_blank => true, :alphanumeric => true, :blacklist => %w(foo bar)
461
+ end
462
+
463
+ assert_prohibits :title, :not_blank do |book|
464
+ book.title = ' '
465
+ end
466
+
467
+ assert_prohibits :title, :alphanumeric do |book|
468
+ book.title = 'asdf@asdf'
469
+ end
470
+
471
+ assert_prohibits :title, :blacklist do |book|
472
+ book.title = 'foo'
473
+ end
474
+
475
+ ActiveRecord::Migration.deconstrain :books do |t|
476
+ t.title :not_blank, :alphanumeric, :blacklist
477
+ end
478
+
479
+ assert_allows do |book|
480
+ book.title = ' '
481
+ end
482
+
483
+ assert_allows do |book|
484
+ book.title = 'asdf@asdf'
485
+ end
486
+
487
+ assert_allows do |book|
488
+ book.title = 'foo'
489
+ end
490
+ end
491
+
492
+ def test_multicolumn_constraint
493
+ ActiveRecord::Migration.constrain :books, [:title, :isbn], :unique => true
494
+
495
+ assert_allows do |book|
496
+ book.title = 'foo'
497
+ book.isbn = 'bar'
498
+ end
499
+
500
+ assert_allows do |book|
501
+ book.title = 'foo'
502
+ book.isbn = 'foo'
503
+ end
504
+
505
+ assert_prohibits [:title, :isbn], :unique, 'unique' do |book|
506
+ book.title = 'foo'
507
+ book.isbn = 'bar'
508
+ end
509
+
510
+ ActiveRecord::Migration.deconstrain :books, [:title, :isbn], :unique
511
+
512
+ assert_allows do |book|
513
+ book.title = 'foo'
514
+ book.isbn = 'bar'
515
+ end
516
+ end
517
+
518
+ def test_multicolumn_constraint_block_syntax
519
+ ActiveRecord::Migration.constrain :books do |t|
520
+ t[:title, :isbn].all :unique => true
521
+ end
522
+
523
+ assert_allows do |book|
524
+ book.title = 'foo'
525
+ book.isbn = 'bar'
526
+ end
527
+
528
+ assert_allows do |book|
529
+ book.title = 'foo'
530
+ book.isbn = 'foo'
531
+ end
532
+
533
+ assert_prohibits [:title, :isbn], :unique, 'unique' do |book|
534
+ book.title = 'foo'
535
+ book.isbn = 'bar'
536
+ end
537
+
538
+ ActiveRecord::Migration.deconstrain :books do |t|
539
+ t[:title, :isbn].all :unique
540
+ end
541
+
542
+ assert_allows do |book|
543
+ book.title = 'foo'
544
+ book.isbn = 'bar'
545
+ end
546
+ end
547
+
548
+ private
549
+ def assert_prohibits(column, constraint, constraint_type = 'check')
550
+ column = column.join('_') if column.respond_to?(:join)
551
+
552
+ book = Book.new
553
+ yield(book)
554
+ assert book.valid?
555
+ error = assert_raise ActiveRecord::StatementInvalid do
556
+ book.save
557
+ end
558
+ assert_match /PGError/, error.message
559
+ assert_match /violates #{constraint_type} constraint "books_#{column}_#{constraint}"/, error.message
560
+ end
561
+
562
+ def assert_allows
563
+ first_count = Book.count
564
+ book = Book.new
565
+ yield(book)
566
+ assert book.valid?
567
+ assert_nothing_raised do
568
+ book.save
569
+ end
570
+ assert_equal first_count + 1, Book.count
571
+ end
572
+ end
@@ -0,0 +1,5 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require 'active_record'
4
+ require 'postgresql_adapter'
5
+ require "sexy_pg_constraints"
metadata ADDED
@@ -0,0 +1,80 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sexy_pg_constraints
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.2
5
+ platform: ruby
6
+ authors:
7
+ - Maxim Chernyak
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-04 00:00:00 -04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Use migrations and simple syntax to manage constraints in PostgreSQL DB.
17
+ email: max@bitsonnet.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - CHANGELOG.rdoc
24
+ - README.rdoc
25
+ - lib/constrainer.rb
26
+ - lib/constraints.rb
27
+ - lib/deconstrainer.rb
28
+ - lib/helpers.rb
29
+ - lib/sexy_pg_constraints.rb
30
+ files:
31
+ - CHANGELOG.rdoc
32
+ - Manifest
33
+ - README.rdoc
34
+ - Rakefile
35
+ - init.rb
36
+ - lib/constrainer.rb
37
+ - lib/constraints.rb
38
+ - lib/deconstrainer.rb
39
+ - lib/helpers.rb
40
+ - lib/sexy_pg_constraints.rb
41
+ - sexy_pg_constraints.gemspec
42
+ - test/postgresql_adapter.rb
43
+ - test/sexy_pg_constraints_test.rb
44
+ - test/test_helper.rb
45
+ has_rdoc: true
46
+ homepage: http://github.com/maxim/sexy_pg_constraints
47
+ licenses: []
48
+
49
+ post_install_message:
50
+ rdoc_options:
51
+ - --line-numbers
52
+ - --inline-source
53
+ - --title
54
+ - Sexy_pg_constraints
55
+ - --main
56
+ - README.rdoc
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: "1.2"
70
+ version:
71
+ requirements: []
72
+
73
+ rubyforge_project: sexy_pg_constraints
74
+ rubygems_version: 1.3.5
75
+ signing_key:
76
+ specification_version: 3
77
+ summary: If you're on PostgreSQL and see the importance of data-layer constraints - this gem/plugin is for you. It integrates constraints into PostgreSQL adapter so you can add/remove them in your migrations. You get two simple methods for adding/removing constraints, as well as a pack of pre-made constraints.
78
+ test_files:
79
+ - test/sexy_pg_constraints_test.rb
80
+ - test/test_helper.rb