sexy_pg_constraints 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Manifest CHANGED
@@ -11,4 +11,8 @@ lib/sexy_pg_constraints.rb
11
11
  sexy_pg_constraints.gemspec
12
12
  test/postgresql_adapter.rb
13
13
  test/sexy_pg_constraints_test.rb
14
+ test/support/assert_prohibits_allows.rb
15
+ test/support/database.yml
16
+ test/support/database.yml.example
17
+ test/support/models.rb
14
18
  test/test_helper.rb
@@ -106,6 +106,8 @@ Below is the list of constraints available and tested so far.
106
106
  * even
107
107
  * odd
108
108
  * format
109
+ * lowercase
110
+ * xor
109
111
 
110
112
  == Extensibility
111
113
 
@@ -113,8 +115,12 @@ All constraints are located in the lib/constraints.rb. Extending this module wit
113
115
 
114
116
  == TODO
115
117
 
118
+ * Add support for Rails schema.rb
116
119
  * Create better API for adding constraints
117
120
 
121
+ == Contributors
122
+ * Empact[http://github.com/Empact] (Big thanks for lots of work. Better flexibility, more tests, organizing code, bug fixes.)
123
+
118
124
  == License
119
125
 
120
126
  Copyright (c) 2008 Maxim Chernyak
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rubygems'
2
2
  require 'rake'
3
3
  require 'echoe'
4
4
 
5
- Echoe.new('sexy_pg_constraints', '0.1.2') do |p|
5
+ Echoe.new('sexy_pg_constraints', '0.2.0') do |p|
6
6
  p.description = "Use migrations and simple syntax to manage constraints in PostgreSQL DB."
7
7
  p.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."
8
8
  p.url = "http://github.com/maxim/sexy_pg_constraints"
@@ -24,7 +24,7 @@ module SexyPgConstraints
24
24
  def add_constraints(table, column, constraints)
25
25
  constraints.each_pair do |type, options|
26
26
  sql = "alter table #{table} add constraint #{make_title(table, column, type)} " +
27
- SexyPgConstraints::Constraints.send(type, column, options) + ';'
27
+ SexyPgConstraints::Constraints.send(type, table, column, options) + ';'
28
28
 
29
29
  execute sql
30
30
  end
@@ -1,38 +1,38 @@
1
1
  module SexyPgConstraints
2
2
  module Constraints
3
3
  module_function
4
-
4
+
5
5
  ##
6
6
  # Only allow listed values.
7
7
  #
8
- # Example:
8
+ # Example:
9
9
  # constrain :books, :variation, :whitelist => %w(hardcover softcover)
10
10
  #
11
- def whitelist(column, options)
12
- "check (#{column} in (#{ options.collect{|v| "'#{v}'"}.join(',') }))"
11
+ def whitelist(table, column, options)
12
+ "check (#{table}.#{column} in (#{ options.collect{|v| "'#{v}'"}.join(',') }))"
13
13
  end
14
-
15
- ##
14
+
15
+ ##
16
16
  # Prohibit listed values.
17
17
  #
18
- # Example:
18
+ # Example:
19
19
  # constrain :books, :isbn, :blacklist => %w(invalid_isbn1 invalid_isbn2)
20
20
  #
21
- def blacklist(column, options)
22
- "check (#{column} not in (#{ options.collect{|v| "'#{v}'"}.join(',') }))"
21
+ def blacklist(table, column, options)
22
+ "check (#{table}.#{column} not in (#{ options.collect{|v| "'#{v}'"}.join(',') }))"
23
23
  end
24
-
25
- ##
24
+
25
+ ##
26
26
  # The value must have at least 1 non-space character.
27
27
  #
28
28
  # Example:
29
29
  # constrain :books, :title, :not_blank => true
30
30
  #
31
- def not_blank(column, options)
32
- "check ( length(trim(both from #{column})) > 0 )"
31
+ def not_blank(table, column, options)
32
+ "check ( length(trim(both from #{table}.#{column})) > 0 )"
33
33
  end
34
34
 
35
- ##
35
+ ##
36
36
  # The numeric value must be within given range.
37
37
  #
38
38
  # Example:
@@ -40,115 +40,140 @@ module SexyPgConstraints
40
40
  # constrain :books, :year, :within => 1980...2009
41
41
  # (the two lines above do the same thing)
42
42
  #
43
- def within(column, options)
44
- "check (#{column} >= #{options.begin} and #{column} #{options.exclude_end? ? ' < ' : ' <= '} #{options.end})"
43
+ def within(table, column, options)
44
+ column_ref = column.to_s.include?('.') ? column : "#{table}.#{column}"
45
+ "check (#{column_ref} >= #{options.begin} and #{column_ref} #{options.exclude_end? ? ' < ' : ' <= '} #{options.end})"
45
46
  end
46
47
 
47
- ##
48
+ ##
48
49
  # Check the length of strings/text to be within the range.
49
50
  #
50
51
  # Example:
51
52
  # constrain :books, :author, :length_within => 4..50
52
53
  #
53
- def length_within(column, options)
54
- within("length(#{column})", options)
54
+ def length_within(table, column, options)
55
+ within(table, "length(#{table}.#{column})", options)
55
56
  end
56
-
57
- ##
57
+
58
+ ##
58
59
  # Allow only valid email format.
59
60
  #
60
61
  # Example:
61
62
  # constrain :books, :author, :email => true
62
63
  #
63
- def email(column, options)
64
- "check (((#{column})::text ~ E'^([-a-z0-9]+)@([-a-z0-9]+[.]+[a-z]{2,4})$'::text))"
64
+ def email(table, column, options)
65
+ "check (((#{table}.#{column})::text ~ E'^([-a-z0-9]+)@([-a-z0-9]+[.]+[a-z]{2,4})$'::text))"
65
66
  end
66
67
 
67
- ##
68
+ ##
68
69
  # Allow only alphanumeric values.
69
70
  #
70
71
  # Example:
71
72
  # constrain :books, :author, :alphanumeric => true
72
73
  #
73
- def alphanumeric(column, options)
74
- "check (((#{column})::text ~* '^[a-z0-9]+$'::text))"
74
+ def alphanumeric(table, column, options)
75
+ "check (((#{table}.#{column})::text ~* '^[a-z0-9]+$'::text))"
76
+ end
77
+
78
+ ##
79
+ # Allow only lower case values.
80
+ #
81
+ # Example:
82
+ # constrain :books, :author, :lowercase => true
83
+ #
84
+ def lowercase(table, column, options)
85
+ "check (#{table}.#{column} = lower(#{table}.#{column}))"
75
86
  end
76
-
77
- ##
87
+
88
+ ##
78
89
  # Allow only positive values.
79
90
  #
80
91
  # Example:
81
92
  # constrain :books, :quantity, :positive => true
82
93
  #
83
- def positive(column, options)
84
- "check (#{column} >= 0)"
94
+ def positive(table, column, options)
95
+ "check (#{table}.#{column} >= 0)"
85
96
  end
86
-
87
- ##
97
+
98
+ ##
88
99
  # Allow only odd values.
89
100
  #
90
101
  # Example:
91
102
  # constrain :books, :quantity, :odd => true
92
103
  #
93
- def odd(column, options)
94
- "check (mod(#{column}, 2) != 0)"
104
+ def odd(table, column, options)
105
+ "check (mod(#{table}.#{column}, 2) != 0)"
95
106
  end
96
107
 
97
- ##
108
+ ##
98
109
  # Allow only even values.
99
110
  #
100
111
  # Example:
101
112
  # constrain :books, :quantity, :even => true
102
113
  #
103
- def even(column, options)
104
- "check (mod(#{column}, 2) = 0)"
114
+ def even(table, column, options)
115
+ "check (mod(#{table}.#{column}, 2) = 0)"
105
116
  end
106
117
 
107
- ##
118
+ ##
108
119
  # Make sure every entry in the column is unique.
109
120
  #
110
121
  # Example:
111
122
  # constrain :books, :isbn, :unique => true
112
123
  #
113
- def unique(column, options)
114
- column = column.join(', ') if column.respond_to?(:join)
124
+ def unique(table, column, options)
125
+ column = Array(column).map {|c| %{"#{c}"} }.join(', ')
115
126
  "unique (#{column})"
116
127
  end
117
-
118
- ##
128
+
129
+ ##
130
+ # Allow only one of the values in the given columns to be true.
131
+ # Only reasonable with more than one column.
132
+ # See Enterprise Rails, Chapter 10 for details.
133
+ #
134
+ # Example:
135
+ # constrain :books, [], :xor => true
136
+ #
137
+ def xor(table, column, options)
138
+ addition = Array(column).map {|c| %{("#{c}" is not null)::integer} }.join(' + ')
139
+
140
+ "check (#{addition} = 1)"
141
+ end
142
+
143
+ ##
119
144
  # Allow only text/strings of the exact length specified, no more, no less.
120
145
  #
121
146
  # Example:
122
147
  # constrain :books, :hash, :exact_length => 32
123
148
  #
124
- def exact_length(column, options)
125
- "check ( length(trim(both from #{column})) = #{options} )"
149
+ def exact_length(table, column, options)
150
+ "check ( length(trim(both from #{table}.#{column})) = #{options} )"
126
151
  end
127
-
128
- ##
152
+
153
+ ##
129
154
  # Allow only values that match the regular expression.
130
155
  #
131
156
  # Example:
132
157
  # constrain :orders, :visa, :format => /^([4]{1})([0-9]{12,15})$/
133
158
  #
134
- def format(column, options)
135
- "check (((#{column})::text #{options.casefold? ? '~*' : '~'} E'#{options.source}'::text ))"
159
+ def format(table, column, options)
160
+ "check (((#{table}.#{column})::text #{options.casefold? ? '~*' : '~'} E'#{options.source}'::text ))"
136
161
  end
137
-
138
- ##
162
+
163
+ ##
139
164
  # Add foreign key constraint.
140
165
  #
141
166
  # Example:
142
167
  # constrain :books, :author_id, :reference => {:authors => :id, :on_delete => :cascade}
143
168
  #
144
- def reference(column, options)
169
+ def reference(table, column, options)
145
170
  on_delete = options.delete(:on_delete)
146
171
  fk_table = options.keys.first
147
172
  fk_column = options[fk_table]
148
-
173
+
149
174
  on_delete = "on delete #{on_delete}" if on_delete
150
-
151
- "foreign key (#{column}) references #{fk_table} (#{fk_column}) #{on_delete}"
175
+
176
+ %{foreign key ("#{column}") references #{fk_table} (#{fk_column}) #{on_delete}}
152
177
  end
153
178
  end
154
- end
179
+ end
@@ -2,15 +2,15 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{sexy_pg_constraints}
5
- s.version = "0.1.2"
5
+ s.version = "0.2.0"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Maxim Chernyak"]
9
- s.date = %q{2009-10-04}
9
+ s.date = %q{2010-02-01}
10
10
  s.description = %q{Use migrations and simple syntax to manage constraints in PostgreSQL DB.}
11
11
  s.email = %q{max@bitsonnet.com}
12
12
  s.extra_rdoc_files = ["CHANGELOG.rdoc", "README.rdoc", "lib/constrainer.rb", "lib/constraints.rb", "lib/deconstrainer.rb", "lib/helpers.rb", "lib/sexy_pg_constraints.rb"]
13
- s.files = ["CHANGELOG.rdoc", "Manifest", "README.rdoc", "Rakefile", "init.rb", "lib/constrainer.rb", "lib/constraints.rb", "lib/deconstrainer.rb", "lib/helpers.rb", "lib/sexy_pg_constraints.rb", "sexy_pg_constraints.gemspec", "test/postgresql_adapter.rb", "test/sexy_pg_constraints_test.rb", "test/test_helper.rb"]
13
+ s.files = ["CHANGELOG.rdoc", "Manifest", "README.rdoc", "Rakefile", "init.rb", "lib/constrainer.rb", "lib/constraints.rb", "lib/deconstrainer.rb", "lib/helpers.rb", "lib/sexy_pg_constraints.rb", "sexy_pg_constraints.gemspec", "test/postgresql_adapter.rb", "test/sexy_pg_constraints_test.rb", "test/support/assert_prohibits_allows.rb", "test/support/database.yml", "test/support/database.yml.example", "test/support/models.rb", "test/test_helper.rb"]
14
14
  s.homepage = %q{http://github.com/maxim/sexy_pg_constraints}
15
15
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Sexy_pg_constraints", "--main", "README.rdoc"]
16
16
  s.require_paths = ["lib"]
@@ -1,572 +1,899 @@
1
1
  require File.dirname(__FILE__) + '/test_helper.rb'
2
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
3
  class SexyPgConstraintsTest < Test::Unit::TestCase
40
4
  def setup
41
5
  CreateBooks.up
42
6
  CreateAuthors.up
43
7
  end
44
-
8
+
45
9
  def teardown
46
10
  CreateBooks.down
47
11
  CreateAuthors.down
48
12
  end
49
-
13
+
50
14
  def test_should_create_book
51
15
  Book.create
52
16
  assert_equal 1, Book.count
53
17
  end
54
-
18
+
55
19
  def test_whitelist
56
20
  ActiveRecord::Migration.constrain :books, :author, :whitelist => %w(whitelisted1 whitelisted2 whitelisted3)
57
-
58
- assert_prohibits :author, :whitelist do |book|
21
+
22
+ assert_prohibits Book, :author, :whitelist do |book|
59
23
  book.author = 'not_whitelisted'
60
24
  end
61
-
62
- assert_allows do |book|
25
+
26
+ assert_allows Book do |book|
63
27
  book.author = 'whitelisted2'
64
28
  end
65
-
29
+
66
30
  ActiveRecord::Migration.deconstrain :books, :author, :whitelist
67
-
68
- assert_allows do |book|
31
+
32
+ assert_allows Book do |book|
69
33
  book.author = 'not_whitelisted'
70
34
  end
71
35
  end
72
-
36
+
37
+ def test_whitelist_on_a_column_whose_name_is_a_sql_keyword
38
+ ActiveRecord::Migration.constrain :books, :as, :whitelist => %w(whitelisted1 whitelisted2 whitelisted3)
39
+
40
+ assert_prohibits Book, :as, :whitelist do |book|
41
+ book.as = 'not_whitelisted'
42
+ end
43
+
44
+ assert_allows Book do |book|
45
+ book.as = 'whitelisted2'
46
+ end
47
+
48
+ ActiveRecord::Migration.deconstrain :books, :as, :whitelist
49
+
50
+ assert_allows Book do |book|
51
+ book.as = 'not_whitelisted'
52
+ end
53
+ end
54
+
73
55
  def test_blacklist
74
56
  ActiveRecord::Migration.constrain :books, :author, :blacklist => %w(blacklisted1 blacklisted2 blacklisted3)
75
-
76
- assert_prohibits :author, :blacklist do |book|
57
+
58
+ assert_prohibits Book, :author, :blacklist do |book|
77
59
  book.author = 'blacklisted2'
78
60
  end
79
-
80
- assert_allows do |book|
61
+
62
+ assert_allows Book do |book|
81
63
  book.author = 'not_blacklisted'
82
64
  end
83
-
65
+
84
66
  ActiveRecord::Migration.deconstrain :books, :author, :blacklist
85
-
86
- assert_allows do |book|
67
+
68
+ assert_allows Book do |book|
87
69
  book.author = 'blacklisted2'
88
70
  end
89
71
  end
90
-
72
+
73
+ def test_blacklist_on_a_column_whose_name_is_a_sql_keyword
74
+ ActiveRecord::Migration.constrain :books, :as, :blacklist => %w(blacklisted1 blacklisted2 blacklisted3)
75
+
76
+ assert_prohibits Book, :as, :blacklist do |book|
77
+ book.as = 'blacklisted2'
78
+ end
79
+
80
+ assert_allows Book do |book|
81
+ book.as = 'not_blacklisted'
82
+ end
83
+
84
+ ActiveRecord::Migration.deconstrain :books, :as, :blacklist
85
+
86
+ assert_allows Book do |book|
87
+ book.as = 'blacklisted2'
88
+ end
89
+ end
90
+
91
91
  def test_not_blank
92
92
  ActiveRecord::Migration.constrain :books, :author, :not_blank => true
93
-
94
- assert_prohibits :author, :not_blank do |book|
93
+
94
+ assert_prohibits Book, :author, :not_blank do |book|
95
95
  book.author = ' '
96
96
  end
97
-
98
- assert_allows do |book|
97
+
98
+ assert_allows Book do |book|
99
99
  book.author = 'foo'
100
100
  end
101
-
101
+
102
102
  ActiveRecord::Migration.deconstrain :books, :author, :not_blank
103
-
104
- assert_allows do |book|
103
+
104
+ assert_allows Book do |book|
105
105
  book.author = ' '
106
106
  end
107
107
  end
108
-
108
+
109
+ def test_not_blank_on_a_column_whose_name_is_a_sql_keyword
110
+ ActiveRecord::Migration.constrain :books, :as, :not_blank => true
111
+
112
+ assert_prohibits Book, :as, :not_blank do |book|
113
+ book.as = ' '
114
+ end
115
+
116
+ assert_allows Book do |book|
117
+ book.as = 'foo'
118
+ end
119
+
120
+ ActiveRecord::Migration.deconstrain :books, :as, :not_blank
121
+
122
+ assert_allows Book do |book|
123
+ book.as = ' '
124
+ end
125
+ end
126
+
109
127
  def test_within_inclusive
110
128
  ActiveRecord::Migration.constrain :books, :quantity, :within => 5..11
111
-
112
- assert_prohibits :quantity, :within do |book|
129
+
130
+ assert_prohibits Book, :quantity, :within do |book|
113
131
  book.quantity = 12
114
132
  end
115
-
116
- assert_prohibits :quantity, :within do |book|
133
+
134
+ assert_prohibits Book, :quantity, :within do |book|
117
135
  book.quantity = 4
118
136
  end
119
-
120
- assert_allows do |book|
137
+
138
+ assert_allows Book do |book|
121
139
  book.quantity = 7
122
140
  end
123
-
141
+
124
142
  ActiveRecord::Migration.deconstrain :books, :quantity, :within
125
-
126
- assert_allows do |book|
143
+
144
+ assert_allows Book do |book|
127
145
  book.quantity = 12
128
146
  end
129
147
  end
130
-
148
+
149
+ def test_within_inclusive_on_a_column_whose_name_is_a_sql_keyword
150
+ ActiveRecord::Migration.constrain :books, :from, :within => 5..11
151
+
152
+ assert_prohibits Book, :from, :within do |book|
153
+ book.from = 12
154
+ end
155
+
156
+ assert_prohibits Book, :from, :within do |book|
157
+ book.from = 4
158
+ end
159
+
160
+ assert_allows Book do |book|
161
+ book.from = 7
162
+ end
163
+
164
+ ActiveRecord::Migration.deconstrain :books, :from, :within
165
+
166
+ assert_allows Book do |book|
167
+ book.from = 12
168
+ end
169
+ end
170
+
131
171
  def test_within_non_inclusive
132
172
  ActiveRecord::Migration.constrain :books, :quantity, :within => 5...11
133
-
134
- assert_prohibits :quantity, :within do |book|
173
+
174
+ assert_prohibits Book, :quantity, :within do |book|
135
175
  book.quantity = 11
136
176
  end
137
-
138
- assert_prohibits :quantity, :within do |book|
177
+
178
+ assert_prohibits Book, :quantity, :within do |book|
139
179
  book.quantity = 4
140
180
  end
141
-
142
- assert_allows do |book|
181
+
182
+ assert_allows Book do |book|
143
183
  book.quantity = 10
144
184
  end
145
-
185
+
146
186
  ActiveRecord::Migration.deconstrain :books, :quantity, :within
147
-
148
- assert_allows do |book|
187
+
188
+ assert_allows Book do |book|
149
189
  book.quantity = 11
150
190
  end
151
191
  end
152
-
192
+
193
+ def test_within_non_inclusive_on_a_column_whose_name_is_a_sql_keyword
194
+ ActiveRecord::Migration.constrain :books, :from, :within => 5...11
195
+
196
+ assert_prohibits Book, :from, :within do |book|
197
+ book.from = 11
198
+ end
199
+
200
+ assert_prohibits Book, :from, :within do |book|
201
+ book.from = 4
202
+ end
203
+
204
+ assert_allows Book do |book|
205
+ book.from = 10
206
+ end
207
+
208
+ ActiveRecord::Migration.deconstrain :books, :from, :within
209
+
210
+ assert_allows Book do |book|
211
+ book.from = 11
212
+ end
213
+ end
214
+
153
215
  def test_length_within_inclusive
154
216
  ActiveRecord::Migration.constrain :books, :title, :length_within => 5..11
155
-
156
- assert_prohibits :title, :length_within do |book|
217
+
218
+ assert_prohibits Book, :title, :length_within do |book|
157
219
  book.title = 'abcdefghijkl'
158
220
  end
159
-
160
- assert_prohibits :title, :length_within do |book|
221
+
222
+ assert_prohibits Book, :title, :length_within do |book|
161
223
  book.title = 'abcd'
162
224
  end
163
-
164
- assert_allows do |book|
225
+
226
+ assert_allows Book do |book|
165
227
  book.title = 'abcdefg'
166
228
  end
167
-
229
+
168
230
  ActiveRecord::Migration.deconstrain :books, :title, :length_within
169
-
170
- assert_allows do |book|
231
+
232
+ assert_allows Book do |book|
171
233
  book.title = 'abcdefghijkl'
172
234
  end
173
235
  end
174
-
236
+
237
+ def test_length_within_inclusive_on_a_column_whose_name_is_a_sql_keyword
238
+ ActiveRecord::Migration.constrain :books, :as, :length_within => 5..11
239
+
240
+ assert_prohibits Book, :as, :length_within do |book|
241
+ book.as = 'abcdefghijkl'
242
+ end
243
+
244
+ assert_prohibits Book, :as, :length_within do |book|
245
+ book.as = 'abcd'
246
+ end
247
+
248
+ assert_allows Book do |book|
249
+ book.as = 'abcdefg'
250
+ end
251
+
252
+ ActiveRecord::Migration.deconstrain :books, :as, :length_within
253
+
254
+ assert_allows Book do |book|
255
+ book.as = 'abcdefghijkl'
256
+ end
257
+ end
258
+
175
259
  def test_length_within_non_inclusive
176
260
  ActiveRecord::Migration.constrain :books, :title, :length_within => 5...11
177
-
178
- assert_prohibits :title, :length_within do |book|
261
+
262
+ assert_prohibits Book, :title, :length_within do |book|
179
263
  book.title = 'abcdefghijk'
180
264
  end
181
-
182
- assert_prohibits :title, :length_within do |book|
265
+
266
+ assert_prohibits Book, :title, :length_within do |book|
183
267
  book.title = 'abcd'
184
268
  end
185
-
186
- assert_allows do |book|
269
+
270
+ assert_allows Book do |book|
187
271
  book.title = 'abcdefg'
188
272
  end
189
-
273
+
190
274
  ActiveRecord::Migration.deconstrain :books, :title, :length_within
191
-
192
- assert_allows do |book|
275
+
276
+ assert_allows Book do |book|
193
277
  book.title = 'abcdefghijk'
194
278
  end
195
279
  end
196
-
280
+
281
+ def test_length_within_non_inclusive_on_a_column_whose_name_is_a_sql_keyword
282
+ ActiveRecord::Migration.constrain :books, :as, :length_within => 5...11
283
+
284
+ assert_prohibits Book, :as, :length_within do |book|
285
+ book.as = 'abcdefghijk'
286
+ end
287
+
288
+ assert_prohibits Book, :as, :length_within do |book|
289
+ book.as = 'abcd'
290
+ end
291
+
292
+ assert_allows Book do |book|
293
+ book.as = 'abcdefg'
294
+ end
295
+
296
+ ActiveRecord::Migration.deconstrain :books, :as, :length_within
297
+
298
+ assert_allows Book do |book|
299
+ book.as = 'abcdefghijk'
300
+ end
301
+ end
302
+
197
303
  def test_email
198
304
  ActiveRecord::Migration.constrain :books, :author, :email => true
199
-
200
- assert_prohibits :author, :email do |book|
305
+
306
+ assert_prohibits Book, :author, :email do |book|
201
307
  book.author = 'blah@example'
202
308
  end
203
-
204
- assert_allows do |book|
309
+
310
+ assert_allows Book do |book|
205
311
  book.author = 'blah@example.com'
206
312
  end
207
-
313
+
208
314
  ActiveRecord::Migration.deconstrain :books, :author, :email
209
-
210
- assert_allows do |book|
315
+
316
+ assert_allows Book do |book|
211
317
  book.author = 'blah@example'
212
318
  end
213
- end
214
-
319
+ end
320
+
321
+ def test_email_on_a_column_whose_name_is_a_sql_keyword
322
+ ActiveRecord::Migration.constrain :books, :as, :email => true
323
+
324
+ assert_prohibits Book, :as, :email do |book|
325
+ book.as = 'blah@example'
326
+ end
327
+
328
+ assert_allows Book do |book|
329
+ book.as = 'blah@example.com'
330
+ end
331
+
332
+ ActiveRecord::Migration.deconstrain :books, :as, :email
333
+
334
+ assert_allows Book do |book|
335
+ book.as = 'blah@example'
336
+ end
337
+ end
338
+
215
339
  def test_alphanumeric
216
340
  ActiveRecord::Migration.constrain :books, :title, :alphanumeric => true
217
-
218
- assert_prohibits :title, :alphanumeric do |book|
341
+
342
+ assert_prohibits Book, :title, :alphanumeric do |book|
219
343
  book.title = 'asdf@asdf'
220
344
  end
221
-
222
- assert_allows do |book|
345
+
346
+ assert_allows Book do |book|
223
347
  book.title = 'asdf'
224
348
  end
225
-
349
+
226
350
  ActiveRecord::Migration.deconstrain :books, :title, :alphanumeric
227
-
228
- assert_allows do |book|
351
+
352
+ assert_allows Book do |book|
229
353
  book.title = 'asdf@asdf'
230
354
  end
231
355
  end
232
-
356
+
357
+ def test_alphanumeric_on_a_column_whose_name_is_a_sql_keyword
358
+ ActiveRecord::Migration.constrain :books, :as, :alphanumeric => true
359
+
360
+ assert_prohibits Book, :as, :alphanumeric do |book|
361
+ book.as = 'asdf@asdf'
362
+ end
363
+
364
+ assert_allows Book do |book|
365
+ book.as = 'asdf'
366
+ end
367
+
368
+ ActiveRecord::Migration.deconstrain :books, :as, :alphanumeric
369
+
370
+ assert_allows Book do |book|
371
+ book.as = 'asdf@asdf'
372
+ end
373
+ end
374
+
233
375
  def test_positive
234
376
  ActiveRecord::Migration.constrain :books, :quantity, :positive => true
235
-
236
- assert_prohibits :quantity, :positive do |book|
377
+
378
+ assert_prohibits Book, :quantity, :positive do |book|
237
379
  book.quantity = -1
238
380
  end
239
-
240
- assert_allows do |book|
381
+
382
+ assert_allows Book do |book|
241
383
  book.quantity = 0
242
384
  end
243
-
244
- assert_allows do |book|
385
+
386
+ assert_allows Book do |book|
245
387
  book.quantity = 1
246
388
  end
247
-
389
+
248
390
  ActiveRecord::Migration.deconstrain :books, :quantity, :positive
249
-
250
- assert_allows do |book|
391
+
392
+ assert_allows Book do |book|
251
393
  book.quantity = -1
252
394
  end
253
395
  end
254
-
396
+
397
+ def test_positive_on_a_column_whose_name_is_a_sql_keyword
398
+ ActiveRecord::Migration.constrain :books, :from, :positive => true
399
+
400
+ assert_prohibits Book, :from, :positive do |book|
401
+ book.from = -1
402
+ end
403
+
404
+ assert_allows Book do |book|
405
+ book.from = 0
406
+ end
407
+
408
+ assert_allows Book do |book|
409
+ book.from = 1
410
+ end
411
+
412
+ ActiveRecord::Migration.deconstrain :books, :from, :positive
413
+
414
+ assert_allows Book do |book|
415
+ book.from = -1
416
+ end
417
+ end
418
+
255
419
  def test_odd
256
420
  ActiveRecord::Migration.constrain :books, :quantity, :odd => true
257
-
258
- assert_prohibits :quantity, :odd do |book|
421
+
422
+ assert_prohibits Book, :quantity, :odd do |book|
259
423
  book.quantity = 2
260
424
  end
261
-
262
- assert_allows do |book|
425
+
426
+ assert_allows Book do |book|
263
427
  book.quantity = 1
264
428
  end
265
-
429
+
266
430
  ActiveRecord::Migration.deconstrain :books, :quantity, :odd
267
-
268
- assert_allows do |book|
431
+
432
+ assert_allows Book do |book|
269
433
  book.quantity = 2
270
434
  end
271
435
  end
272
-
436
+
437
+ def test_odd_on_a_column_whose_name_is_a_sql_keyword
438
+ ActiveRecord::Migration.constrain :books, :from, :odd => true
439
+
440
+ assert_prohibits Book, :from, :odd do |book|
441
+ book.from = 2
442
+ end
443
+
444
+ assert_allows Book do |book|
445
+ book.from = 1
446
+ end
447
+
448
+ ActiveRecord::Migration.deconstrain :books, :from, :odd
449
+
450
+ assert_allows Book do |book|
451
+ book.from = 2
452
+ end
453
+ end
454
+
273
455
  def test_even
274
456
  ActiveRecord::Migration.constrain :books, :quantity, :even => true
275
-
276
- assert_prohibits :quantity, :even do |book|
457
+
458
+ assert_prohibits Book, :quantity, :even do |book|
277
459
  book.quantity = 1
278
460
  end
279
-
280
- assert_allows do |book|
461
+
462
+ assert_allows Book do |book|
281
463
  book.quantity = 2
282
464
  end
283
-
465
+
284
466
  ActiveRecord::Migration.deconstrain :books, :quantity, :even
285
-
286
- assert_allows do |book|
467
+
468
+ assert_allows Book do |book|
287
469
  book.quantity = 1
288
470
  end
289
471
  end
290
-
472
+
473
+ def test_even_on_a_column_whose_name_is_a_sql_keyword
474
+ ActiveRecord::Migration.constrain :books, :from, :even => true
475
+
476
+ assert_prohibits Book, :from, :even do |book|
477
+ book.from = 1
478
+ end
479
+
480
+ assert_allows Book do |book|
481
+ book.from = 2
482
+ end
483
+
484
+ ActiveRecord::Migration.deconstrain :books, :from, :even
485
+
486
+ assert_allows Book do |book|
487
+ book.from = 1
488
+ end
489
+ end
490
+
291
491
  def test_unique
292
492
  ActiveRecord::Migration.constrain :books, :isbn, :unique => true
293
-
294
- assert_allows do |book|
493
+
494
+ assert_allows Book do |book|
295
495
  book.isbn = 'foo'
296
496
  end
297
-
298
- assert_prohibits :isbn, :unique, 'unique' do |book|
497
+
498
+ assert_prohibits Book, :isbn, :unique, 'unique' do |book|
299
499
  book.isbn = 'foo'
300
500
  end
301
-
501
+
302
502
  ActiveRecord::Migration.deconstrain :books, :isbn, :unique
303
-
304
- assert_allows do |book|
503
+
504
+ assert_allows Book do |book|
305
505
  book.isbn = 'foo'
306
506
  end
307
507
  end
308
-
508
+
509
+ def test_unique_on_a_column_whose_name_is_a_sql_keyword
510
+ ActiveRecord::Migration.constrain :books, :as, :unique => true
511
+
512
+ assert_allows Book do |book|
513
+ book.as = 'foo'
514
+ end
515
+
516
+ assert_prohibits Book, :as, :unique, 'unique' do |book|
517
+ book.as = 'foo'
518
+ end
519
+
520
+ ActiveRecord::Migration.deconstrain :books, :as, :unique
521
+
522
+ assert_allows Book do |book|
523
+ book.as = 'foo'
524
+ end
525
+ end
526
+
309
527
  def test_exact_length
310
528
  ActiveRecord::Migration.constrain :books, :isbn, :exact_length => 5
311
-
312
- assert_prohibits :isbn, :exact_length do |book|
529
+
530
+ assert_prohibits Book, :isbn, :exact_length do |book|
313
531
  book.isbn = '123456'
314
532
  end
315
-
316
- assert_prohibits :isbn, :exact_length do |book|
533
+
534
+ assert_prohibits Book, :isbn, :exact_length do |book|
317
535
  book.isbn = '1234'
318
536
  end
319
-
320
- assert_allows do |book|
537
+
538
+ assert_allows Book do |book|
321
539
  book.isbn = '12345'
322
540
  end
323
-
541
+
324
542
  ActiveRecord::Migration.deconstrain :books, :isbn, :exact_length
325
-
326
- assert_allows do |book|
543
+
544
+ assert_allows Book do |book|
327
545
  book.isbn = '123456'
328
546
  end
329
547
  end
330
-
548
+
549
+ def test_exact_length_on_a_column_whose_name_is_a_sql_keyword
550
+ ActiveRecord::Migration.constrain :books, :as, :exact_length => 5
551
+
552
+ assert_prohibits Book, :as, :exact_length do |book|
553
+ book.as = '123456'
554
+ end
555
+
556
+ assert_prohibits Book, :as, :exact_length do |book|
557
+ book.as = '1234'
558
+ end
559
+
560
+ assert_allows Book do |book|
561
+ book.as = '12345'
562
+ end
563
+
564
+ ActiveRecord::Migration.deconstrain :books, :as, :exact_length
565
+
566
+ assert_allows Book do |book|
567
+ book.as = '123456'
568
+ end
569
+ end
570
+
331
571
  def test_format_case_insensitive
332
572
  ActiveRecord::Migration.constrain :books, :title, :format => /^[a-z]+$/i
333
-
334
- assert_prohibits :title, :format do |book|
573
+
574
+ assert_prohibits Book, :title, :format do |book|
335
575
  book.title = 'abc3'
336
576
  end
337
-
338
- assert_prohibits :title, :format do |book|
577
+
578
+ assert_prohibits Book, :title, :format do |book|
339
579
  book.title = ''
340
580
  end
341
-
342
- assert_allows do |book|
581
+
582
+ assert_allows Book do |book|
343
583
  book.title = 'abc'
344
584
  end
345
-
346
- assert_allows do |book|
585
+
586
+ assert_allows Book do |book|
347
587
  book.title = 'ABc'
348
588
  end
349
-
589
+
350
590
  ActiveRecord::Migration.deconstrain :books, :title, :format
351
-
352
- assert_allows do |book|
591
+
592
+ assert_allows Book do |book|
353
593
  book.title = 'abc3'
354
594
  end
355
595
  end
356
-
596
+
597
+ def test_format_case_insensitive_on_a_column_whose_name_is_a_sql_keyword
598
+ ActiveRecord::Migration.constrain :books, :as, :format => /^[a-z]+$/i
599
+
600
+ assert_prohibits Book, :as, :format do |book|
601
+ book.as = 'abc3'
602
+ end
603
+
604
+ assert_prohibits Book, :as, :format do |book|
605
+ book.as = ''
606
+ end
607
+
608
+ assert_allows Book do |book|
609
+ book.as = 'abc'
610
+ end
611
+
612
+ assert_allows Book do |book|
613
+ book.as = 'ABc'
614
+ end
615
+
616
+ ActiveRecord::Migration.deconstrain :books, :as, :format
617
+
618
+ assert_allows Book do |book|
619
+ book.as = 'abc3'
620
+ end
621
+ end
622
+
357
623
  def test_format_case_sensitive
358
624
  ActiveRecord::Migration.constrain :books, :title, :format => /^[a-z]+$/
359
-
360
- assert_prohibits :title, :format do |book|
625
+
626
+ assert_prohibits Book, :title, :format do |book|
361
627
  book.title = 'aBc'
362
628
  end
363
-
364
- assert_allows do |book|
629
+
630
+ assert_allows Book do |book|
365
631
  book.title = 'abc'
366
632
  end
367
-
633
+
368
634
  ActiveRecord::Migration.deconstrain :books, :title, :format
369
-
370
- assert_allows do |book|
635
+
636
+ assert_allows Book do |book|
371
637
  book.title = 'aBc'
372
638
  end
373
639
  end
374
-
640
+
641
+ def test_format_case_sensitive_on_a_column_whose_name_is_a_sql_keyword
642
+ ActiveRecord::Migration.constrain :books, :as, :format => /^[a-z]+$/
643
+
644
+ assert_prohibits Book, :as, :format do |book|
645
+ book.as = 'aBc'
646
+ end
647
+
648
+ assert_allows Book do |book|
649
+ book.as = 'abc'
650
+ end
651
+
652
+ ActiveRecord::Migration.deconstrain :books, :as, :format
653
+
654
+ assert_allows Book do |book|
655
+ book.as = 'aBc'
656
+ end
657
+ end
658
+
375
659
  def test_reference
376
660
  ActiveRecord::Migration.constrain :books, :author_id, :reference => {:authors => :id}
377
-
378
- assert_prohibits :author_id, :reference, 'foreign key' do |book|
661
+
662
+ assert_prohibits Book, :author_id, :reference, 'foreign key' do |book|
379
663
  book.author_id = 1
380
664
  end
381
-
665
+
382
666
  author = Author.new
383
667
  author.name = "Mark Twain"
384
668
  author.bio = "American writer"
385
669
  assert author.save
386
-
670
+
387
671
  assert_equal 1, author.id
388
-
389
- assert_allows do |book|
672
+
673
+ assert_allows Book do |book|
390
674
  book.author_id = 1
391
675
  end
392
-
676
+
393
677
  ActiveRecord::Migration.deconstrain :books, :author_id, :reference
394
-
395
- assert_allows do |book|
678
+
679
+ assert_allows Book do |book|
396
680
  book.author_id = 2
397
681
  end
398
682
  end
399
-
683
+
684
+ def test_reference_on_a_column_whose_name_is_a_sql_keyword
685
+ ActiveRecord::Migration.constrain :books, :from, :reference => {:authors => :id}
686
+
687
+ assert_prohibits Book, :from, :reference, 'foreign key' do |book|
688
+ book.from = 1
689
+ end
690
+
691
+ author = Author.new
692
+ author.name = "Mark Twain"
693
+ author.bio = "American writer"
694
+ assert author.save
695
+
696
+ assert_equal 1, author.id
697
+
698
+ assert_allows Book do |book|
699
+ book.from = 1
700
+ end
701
+
702
+ ActiveRecord::Migration.deconstrain :books, :from, :reference
703
+
704
+ assert_allows Book do |book|
705
+ book.from = 2
706
+ end
707
+ end
708
+
400
709
  def test_reference_with_on_delete
401
710
  ActiveRecord::Migration.constrain :books, :author_id, :reference => {:authors => :id, :on_delete => :cascade}
402
-
711
+
403
712
  author = Author.new
404
713
  author.name = "Mark Twain"
405
714
  author.bio = "American writer"
406
715
  assert author.save
407
-
716
+
408
717
  assert_equal 1, Author.count
409
-
410
- assert_allows do |book|
718
+
719
+ assert_allows Book do |book|
411
720
  book.title = "The Adventures of Tom Sawyer"
412
721
  book.author_id = 1
413
722
  end
414
-
415
- assert_allows do |book|
723
+
724
+ assert_allows Book do |book|
416
725
  book.title = "The Adventures of Huckleberry Finn"
417
726
  book.author_id = 1
418
727
  end
419
-
728
+
420
729
  author.destroy
421
-
730
+
422
731
  assert_equal 0, Author.count
423
732
  assert_equal 0, Book.count
424
733
  end
425
-
734
+
426
735
  def test_block_syntax
427
736
  ActiveRecord::Migration.constrain :books do |t|
428
737
  t.title :not_blank => true
429
738
  t.isbn :exact_length => 15
430
739
  t.author :alphanumeric => true
431
740
  end
432
-
433
- assert_prohibits :title, :not_blank do |book|
741
+
742
+ assert_prohibits Book, :title, :not_blank do |book|
434
743
  book.title = ' '
435
744
  end
436
-
437
- assert_prohibits :isbn, :exact_length do |book|
745
+
746
+ assert_prohibits Book, :isbn, :exact_length do |book|
438
747
  book.isbn = 'asdf'
439
748
  end
440
-
441
- assert_prohibits :author, :alphanumeric do |book|
749
+
750
+ assert_prohibits Book, :author, :alphanumeric do |book|
442
751
  book.author = 'foo#bar'
443
752
  end
444
-
753
+
445
754
  ActiveRecord::Migration.deconstrain :books do |t|
446
755
  t.title :not_blank
447
756
  t.isbn :exact_length
448
757
  t.author :alphanumeric
449
758
  end
450
-
451
- assert_allows do |book|
759
+
760
+ assert_allows Book do |book|
452
761
  book.title = ' '
453
762
  book.isbn = 'asdf'
454
763
  book.author = 'foo#bar'
455
764
  end
456
765
  end
457
-
766
+
458
767
  def test_multiple_constraints_per_line
459
768
  ActiveRecord::Migration.constrain :books do |t|
460
769
  t.title :not_blank => true, :alphanumeric => true, :blacklist => %w(foo bar)
461
770
  end
462
-
463
- assert_prohibits :title, :not_blank do |book|
771
+
772
+ assert_prohibits Book, :title, [:not_blank, :alphanumeric] do |book|
464
773
  book.title = ' '
465
774
  end
466
-
467
- assert_prohibits :title, :alphanumeric do |book|
775
+
776
+ assert_prohibits Book, :title, :alphanumeric do |book|
468
777
  book.title = 'asdf@asdf'
469
778
  end
470
-
471
- assert_prohibits :title, :blacklist do |book|
779
+
780
+ assert_prohibits Book, :title, :blacklist do |book|
472
781
  book.title = 'foo'
473
782
  end
474
-
783
+
475
784
  ActiveRecord::Migration.deconstrain :books do |t|
476
785
  t.title :not_blank, :alphanumeric, :blacklist
477
786
  end
478
-
479
- assert_allows do |book|
787
+
788
+ assert_allows Book do |book|
480
789
  book.title = ' '
481
790
  end
482
-
483
- assert_allows do |book|
791
+
792
+ assert_allows Book do |book|
484
793
  book.title = 'asdf@asdf'
485
794
  end
486
-
487
- assert_allows do |book|
795
+
796
+ assert_allows Book do |book|
488
797
  book.title = 'foo'
489
798
  end
490
799
  end
491
-
800
+
492
801
  def test_multicolumn_constraint
493
802
  ActiveRecord::Migration.constrain :books, [:title, :isbn], :unique => true
494
-
495
- assert_allows do |book|
803
+
804
+ assert_allows Book do |book|
496
805
  book.title = 'foo'
497
806
  book.isbn = 'bar'
498
807
  end
499
-
500
- assert_allows do |book|
808
+
809
+ assert_allows Book do |book|
501
810
  book.title = 'foo'
502
811
  book.isbn = 'foo'
503
812
  end
504
-
505
- assert_prohibits [:title, :isbn], :unique, 'unique' do |book|
813
+
814
+ assert_prohibits Book, [:title, :isbn], :unique, 'unique' do |book|
506
815
  book.title = 'foo'
507
816
  book.isbn = 'bar'
508
817
  end
509
-
818
+
510
819
  ActiveRecord::Migration.deconstrain :books, [:title, :isbn], :unique
511
-
512
- assert_allows do |book|
820
+
821
+ assert_allows Book do |book|
513
822
  book.title = 'foo'
514
823
  book.isbn = 'bar'
515
824
  end
516
825
  end
517
-
826
+
518
827
  def test_multicolumn_constraint_block_syntax
519
828
  ActiveRecord::Migration.constrain :books do |t|
520
829
  t[:title, :isbn].all :unique => true
521
830
  end
522
-
523
- assert_allows do |book|
831
+
832
+ assert_allows Book do |book|
524
833
  book.title = 'foo'
525
834
  book.isbn = 'bar'
526
835
  end
527
-
528
- assert_allows do |book|
836
+
837
+ assert_allows Book do |book|
529
838
  book.title = 'foo'
530
839
  book.isbn = 'foo'
531
840
  end
532
-
533
- assert_prohibits [:title, :isbn], :unique, 'unique' do |book|
841
+
842
+ assert_prohibits Book, [:title, :isbn], :unique, 'unique' do |book|
534
843
  book.title = 'foo'
535
844
  book.isbn = 'bar'
536
845
  end
537
-
846
+
538
847
  ActiveRecord::Migration.deconstrain :books do |t|
539
848
  t[:title, :isbn].all :unique
540
849
  end
541
-
542
- assert_allows do |book|
850
+
851
+ assert_allows Book do |book|
543
852
  book.title = 'foo'
544
853
  book.isbn = 'bar'
545
854
  end
546
855
  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
856
+
857
+ def test_lowercase
858
+ ActiveRecord::Migration.constrain :books, :author, :lowercase => true
859
+
860
+ assert_prohibits Book, :author, :lowercase do |book|
861
+ book.author = 'UPPER'
862
+ end
863
+
864
+ assert_allows Book do |book|
865
+ book.author = 'lower with 1337'
866
+ end
867
+
868
+ ActiveRecord::Migration.deconstrain :books, :author, :lowercase
869
+
870
+ assert_allows Book do |book|
871
+ book.author = 'UPPER'
872
+ end
873
+
874
+ end
875
+
876
+ def test_xor
877
+ ActiveRecord::Migration.constrain :books, [:xor_col_1, :xor_col_2], :xor => true
878
+
879
+ assert_prohibits Book, [:xor_col_1, :xor_col_2], :xor do |book|
880
+ book.xor_col_1 = 123
881
+ book.xor_col_2 = 321
882
+ end
883
+
884
+ assert_allows Book do |book|
885
+ book.xor_col_1 = 123
886
+ end
887
+
888
+ assert_allows Book do |book|
889
+ book.xor_col_2 = 123
890
+ end
891
+
892
+ ActiveRecord::Migration.deconstrain :books, [:xor_col_1, :xor_col_2], :xor
893
+
894
+ assert_allows Book do |book|
895
+ book.xor_col_1 = 123
896
+ book.xor_col_2 = 123
897
+ end
571
898
  end
572
899
  end