sexy_pg_constraints 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -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