Empact-sexy_pg_constraints 0.3.0 → 0.4.0
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.
- data/CHANGELOG.rdoc +4 -0
- data/Empact-sexy_pg_constraints.gemspec +9 -8
- data/README.rdoc +5 -5
- data/VERSION +1 -1
- data/lib/sexy_pg_constraints.rb +0 -3
- data/lib/sexy_pg_constraints/{constrainer.rb → constrainers/constrainer.rb} +4 -2
- data/lib/sexy_pg_constraints/{deconstrainer.rb → constrainers/deconstrainer.rb} +8 -6
- data/lib/sexy_pg_constraints/constrainers/helpers.rb +13 -0
- data/lib/sexy_pg_constraints/constraints.rb +73 -52
- data/lib/sexy_pg_constraints/schema_definitions.rb +3 -0
- data/test/general_test.rb +6 -6
- data/test/like_test.rb +39 -0
- data/test/{not_blank_test.rb → present_test.rb} +5 -5
- data/test/{trimmed_test.rb → stripped_test.rb} +13 -13
- metadata +38 -17
- data/lib/sexy_pg_constraints/helpers.rb +0 -19
data/CHANGELOG.rdoc
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = "Empact-sexy_pg_constraints"
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.4.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Maxim Chernyak", "Ben Woosley"]
|
12
|
-
s.date = "2012-
|
12
|
+
s.date = "2012-11-16"
|
13
13
|
s.description = "Use migrations and simple syntax to manage constraints in PostgreSQL DB."
|
14
14
|
s.email = "ben.woosley@gmail.com"
|
15
15
|
s.extra_rdoc_files = [
|
@@ -27,10 +27,10 @@ Gem::Specification.new do |s|
|
|
27
27
|
"VERSION",
|
28
28
|
"init.rb",
|
29
29
|
"lib/sexy_pg_constraints.rb",
|
30
|
-
"lib/sexy_pg_constraints/constrainer.rb",
|
30
|
+
"lib/sexy_pg_constraints/constrainers/constrainer.rb",
|
31
|
+
"lib/sexy_pg_constraints/constrainers/deconstrainer.rb",
|
32
|
+
"lib/sexy_pg_constraints/constrainers/helpers.rb",
|
31
33
|
"lib/sexy_pg_constraints/constraints.rb",
|
32
|
-
"lib/sexy_pg_constraints/deconstrainer.rb",
|
33
|
-
"lib/sexy_pg_constraints/helpers.rb",
|
34
34
|
"lib/sexy_pg_constraints/railtie.rb",
|
35
35
|
"lib/sexy_pg_constraints/schema_definitions.rb",
|
36
36
|
"test/alphanumeric_test.rb",
|
@@ -41,23 +41,24 @@ Gem::Specification.new do |s|
|
|
41
41
|
"test/general_test.rb",
|
42
42
|
"test/greater_less_than_test.rb",
|
43
43
|
"test/length_within_test.rb",
|
44
|
+
"test/like_test.rb",
|
44
45
|
"test/lowercase_test.rb",
|
45
|
-
"test/not_blank_test.rb",
|
46
46
|
"test/odd_event_test.rb",
|
47
47
|
"test/positive_test.rb",
|
48
|
+
"test/present_test.rb",
|
48
49
|
"test/reference_test.rb",
|
50
|
+
"test/stripped_test.rb",
|
49
51
|
"test/support/assert_prohibits_allows.rb",
|
50
52
|
"test/support/database.yml.example",
|
51
53
|
"test/support/models.rb",
|
52
54
|
"test/test_helper.rb",
|
53
|
-
"test/trimmed_test.rb",
|
54
55
|
"test/whitelist_test.rb",
|
55
56
|
"test/within_test.rb",
|
56
57
|
"test/xor_test.rb"
|
57
58
|
]
|
58
59
|
s.homepage = "http://github.com/maxim/sexy_pg_constraints"
|
59
60
|
s.require_paths = ["lib"]
|
60
|
-
s.rubygems_version = "1.8.
|
61
|
+
s.rubygems_version = "1.8.23"
|
61
62
|
s.summary = nil
|
62
63
|
|
63
64
|
if s.respond_to? :specification_version then
|
data/README.rdoc
CHANGED
@@ -24,14 +24,14 @@ Say you have a table "books" and you want your Postgres DB to ensure that their
|
|
24
24
|
class AddConstraintsToBooks < ActiveRecord::Migration
|
25
25
|
def self.up
|
26
26
|
constrain :books do |t|
|
27
|
-
t.title :
|
27
|
+
t.title :present => true, :alphanumeric => true, :length_within => 3..50
|
28
28
|
t.isbn :unique => true, :blacklist => %w(badbook1 badbook2)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
32
|
def self.down
|
33
33
|
deconstrain :books do |t|
|
34
|
-
t.title :
|
34
|
+
t.title :present, :alphanumeric, :length_within
|
35
35
|
t.isbn :unique, :blacklist
|
36
36
|
end
|
37
37
|
end
|
@@ -41,12 +41,12 @@ This will add all the necessary constraints to the database on the next migratio
|
|
41
41
|
|
42
42
|
There's also a syntax for when you don't need to work with multiple columns at once.
|
43
43
|
|
44
|
-
constrain :books, :title, :
|
44
|
+
constrain :books, :title, :present => true, :length_within => 3..50
|
45
45
|
|
46
46
|
The above line works exactly the same as this block
|
47
47
|
|
48
48
|
constrain :books do |t|
|
49
|
-
t.title :
|
49
|
+
t.title :present => true, :length_within => 3..50
|
50
50
|
end
|
51
51
|
|
52
52
|
Same applies to deconstrain.
|
@@ -95,7 +95,7 @@ Below is the list of constraints available and tested so far.
|
|
95
95
|
|
96
96
|
* whitelist
|
97
97
|
* blacklist
|
98
|
-
*
|
98
|
+
* present
|
99
99
|
* within
|
100
100
|
* length_within
|
101
101
|
* email
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.4.0
|
data/lib/sexy_pg_constraints.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
require 'sexy_pg_constraints/constrainers/helpers'
|
2
|
+
|
1
3
|
module SexyPgConstraints
|
2
4
|
class Constrainer
|
3
|
-
|
5
|
+
extend SexyPgConstraints::Helpers
|
4
6
|
|
5
7
|
def initialize(table, columns = [])
|
6
8
|
@table = table.to_s
|
@@ -24,7 +26,7 @@ module SexyPgConstraints
|
|
24
26
|
def add_constraints(table, column, constraints)
|
25
27
|
constraints.each_pair do |type, options|
|
26
28
|
execute "alter table #{table} add constraint #{make_title(table, column, type)} " \
|
27
|
-
+ SexyPgConstraints::Constraints.send(type,
|
29
|
+
+ SexyPgConstraints::Constraints.send(type, column, options) + ';'
|
28
30
|
end
|
29
31
|
end
|
30
32
|
end
|
@@ -1,25 +1,27 @@
|
|
1
|
+
require 'sexy_pg_constraints/constrainers/helpers'
|
2
|
+
|
1
3
|
module SexyPgConstraints
|
2
4
|
class Deconstrainer
|
3
|
-
|
4
|
-
|
5
|
+
extend SexyPgConstraints::Helpers
|
6
|
+
|
5
7
|
def initialize(table, columns = [])
|
6
8
|
@table = table.to_s
|
7
9
|
@columns = columns
|
8
10
|
end
|
9
|
-
|
11
|
+
|
10
12
|
def method_missing(column, *constraints)
|
11
13
|
self.class.drop_constraints(@table, column.to_s, *constraints)
|
12
14
|
end
|
13
|
-
|
15
|
+
|
14
16
|
def [](*columns)
|
15
17
|
@columns = columns.map{|c| c.to_s}
|
16
18
|
self
|
17
19
|
end
|
18
|
-
|
20
|
+
|
19
21
|
def all(*constraints)
|
20
22
|
self.class.drop_constraints(@table, @columns, *constraints)
|
21
23
|
end
|
22
|
-
|
24
|
+
|
23
25
|
class << self
|
24
26
|
def drop_constraints(table, column, *constraints)
|
25
27
|
constraints.each do |type|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
module SexyPgConstraints
|
2
|
+
module Helpers
|
3
|
+
def make_title(table, column, type)
|
4
|
+
column = column.join('_') if column.respond_to?(:join)
|
5
|
+
|
6
|
+
"#{table}_#{column}_#{type}"
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute(*args)
|
10
|
+
ActiveRecord::Base.connection.execute(*args)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -8,8 +8,8 @@ module SexyPgConstraints
|
|
8
8
|
# Example:
|
9
9
|
# constrain :books, :variation, :whitelist => %w(hardcover softcover)
|
10
10
|
#
|
11
|
-
def whitelist(
|
12
|
-
|
11
|
+
def whitelist(column, options)
|
12
|
+
%{check ("#{column}" in (#{ options.collect{|v| "'#{v}'"}.join(',') }))}
|
13
13
|
end
|
14
14
|
|
15
15
|
##
|
@@ -18,21 +18,19 @@ module SexyPgConstraints
|
|
18
18
|
# Example:
|
19
19
|
# constrain :books, :isbn, :blacklist => %w(invalid_isbn1 invalid_isbn2)
|
20
20
|
#
|
21
|
-
def blacklist(
|
22
|
-
|
21
|
+
def blacklist(column, options)
|
22
|
+
%{check ("#{column}" not in (#{ options.collect{|v| "'#{v}'"}.join(',') }))}
|
23
23
|
end
|
24
24
|
|
25
25
|
##
|
26
26
|
# The value must have at least 1 non-space character.
|
27
27
|
#
|
28
28
|
# Example:
|
29
|
-
# constrain :books, :title, :
|
29
|
+
# constrain :books, :title, :present => true
|
30
30
|
#
|
31
|
-
def
|
32
|
-
|
31
|
+
def present(column, options)
|
32
|
+
%{check ( length(btrim("#{column}")) > 0 )}
|
33
33
|
end
|
34
|
-
alias_method :present, :not_blank
|
35
|
-
module_function :present
|
36
34
|
|
37
35
|
##
|
38
36
|
# The value must have characters other than those listed in the option string.
|
@@ -40,28 +38,26 @@ module SexyPgConstraints
|
|
40
38
|
# Example:
|
41
39
|
# constrain :books, :title, :not_only => 'abcd'
|
42
40
|
#
|
43
|
-
def not_only(
|
44
|
-
|
41
|
+
def not_only(column, options)
|
42
|
+
%{check ( length(btrim("#{column}", E'#{options}')) > 0 )}
|
45
43
|
end
|
46
44
|
|
47
45
|
##
|
48
46
|
# The value must not have leading or trailing spaces.
|
49
47
|
#
|
50
|
-
# You can pass a string as an option to indicate what characters are
|
48
|
+
# You can pass a string as an option to indicate what characters are stripped.
|
51
49
|
#
|
52
50
|
# Example:
|
53
|
-
# constrain :books, :title, :
|
54
|
-
# constrain :books, :title, :
|
51
|
+
# constrain :books, :title, :stripped => true
|
52
|
+
# constrain :books, :title, :stripped => "abc"
|
55
53
|
#
|
56
|
-
def
|
54
|
+
def stripped(column, options)
|
57
55
|
if options == true
|
58
|
-
|
56
|
+
%{check (length("#{column}") = length(btrim("#{column}")))}
|
59
57
|
else
|
60
|
-
|
58
|
+
%{check (length("#{column}") = length(btrim("#{column}", E'#{options}')))}
|
61
59
|
end
|
62
60
|
end
|
63
|
-
alias_method :stripped, :trimmed
|
64
|
-
module_function :stripped
|
65
61
|
|
66
62
|
##
|
67
63
|
# The numeric value must be within given range.
|
@@ -73,8 +69,8 @@ module SexyPgConstraints
|
|
73
69
|
# constrain :books, :year, :within => {:range => 1979..2009, :exclude_beginning => true, :exclude_end => true}
|
74
70
|
# (the four lines above do the same thing)
|
75
71
|
#
|
76
|
-
def within(
|
77
|
-
column_ref = column.to_s.include?('
|
72
|
+
def within(column, options)
|
73
|
+
column_ref = column.to_s.include?('"') ? column : %{"#{column}"}
|
78
74
|
if options.respond_to?(:to_hash)
|
79
75
|
options = options.to_hash
|
80
76
|
options.assert_valid_keys(:range, :exclude_end, :exclude_beginning)
|
@@ -95,8 +91,8 @@ module SexyPgConstraints
|
|
95
91
|
# Example:
|
96
92
|
# constrain :books, :author, :length_within => 4..50
|
97
93
|
#
|
98
|
-
def length_within(
|
99
|
-
within(
|
94
|
+
def length_within(column, options)
|
95
|
+
within(%{length("#{column}")}, options)
|
100
96
|
end
|
101
97
|
|
102
98
|
##
|
@@ -105,8 +101,8 @@ module SexyPgConstraints
|
|
105
101
|
# Example:
|
106
102
|
# constrain :books, :author, :email => true
|
107
103
|
#
|
108
|
-
def email(
|
109
|
-
|
104
|
+
def email(column, options)
|
105
|
+
%{check ((("#{column}")::text ~ E'^([-a-z0-9]+)@([-a-z0-9]+[.]+[a-z]{2,4})$'::text))}
|
110
106
|
end
|
111
107
|
|
112
108
|
##
|
@@ -115,8 +111,8 @@ module SexyPgConstraints
|
|
115
111
|
# Example:
|
116
112
|
# constrain :books, :author, :alphanumeric => true
|
117
113
|
#
|
118
|
-
def alphanumeric(
|
119
|
-
|
114
|
+
def alphanumeric(column, options)
|
115
|
+
%{check ((("#{column}")::text ~* '^[a-z0-9]+$'::text))}
|
120
116
|
end
|
121
117
|
|
122
118
|
##
|
@@ -125,8 +121,8 @@ module SexyPgConstraints
|
|
125
121
|
# Example:
|
126
122
|
# constrain :books, :author, :lowercase => true
|
127
123
|
#
|
128
|
-
def lowercase(
|
129
|
-
|
124
|
+
def lowercase(column, options)
|
125
|
+
%{check ("#{column}" = lower("#{column}"))}
|
130
126
|
end
|
131
127
|
|
132
128
|
##
|
@@ -135,8 +131,8 @@ module SexyPgConstraints
|
|
135
131
|
# Example:
|
136
132
|
# constrain :books, :quantity, :positive => true
|
137
133
|
#
|
138
|
-
def positive(
|
139
|
-
greater_than_or_equal_to(
|
134
|
+
def positive(column, options)
|
135
|
+
greater_than_or_equal_to(column, 0)
|
140
136
|
end
|
141
137
|
|
142
138
|
##
|
@@ -145,8 +141,8 @@ module SexyPgConstraints
|
|
145
141
|
# Example:
|
146
142
|
# constrain :books, :quantity, :greater_than => 12
|
147
143
|
#
|
148
|
-
def less_than(
|
149
|
-
|
144
|
+
def less_than(column, options)
|
145
|
+
%{check ("#{column}" < #{options})}
|
150
146
|
end
|
151
147
|
|
152
148
|
##
|
@@ -155,8 +151,8 @@ module SexyPgConstraints
|
|
155
151
|
# Example:
|
156
152
|
# constrain :books, :quantity, :greater_than => 12
|
157
153
|
#
|
158
|
-
def less_than_or_equal_to(
|
159
|
-
|
154
|
+
def less_than_or_equal_to(column, options)
|
155
|
+
%{check ("#{column}" <= #{options})}
|
160
156
|
end
|
161
157
|
|
162
158
|
##
|
@@ -165,8 +161,8 @@ module SexyPgConstraints
|
|
165
161
|
# Example:
|
166
162
|
# constrain :books, :quantity, :greater_than => 12
|
167
163
|
#
|
168
|
-
def greater_than(
|
169
|
-
|
164
|
+
def greater_than(column, options)
|
165
|
+
%{check ("#{column}" > #{options})}
|
170
166
|
end
|
171
167
|
|
172
168
|
##
|
@@ -175,8 +171,8 @@ module SexyPgConstraints
|
|
175
171
|
# Example:
|
176
172
|
# constrain :books, :quantity, :greater_than_or_equal_to => 12
|
177
173
|
#
|
178
|
-
def greater_than_or_equal_to(
|
179
|
-
|
174
|
+
def greater_than_or_equal_to(column, options)
|
175
|
+
%{check ("#{column}" >= #{options})}
|
180
176
|
end
|
181
177
|
|
182
178
|
##
|
@@ -185,8 +181,8 @@ module SexyPgConstraints
|
|
185
181
|
# Example:
|
186
182
|
# constrain :books, :quantity, :odd => true
|
187
183
|
#
|
188
|
-
def odd(
|
189
|
-
|
184
|
+
def odd(column, options)
|
185
|
+
%{check (mod("#{column}", 2) != 0)}
|
190
186
|
end
|
191
187
|
|
192
188
|
##
|
@@ -195,8 +191,8 @@ module SexyPgConstraints
|
|
195
191
|
# Example:
|
196
192
|
# constrain :books, :quantity, :even => true
|
197
193
|
#
|
198
|
-
def even(
|
199
|
-
|
194
|
+
def even(column, options)
|
195
|
+
%{check (mod("#{column}", 2) = 0)}
|
200
196
|
end
|
201
197
|
|
202
198
|
##
|
@@ -205,9 +201,9 @@ module SexyPgConstraints
|
|
205
201
|
# Example:
|
206
202
|
# constrain :books, :isbn, :unique => true
|
207
203
|
#
|
208
|
-
def unique(
|
209
|
-
|
210
|
-
"unique
|
204
|
+
def unique(column, options)
|
205
|
+
columns = Array(column).map {|c| %{"#{c}"} }.join(', ')
|
206
|
+
"unique(#{columns})"
|
211
207
|
end
|
212
208
|
|
213
209
|
##
|
@@ -218,7 +214,7 @@ module SexyPgConstraints
|
|
218
214
|
# Example:
|
219
215
|
# constrain :books, [], :xor => true
|
220
216
|
#
|
221
|
-
def xor(
|
217
|
+
def xor(column, options)
|
222
218
|
addition = Array(column).map {|c| %{("#{c}" is not null)::integer} }.join(' + ')
|
223
219
|
|
224
220
|
"check (#{addition} = 1)"
|
@@ -230,8 +226,8 @@ module SexyPgConstraints
|
|
230
226
|
# Example:
|
231
227
|
# constrain :books, :hash, :exact_length => 32
|
232
228
|
#
|
233
|
-
def exact_length(
|
234
|
-
|
229
|
+
def exact_length(column, options)
|
230
|
+
%{check ( length(trim(both from "#{column}")) = #{options} )}
|
235
231
|
end
|
236
232
|
|
237
233
|
##
|
@@ -240,17 +236,42 @@ module SexyPgConstraints
|
|
240
236
|
# Example:
|
241
237
|
# constrain :orders, :visa, :format => /^([4]{1})([0-9]{12,15})$/
|
242
238
|
#
|
243
|
-
def format(
|
244
|
-
|
239
|
+
def format(column, options)
|
240
|
+
%{check ((("#{column}")::text #{options.casefold? ? '~*' : '~'} E'#{options.source}'::text ))}
|
245
241
|
end
|
246
242
|
|
243
|
+
##
|
244
|
+
# Exclude values not matching the like pattern provided
|
245
|
+
#
|
246
|
+
# Example:
|
247
|
+
# constrain :orders, :visa, :like => '%FOO%'
|
248
|
+
#
|
249
|
+
def like(column, options)
|
250
|
+
constrain_like(column, 'LIKE', options)
|
251
|
+
end
|
252
|
+
|
253
|
+
##
|
254
|
+
# Exclude values matching the like pattern provided
|
255
|
+
#
|
256
|
+
# Example:
|
257
|
+
# constrain :orders, :visa, :not_like => '%FOO%'
|
258
|
+
#
|
259
|
+
def not_like(column, options)
|
260
|
+
constrain_like(column, 'NOT LIKE', options)
|
261
|
+
end
|
262
|
+
|
263
|
+
def constrain_like(column, operator, string)
|
264
|
+
%{check (("#{column}")::text #{operator} '#{string.gsub(/([^'])'([^'])/, '\1\'\'\2')}')}
|
265
|
+
end
|
266
|
+
private :constrain_like
|
267
|
+
|
247
268
|
##
|
248
269
|
# Add foreign key constraint.
|
249
270
|
#
|
250
271
|
# Example:
|
251
272
|
# constrain :books, :author_id, :reference => {:authors => :id, :on_delete => :cascade}
|
252
273
|
#
|
253
|
-
def reference(
|
274
|
+
def reference(column, options)
|
254
275
|
on_delete = options.delete(:on_delete)
|
255
276
|
fk_table = options.keys.first
|
256
277
|
fk_column = options[fk_table]
|
data/test/general_test.rb
CHANGED
@@ -10,12 +10,12 @@ class GeneralTest < SexyPgConstraintsTest
|
|
10
10
|
|
11
11
|
def test_block_syntax
|
12
12
|
ActiveRecord::Migration.constrain :books do |t|
|
13
|
-
t.title :
|
13
|
+
t.title :present => true
|
14
14
|
t.isbn :exact_length => 15
|
15
15
|
t.author :alphanumeric => true
|
16
16
|
end
|
17
17
|
|
18
|
-
assert_prohibits Book, :title, :
|
18
|
+
assert_prohibits Book, :title, :present do |book|
|
19
19
|
book.title = ' '
|
20
20
|
end
|
21
21
|
|
@@ -28,7 +28,7 @@ class GeneralTest < SexyPgConstraintsTest
|
|
28
28
|
end
|
29
29
|
|
30
30
|
ActiveRecord::Migration.deconstrain :books do |t|
|
31
|
-
t.title :
|
31
|
+
t.title :present
|
32
32
|
t.isbn :exact_length
|
33
33
|
t.author :alphanumeric
|
34
34
|
end
|
@@ -42,10 +42,10 @@ class GeneralTest < SexyPgConstraintsTest
|
|
42
42
|
|
43
43
|
def test_multiple_constraints_per_line
|
44
44
|
ActiveRecord::Migration.constrain :books do |t|
|
45
|
-
t.title :
|
45
|
+
t.title :present => true, :alphanumeric => true, :blacklist => %w(foo bar)
|
46
46
|
end
|
47
47
|
|
48
|
-
assert_prohibits Book, :title, [:
|
48
|
+
assert_prohibits Book, :title, [:present, :alphanumeric] do |book|
|
49
49
|
book.title = ' '
|
50
50
|
end
|
51
51
|
|
@@ -58,7 +58,7 @@ class GeneralTest < SexyPgConstraintsTest
|
|
58
58
|
end
|
59
59
|
|
60
60
|
ActiveRecord::Migration.deconstrain :books do |t|
|
61
|
-
t.title :
|
61
|
+
t.title :present, :alphanumeric, :blacklist
|
62
62
|
end
|
63
63
|
|
64
64
|
assert_allows Book do |book|
|
data/test/like_test.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class LikeTest < SexyPgConstraintsTest
|
4
|
+
def assert_protects_from(contraint, test, allows, disallows)
|
5
|
+
column = :title
|
6
|
+
constraint = {contraint => test}
|
7
|
+
ActiveRecord::Migration.constrain :books, column, constraint
|
8
|
+
|
9
|
+
assert_prohibits Book, column, constraint.keys.first do |book|
|
10
|
+
book.send("#{column}=", disallows)
|
11
|
+
end
|
12
|
+
|
13
|
+
assert_allows Book do |book|
|
14
|
+
book.send("#{column}=", allows)
|
15
|
+
end
|
16
|
+
|
17
|
+
ActiveRecord::Migration.deconstrain :books, column, contraint
|
18
|
+
|
19
|
+
assert_allows Book do |book|
|
20
|
+
book.send("#{column}=", disallows)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_protects_from_vanilla_like
|
25
|
+
assert_protects_from :like, '%FUN%', 'HAPPY FUN TIME', 'No Fun'
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_protects_from_quotey_like
|
29
|
+
assert_protects_from :like, "%'FUN'%", "HAPPY 'FUN' TIME", 'No FUN`'
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_protects_from_vanilla_not_like
|
33
|
+
assert_protects_from :not_like, '%FUN%', 'No Fun', 'HAPPY FUN TIME'
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_protects_from_quotey_not_like
|
37
|
+
assert_protects_from :not_like, "%'FUN'%", 'No FUN`', "HAPPY 'FUN' TIME"
|
38
|
+
end
|
39
|
+
end
|
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class
|
3
|
+
class PresentTest < SexyPgConstraintsTest
|
4
4
|
def assert_protects_from_blank(column, contraint)
|
5
5
|
ActiveRecord::Migration.constrain :books, column, contraint => true
|
6
6
|
|
@@ -19,15 +19,15 @@ class NotBlankTest < SexyPgConstraintsTest
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
def
|
23
|
-
assert_protects_from_blank(:author, :
|
22
|
+
def test_present
|
23
|
+
assert_protects_from_blank(:author, :present)
|
24
24
|
end
|
25
25
|
|
26
26
|
def test_present
|
27
27
|
assert_protects_from_blank(:author, :present)
|
28
28
|
end
|
29
29
|
|
30
|
-
def
|
31
|
-
assert_protects_from_blank(:as, :
|
30
|
+
def test_present_on_a_column_whose_name_is_a_sql_keyword
|
31
|
+
assert_protects_from_blank(:as, :present)
|
32
32
|
end
|
33
33
|
end
|
@@ -1,15 +1,15 @@
|
|
1
1
|
require 'test_helper'
|
2
2
|
|
3
|
-
class
|
3
|
+
class StrippedTest < SexyPgConstraintsTest
|
4
4
|
DEFAULT_PROHIBITED = [' foo', 'foo ', ' foo ']
|
5
5
|
CONFIGURABLE_PROHIBITED = ["foo \t", "\t foo"]
|
6
6
|
DEFAULT_ALLOWED = ['foo', 'foo \t']
|
7
7
|
|
8
|
-
def
|
9
|
-
ActiveRecord::Migration.constrain :books, :author, :
|
8
|
+
def test_stripped
|
9
|
+
ActiveRecord::Migration.constrain :books, :author, :stripped => true
|
10
10
|
|
11
11
|
DEFAULT_PROHIBITED.each do |prohibited|
|
12
|
-
assert_prohibits Book, :author, :
|
12
|
+
assert_prohibits Book, :author, :stripped do |book|
|
13
13
|
book.author = prohibited
|
14
14
|
end
|
15
15
|
end
|
@@ -20,7 +20,7 @@ class TrimmedTest < SexyPgConstraintsTest
|
|
20
20
|
end
|
21
21
|
end
|
22
22
|
|
23
|
-
ActiveRecord::Migration.deconstrain :books, :author, :
|
23
|
+
ActiveRecord::Migration.deconstrain :books, :author, :stripped
|
24
24
|
|
25
25
|
DEFAULT_PROHIBITED.each do |prohibited|
|
26
26
|
assert_allows Book do |book|
|
@@ -53,11 +53,11 @@ class TrimmedTest < SexyPgConstraintsTest
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
def
|
57
|
-
ActiveRecord::Migration.constrain :books, :as, :
|
56
|
+
def test_stripped_on_a_column_whose_name_is_a_sql_keyword
|
57
|
+
ActiveRecord::Migration.constrain :books, :as, :stripped => true
|
58
58
|
|
59
59
|
DEFAULT_PROHIBITED.each do |prohibited|
|
60
|
-
assert_prohibits Book, :as, :
|
60
|
+
assert_prohibits Book, :as, :stripped do |book|
|
61
61
|
book.as = prohibited
|
62
62
|
end
|
63
63
|
end
|
@@ -68,7 +68,7 @@ class TrimmedTest < SexyPgConstraintsTest
|
|
68
68
|
end
|
69
69
|
end
|
70
70
|
|
71
|
-
ActiveRecord::Migration.deconstrain :books, :as, :
|
71
|
+
ActiveRecord::Migration.deconstrain :books, :as, :stripped
|
72
72
|
|
73
73
|
DEFAULT_PROHIBITED.each do |prohibited|
|
74
74
|
assert_allows Book do |book|
|
@@ -77,12 +77,12 @@ class TrimmedTest < SexyPgConstraintsTest
|
|
77
77
|
end
|
78
78
|
end
|
79
79
|
|
80
|
-
def
|
81
|
-
ActiveRecord::Migration.constrain :books, :as, :
|
80
|
+
def test_stripped_with_a_character_list
|
81
|
+
ActiveRecord::Migration.constrain :books, :as, :stripped => '\t '
|
82
82
|
|
83
83
|
DEFAULT_PROHIBITED + CONFIGURABLE_PROHIBITED.each do |prohibited|
|
84
84
|
p prohibited
|
85
|
-
assert_prohibits Book, :as, :
|
85
|
+
assert_prohibits Book, :as, :stripped do |book|
|
86
86
|
book.as = prohibited
|
87
87
|
end
|
88
88
|
end
|
@@ -93,7 +93,7 @@ class TrimmedTest < SexyPgConstraintsTest
|
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
-
ActiveRecord::Migration.deconstrain :books, :as, :
|
96
|
+
ActiveRecord::Migration.deconstrain :books, :as, :stripped
|
97
97
|
|
98
98
|
DEFAULT_PROHIBITED + CONFIGURABLE_PROHIBITED.each do |prohibited|
|
99
99
|
assert_allows Book do |book|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: Empact-sexy_pg_constraints
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-11-16 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
17
|
-
requirement:
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,15 @@ dependencies:
|
|
22
22
|
version: 3.0.0
|
23
23
|
type: :runtime
|
24
24
|
prerelease: false
|
25
|
-
version_requirements:
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: 3.0.0
|
26
31
|
- !ruby/object:Gem::Dependency
|
27
32
|
name: pg
|
28
|
-
requirement:
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
29
34
|
none: false
|
30
35
|
requirements:
|
31
36
|
- - ! '>='
|
@@ -33,10 +38,15 @@ dependencies:
|
|
33
38
|
version: '0'
|
34
39
|
type: :runtime
|
35
40
|
prerelease: false
|
36
|
-
version_requirements:
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
37
47
|
- !ruby/object:Gem::Dependency
|
38
48
|
name: shoulda
|
39
|
-
requirement:
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
40
50
|
none: false
|
41
51
|
requirements:
|
42
52
|
- - ! '>='
|
@@ -44,10 +54,15 @@ dependencies:
|
|
44
54
|
version: '0'
|
45
55
|
type: :development
|
46
56
|
prerelease: false
|
47
|
-
version_requirements:
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
48
63
|
- !ruby/object:Gem::Dependency
|
49
64
|
name: jeweler
|
50
|
-
requirement:
|
65
|
+
requirement: !ruby/object:Gem::Requirement
|
51
66
|
none: false
|
52
67
|
requirements:
|
53
68
|
- - ! '>='
|
@@ -55,7 +70,12 @@ dependencies:
|
|
55
70
|
version: '0'
|
56
71
|
type: :development
|
57
72
|
prerelease: false
|
58
|
-
version_requirements:
|
73
|
+
version_requirements: !ruby/object:Gem::Requirement
|
74
|
+
none: false
|
75
|
+
requirements:
|
76
|
+
- - ! '>='
|
77
|
+
- !ruby/object:Gem::Version
|
78
|
+
version: '0'
|
59
79
|
description: Use migrations and simple syntax to manage constraints in PostgreSQL
|
60
80
|
DB.
|
61
81
|
email: ben.woosley@gmail.com
|
@@ -75,10 +95,10 @@ files:
|
|
75
95
|
- VERSION
|
76
96
|
- init.rb
|
77
97
|
- lib/sexy_pg_constraints.rb
|
78
|
-
- lib/sexy_pg_constraints/constrainer.rb
|
98
|
+
- lib/sexy_pg_constraints/constrainers/constrainer.rb
|
99
|
+
- lib/sexy_pg_constraints/constrainers/deconstrainer.rb
|
100
|
+
- lib/sexy_pg_constraints/constrainers/helpers.rb
|
79
101
|
- lib/sexy_pg_constraints/constraints.rb
|
80
|
-
- lib/sexy_pg_constraints/deconstrainer.rb
|
81
|
-
- lib/sexy_pg_constraints/helpers.rb
|
82
102
|
- lib/sexy_pg_constraints/railtie.rb
|
83
103
|
- lib/sexy_pg_constraints/schema_definitions.rb
|
84
104
|
- test/alphanumeric_test.rb
|
@@ -89,16 +109,17 @@ files:
|
|
89
109
|
- test/general_test.rb
|
90
110
|
- test/greater_less_than_test.rb
|
91
111
|
- test/length_within_test.rb
|
112
|
+
- test/like_test.rb
|
92
113
|
- test/lowercase_test.rb
|
93
|
-
- test/not_blank_test.rb
|
94
114
|
- test/odd_event_test.rb
|
95
115
|
- test/positive_test.rb
|
116
|
+
- test/present_test.rb
|
96
117
|
- test/reference_test.rb
|
118
|
+
- test/stripped_test.rb
|
97
119
|
- test/support/assert_prohibits_allows.rb
|
98
120
|
- test/support/database.yml.example
|
99
121
|
- test/support/models.rb
|
100
122
|
- test/test_helper.rb
|
101
|
-
- test/trimmed_test.rb
|
102
123
|
- test/whitelist_test.rb
|
103
124
|
- test/within_test.rb
|
104
125
|
- test/xor_test.rb
|
@@ -116,7 +137,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
116
137
|
version: '0'
|
117
138
|
segments:
|
118
139
|
- 0
|
119
|
-
hash:
|
140
|
+
hash: -808866161196602339
|
120
141
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
142
|
none: false
|
122
143
|
requirements:
|
@@ -125,7 +146,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
146
|
version: '0'
|
126
147
|
requirements: []
|
127
148
|
rubyforge_project:
|
128
|
-
rubygems_version: 1.8.
|
149
|
+
rubygems_version: 1.8.23
|
129
150
|
signing_key:
|
130
151
|
specification_version: 3
|
131
152
|
summary: ''
|
@@ -1,19 +0,0 @@
|
|
1
|
-
module SexyPgConstraints
|
2
|
-
module Helpers
|
3
|
-
def self.included(base)
|
4
|
-
base.extend ClassMethods
|
5
|
-
end
|
6
|
-
|
7
|
-
module ClassMethods
|
8
|
-
def make_title(table, column, type)
|
9
|
-
column = column.join('_') if column.respond_to?(:join)
|
10
|
-
|
11
|
-
"#{table}_#{column}_#{type}"
|
12
|
-
end
|
13
|
-
|
14
|
-
def execute(*args)
|
15
|
-
ActiveRecord::Base.connection.execute(*args)
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|