sequel 2.11.0 → 2.12.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.
Files changed (162) hide show
  1. data/CHANGELOG +168 -0
  2. data/README.rdoc +77 -95
  3. data/Rakefile +100 -80
  4. data/bin/sequel +2 -1
  5. data/doc/advanced_associations.rdoc +23 -32
  6. data/doc/cheat_sheet.rdoc +23 -40
  7. data/doc/dataset_filtering.rdoc +6 -6
  8. data/doc/prepared_statements.rdoc +22 -22
  9. data/doc/release_notes/2.12.0.txt +534 -0
  10. data/doc/schema.rdoc +3 -1
  11. data/doc/sharding.rdoc +8 -8
  12. data/doc/virtual_rows.rdoc +65 -0
  13. data/lib/sequel.rb +1 -1
  14. data/lib/{sequel_core → sequel}/adapters/ado.rb +3 -3
  15. data/lib/{sequel_core → sequel}/adapters/db2.rb +0 -0
  16. data/lib/{sequel_core → sequel}/adapters/dbi.rb +1 -1
  17. data/lib/{sequel_core → sequel}/adapters/do.rb +9 -5
  18. data/lib/{sequel_core → sequel}/adapters/do/mysql.rb +1 -1
  19. data/lib/{sequel_core → sequel}/adapters/do/postgres.rb +1 -1
  20. data/lib/{sequel_core → sequel}/adapters/do/sqlite.rb +1 -1
  21. data/lib/{sequel_core → sequel}/adapters/firebird.rb +84 -80
  22. data/lib/{sequel_core → sequel}/adapters/informix.rb +1 -1
  23. data/lib/{sequel_core → sequel}/adapters/jdbc.rb +21 -14
  24. data/lib/{sequel_core → sequel}/adapters/jdbc/h2.rb +14 -13
  25. data/lib/{sequel_core → sequel}/adapters/jdbc/mysql.rb +1 -1
  26. data/lib/{sequel_core → sequel}/adapters/jdbc/oracle.rb +1 -1
  27. data/lib/{sequel_core → sequel}/adapters/jdbc/postgresql.rb +1 -1
  28. data/lib/{sequel_core → sequel}/adapters/jdbc/sqlite.rb +1 -1
  29. data/lib/{sequel_core → sequel}/adapters/mysql.rb +60 -39
  30. data/lib/{sequel_core → sequel}/adapters/odbc.rb +8 -4
  31. data/lib/{sequel_core → sequel}/adapters/openbase.rb +0 -0
  32. data/lib/{sequel_core → sequel}/adapters/oracle.rb +38 -7
  33. data/lib/{sequel_core → sequel}/adapters/postgres.rb +24 -24
  34. data/lib/{sequel_core → sequel}/adapters/shared/mssql.rb +5 -5
  35. data/lib/{sequel_core → sequel}/adapters/shared/mysql.rb +126 -71
  36. data/lib/{sequel_core → sequel}/adapters/shared/oracle.rb +7 -10
  37. data/lib/{sequel_core → sequel}/adapters/shared/postgres.rb +159 -125
  38. data/lib/{sequel_core → sequel}/adapters/shared/progress.rb +1 -2
  39. data/lib/{sequel_core → sequel}/adapters/shared/sqlite.rb +72 -67
  40. data/lib/{sequel_core → sequel}/adapters/sqlite.rb +11 -7
  41. data/lib/{sequel_core → sequel}/adapters/utils/date_format.rb +0 -0
  42. data/lib/{sequel_core → sequel}/adapters/utils/stored_procedures.rb +0 -0
  43. data/lib/{sequel_core → sequel}/adapters/utils/unsupported.rb +19 -0
  44. data/lib/{sequel_core → sequel}/connection_pool.rb +7 -5
  45. data/lib/sequel/core.rb +221 -0
  46. data/lib/{sequel_core → sequel}/core_sql.rb +91 -49
  47. data/lib/{sequel_core → sequel}/database.rb +264 -149
  48. data/lib/{sequel_core/schema/generator.rb → sequel/database/schema_generator.rb} +6 -2
  49. data/lib/{sequel_core/database/schema.rb → sequel/database/schema_methods.rb} +12 -12
  50. data/lib/sequel/database/schema_sql.rb +224 -0
  51. data/lib/{sequel_core → sequel}/dataset.rb +78 -236
  52. data/lib/{sequel_core → sequel}/dataset/convenience.rb +99 -61
  53. data/lib/{sequel_core/object_graph.rb → sequel/dataset/graph.rb} +16 -14
  54. data/lib/{sequel_core → sequel}/dataset/prepared_statements.rb +1 -1
  55. data/lib/{sequel_core → sequel}/dataset/sql.rb +150 -99
  56. data/lib/sequel/deprecated.rb +593 -0
  57. data/lib/sequel/deprecated_migration.rb +91 -0
  58. data/lib/sequel/exceptions.rb +48 -0
  59. data/lib/sequel/extensions/blank.rb +42 -0
  60. data/lib/{sequel_model → sequel/extensions}/inflector.rb +8 -1
  61. data/lib/{sequel_core → sequel/extensions}/migration.rb +1 -1
  62. data/lib/{sequel_core/dataset → sequel/extensions}/pagination.rb +0 -0
  63. data/lib/{sequel_core → sequel/extensions}/pretty_table.rb +7 -0
  64. data/lib/{sequel_core/dataset → sequel/extensions}/query.rb +7 -0
  65. data/lib/sequel/extensions/string_date_time.rb +47 -0
  66. data/lib/sequel/metaprogramming.rb +43 -0
  67. data/lib/sequel/model.rb +110 -0
  68. data/lib/sequel/model/associations.rb +1300 -0
  69. data/lib/sequel/model/base.rb +937 -0
  70. data/lib/sequel/model/deprecated.rb +204 -0
  71. data/lib/sequel/model/deprecated_hooks.rb +103 -0
  72. data/lib/sequel/model/deprecated_inflector.rb +335 -0
  73. data/lib/sequel/model/deprecated_validations.rb +388 -0
  74. data/lib/sequel/model/errors.rb +39 -0
  75. data/lib/{sequel_model → sequel/model}/exceptions.rb +4 -4
  76. data/lib/sequel/model/inflections.rb +208 -0
  77. data/lib/sequel/model/plugins.rb +76 -0
  78. data/lib/sequel/plugins/caching.rb +122 -0
  79. data/lib/sequel/plugins/hook_class_methods.rb +122 -0
  80. data/lib/sequel/plugins/schema.rb +53 -0
  81. data/lib/sequel/plugins/serialization.rb +117 -0
  82. data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
  83. data/lib/sequel/plugins/validation_class_methods.rb +384 -0
  84. data/lib/sequel/plugins/validation_helpers.rb +150 -0
  85. data/lib/{sequel_core → sequel}/sql.rb +125 -190
  86. data/lib/{sequel_core → sequel}/version.rb +2 -1
  87. data/lib/sequel_core.rb +1 -172
  88. data/lib/sequel_model.rb +1 -91
  89. data/spec/adapters/firebird_spec.rb +5 -5
  90. data/spec/adapters/informix_spec.rb +1 -1
  91. data/spec/adapters/mysql_spec.rb +128 -42
  92. data/spec/adapters/oracle_spec.rb +47 -19
  93. data/spec/adapters/postgres_spec.rb +64 -52
  94. data/spec/adapters/spec_helper.rb +1 -1
  95. data/spec/adapters/sqlite_spec.rb +12 -17
  96. data/spec/{sequel_core → core}/connection_pool_spec.rb +10 -10
  97. data/spec/{sequel_core → core}/core_ext_spec.rb +19 -19
  98. data/spec/{sequel_core → core}/core_sql_spec.rb +68 -71
  99. data/spec/{sequel_core → core}/database_spec.rb +135 -99
  100. data/spec/{sequel_core → core}/dataset_spec.rb +398 -242
  101. data/spec/{sequel_core → core}/expression_filters_spec.rb +13 -13
  102. data/spec/core/migration_spec.rb +263 -0
  103. data/spec/{sequel_core → core}/object_graph_spec.rb +10 -10
  104. data/spec/{sequel_core → core}/pretty_table_spec.rb +2 -2
  105. data/spec/{sequel_core → core}/schema_generator_spec.rb +0 -0
  106. data/spec/{sequel_core → core}/schema_spec.rb +8 -10
  107. data/spec/{sequel_core → core}/spec_helper.rb +29 -2
  108. data/spec/{sequel_core → core}/version_spec.rb +0 -0
  109. data/spec/extensions/blank_spec.rb +67 -0
  110. data/spec/extensions/caching_spec.rb +201 -0
  111. data/spec/{sequel_model/hooks_spec.rb → extensions/hook_class_methods_spec.rb} +8 -23
  112. data/spec/{sequel_model → extensions}/inflector_spec.rb +3 -0
  113. data/spec/{sequel_core → extensions}/migration_spec.rb +4 -4
  114. data/spec/extensions/pagination_spec.rb +99 -0
  115. data/spec/extensions/pretty_table_spec.rb +91 -0
  116. data/spec/extensions/query_spec.rb +85 -0
  117. data/spec/{sequel_model → extensions}/schema_spec.rb +22 -1
  118. data/spec/extensions/serialization_spec.rb +109 -0
  119. data/spec/extensions/single_table_inheritance_spec.rb +53 -0
  120. data/spec/{sequel_model → extensions}/spec_helper.rb +13 -4
  121. data/spec/extensions/string_date_time_spec.rb +93 -0
  122. data/spec/{sequel_model/validations_spec.rb → extensions/validation_class_methods_spec.rb} +15 -103
  123. data/spec/extensions/validation_helpers_spec.rb +291 -0
  124. data/spec/integration/dataset_test.rb +31 -0
  125. data/spec/integration/eager_loader_test.rb +17 -30
  126. data/spec/integration/schema_test.rb +8 -5
  127. data/spec/integration/spec_helper.rb +17 -0
  128. data/spec/integration/transaction_test.rb +68 -0
  129. data/spec/{sequel_model → model}/association_reflection_spec.rb +0 -0
  130. data/spec/{sequel_model → model}/associations_spec.rb +23 -10
  131. data/spec/{sequel_model → model}/base_spec.rb +29 -20
  132. data/spec/{sequel_model → model}/caching_spec.rb +16 -14
  133. data/spec/{sequel_model → model}/dataset_methods_spec.rb +0 -0
  134. data/spec/{sequel_model → model}/eager_loading_spec.rb +8 -8
  135. data/spec/model/hooks_spec.rb +472 -0
  136. data/spec/model/inflector_spec.rb +126 -0
  137. data/spec/{sequel_model → model}/model_spec.rb +25 -20
  138. data/spec/model/plugins_spec.rb +142 -0
  139. data/spec/{sequel_model → model}/record_spec.rb +121 -62
  140. data/spec/model/schema_spec.rb +92 -0
  141. data/spec/model/spec_helper.rb +124 -0
  142. data/spec/model/validations_spec.rb +1080 -0
  143. metadata +136 -107
  144. data/lib/sequel_core/core_ext.rb +0 -217
  145. data/lib/sequel_core/dataset/callback.rb +0 -13
  146. data/lib/sequel_core/dataset/schema.rb +0 -15
  147. data/lib/sequel_core/deprecated.rb +0 -26
  148. data/lib/sequel_core/exceptions.rb +0 -44
  149. data/lib/sequel_core/schema.rb +0 -2
  150. data/lib/sequel_core/schema/sql.rb +0 -325
  151. data/lib/sequel_model/association_reflection.rb +0 -267
  152. data/lib/sequel_model/associations.rb +0 -499
  153. data/lib/sequel_model/base.rb +0 -539
  154. data/lib/sequel_model/caching.rb +0 -82
  155. data/lib/sequel_model/dataset_methods.rb +0 -26
  156. data/lib/sequel_model/eager_loading.rb +0 -370
  157. data/lib/sequel_model/hooks.rb +0 -101
  158. data/lib/sequel_model/plugins.rb +0 -62
  159. data/lib/sequel_model/record.rb +0 -568
  160. data/lib/sequel_model/schema.rb +0 -49
  161. data/lib/sequel_model/validations.rb +0 -429
  162. data/spec/sequel_model/plugins_spec.rb +0 -80
@@ -1,49 +0,0 @@
1
- module Sequel
2
- class Model
3
- # Creates table, using the column information from set_schema.
4
- def self.create_table
5
- db.create_table(table_name, :generator=>@schema)
6
- @db_schema = get_db_schema(true)
7
- columns
8
- end
9
-
10
- # Drops the table if it exists and then runs create_table. Should probably
11
- # not be used except in testing.
12
- def self.create_table!
13
- drop_table rescue nil
14
- create_table
15
- end
16
-
17
- # Drops table.
18
- def self.drop_table
19
- db.drop_table(table_name)
20
- end
21
-
22
- # Returns table schema created with set_schema for direct descendant of Model.
23
- # Does not retreive schema information from the database, see db_schema if you
24
- # want that.
25
- def self.schema
26
- @schema || (superclass.schema unless superclass == Model)
27
- end
28
-
29
- # Defines a table schema (see Schema::Generator for more information).
30
- #
31
- # This is only needed if you want to use the create_table/create_table! methods.
32
- # Will also set the dataset if you provide a name, as well as setting
33
- # the primary key if you defined one in the passed block.
34
- #
35
- # In general, it is a better idea to use migrations for production code, as
36
- # migrations allow changes to existing schema. set_schema is mostly useful for
37
- # test code or simple examples.
38
- def self.set_schema(name = nil, &block)
39
- set_dataset(db[name]) if name
40
- @schema = Schema::Generator.new(db, &block)
41
- set_primary_key(@schema.primary_key_name) if @schema.primary_key_name
42
- end
43
-
44
- # Returns true if table exists, false otherwise.
45
- def self.table_exists?
46
- db.table_exists?(table_name)
47
- end
48
- end
49
- end
@@ -1,429 +0,0 @@
1
- module Sequel
2
- class Model
3
- # Validations without an :if option are always run
4
- DEFAULT_VALIDATION_IF_PROC = proc{true}
5
-
6
- # The Validation module houses a couple of classes used by Sequel's
7
- # validation code.
8
- module Validation
9
- # Validation::Errors represents validation errors, a simple hash subclass
10
- # with a few convenience methods.
11
- class Errors < ::Hash
12
- # Assign an array of messages for each attribute on access
13
- def initialize
14
- super{|h,k| h[k] = []}
15
- end
16
-
17
- # Adds an error for the given attribute.
18
- def add(att, msg)
19
- self[att] << msg
20
- end
21
-
22
- # Return the total number of error messages.
23
- def count
24
- full_messages.length
25
- end
26
-
27
- # Returns an array of fully-formatted error messages.
28
- def full_messages
29
- inject([]) do |m, kv|
30
- att, errors = *kv
31
- errors.each {|e| m << "#{Array(att).join(' and ')} #{e}"}
32
- m
33
- end
34
- end
35
-
36
- # Returns the array of errors for the given attribute, or nil
37
- # if there are no errors for the attribute.
38
- def on(att)
39
- self[att] if include?(att)
40
- end
41
- end
42
-
43
- # The Generator class is used to generate validation definitions using
44
- # the validates {} idiom.
45
- class Generator
46
- # Initializes a new generator.
47
- def initialize(receiver ,&block)
48
- @receiver = receiver
49
- instance_eval(&block)
50
- end
51
-
52
- # Delegates method calls to the receiver by calling receiver.validates_xxx.
53
- def method_missing(m, *args, &block)
54
- @receiver.send(:"validates_#{m}", *args, &block)
55
- end
56
- end
57
- end
58
-
59
- # Returns true if validations are defined.
60
- def self.has_validations?
61
- !validations.empty?
62
- end
63
-
64
- # Instructs the model to skip validations defined in superclasses
65
- def self.skip_superclass_validations
66
- @skip_superclass_validations = true
67
- end
68
-
69
- # Defines validations by converting a longhand block into a series of
70
- # shorthand definitions. For example:
71
- #
72
- # class MyClass < Sequel::Model
73
- # validates do
74
- # length_of :name, :minimum => 6
75
- # length_of :password, :minimum => 8
76
- # end
77
- # end
78
- #
79
- # is equivalent to:
80
- # class MyClass < Sequel::Model
81
- # validates_length_of :name, :minimum => 6
82
- # validates_length_of :password, :minimum => 8
83
- # end
84
- def self.validates(&block)
85
- Validation::Generator.new(self, &block)
86
- end
87
-
88
- # Validates the given instance.
89
- def self.validate(o)
90
- if superclass.respond_to?(:validate) && !@skip_superclass_validations
91
- superclass.validate(o)
92
- end
93
- validations.each do |att, procs|
94
- v = case att
95
- when Array
96
- att.collect{|a| o.send(a)}
97
- else
98
- o.send(att)
99
- end
100
- procs.each {|tag, p| p.call(o, att, v)}
101
- end
102
- end
103
-
104
- # Validates acceptance of an attribute. Just checks that the value
105
- # is equal to the :accept option. This method is unique in that
106
- # :allow_nil is assumed to be true instead of false.
107
- #
108
- # Possible Options:
109
- # * :accept - The value required for the object to be valid (default: '1')
110
- # * :message - The message to use (default: 'is not accepted')
111
- def self.validates_acceptance_of(*atts)
112
- opts = {
113
- :message => 'is not accepted',
114
- :allow_nil => true,
115
- :accept => '1',
116
- :tag => :acceptance,
117
- }.merge!(atts.extract_options!)
118
- atts << opts
119
- validates_each(*atts) do |o, a, v|
120
- o.errors[a] << opts[:message] unless v == opts[:accept]
121
- end
122
- end
123
-
124
- # Validates confirmation of an attribute. Checks that the object has
125
- # a _confirmation value matching the current value. For example:
126
- #
127
- # validates_confirmation_of :blah
128
- #
129
- # Just makes sure that object.blah = object.blah_confirmation. Often used for passwords
130
- # or email addresses on web forms.
131
- #
132
- # Possible Options:
133
- # * :message - The message to use (default: 'is not confirmed')
134
- def self.validates_confirmation_of(*atts)
135
- opts = {
136
- :message => 'is not confirmed',
137
- :tag => :confirmation,
138
- }.merge!(atts.extract_options!)
139
- atts << opts
140
- validates_each(*atts) do |o, a, v|
141
- o.errors[a] << opts[:message] unless v == o.send(:"#{a}_confirmation")
142
- end
143
- end
144
-
145
- # Adds a validation for each of the given attributes using the supplied
146
- # block. The block must accept three arguments: instance, attribute and
147
- # value, e.g.:
148
- #
149
- # validates_each :name, :password do |object, attribute, value|
150
- # object.errors[attribute] << 'is not nice' unless value.nice?
151
- # end
152
- #
153
- # Possible Options:
154
- # * :allow_blank - Whether to skip the validation if the value is blank.
155
- # * :allow_missing - Whether to skip the validation if the attribute isn't a key in the
156
- # values hash. This is different from allow_nil, because Sequel only sends the attributes
157
- # in the values when doing an insert or update. If the attribute is not present, Sequel
158
- # doesn't specify it, so the database will use the table's default value. This is different
159
- # from having an attribute in values with a value of nil, which Sequel will send as NULL.
160
- # If your database table has a non NULL default, this may be a good option to use. You
161
- # don't want to use allow_nil, because if the attribute is in values but has a value nil,
162
- # Sequel will attempt to insert a NULL value into the database, instead of using the
163
- # database's default.
164
- # * :allow_nil - Whether to skip the validation if the value is nil.
165
- # * :if - A symbol (indicating an instance_method) or proc (which is instance_evaled)
166
- # skipping this validation if it returns nil or false.
167
- # * :tag - The tag to use for this validation.
168
- def self.validates_each(*atts, &block)
169
- opts = atts.extract_options!
170
- blk = if (i = opts[:if]) || (am = opts[:allow_missing]) || (an = opts[:allow_nil]) || (ab = opts[:allow_blank])
171
- proc do |o,a,v|
172
- next if i && !o.instance_eval(&if_proc(opts))
173
- next if an && Array(v).all?{|x| x.nil?}
174
- next if ab && Array(v).all?{|x| x.blank?}
175
- next if am && Array(a).all?{|x| !o.values.has_key?(x)}
176
- block.call(o,a,v)
177
- end
178
- else
179
- block
180
- end
181
- tag = opts[:tag]
182
- atts.each do |a|
183
- a_vals = validations[a]
184
- if tag && (old = a_vals.find{|x| x[0] == tag})
185
- old[1] = blk
186
- else
187
- a_vals << [tag, blk]
188
- end
189
- end
190
- end
191
-
192
- # Validates the format of an attribute, checking the string representation of the
193
- # value against the regular expression provided by the :with option.
194
- #
195
- # Possible Options:
196
- # * :message - The message to use (default: 'is invalid')
197
- # * :with - The regular expression to validate the value with (required).
198
- def self.validates_format_of(*atts)
199
- opts = {
200
- :message => 'is invalid',
201
- :tag => :format,
202
- }.merge!(atts.extract_options!)
203
-
204
- unless opts[:with].is_a?(Regexp)
205
- raise ArgumentError, "A regular expression must be supplied as the :with option of the options hash"
206
- end
207
-
208
- atts << opts
209
- validates_each(*atts) do |o, a, v|
210
- o.errors[a] << opts[:message] unless v.to_s =~ opts[:with]
211
- end
212
- end
213
-
214
- # Validates the length of an attribute.
215
- #
216
- # Possible Options:
217
- # * :is - The exact size required for the value to be valid (no default)
218
- # * :maximum - The maximum size allowed for the value (no default)
219
- # * :message - The message to use (no default, overrides :too_long, :too_short, and :wrong_length
220
- # options if present)
221
- # * :minimum - The minimum size allowed for the value (no default)
222
- # * :too_long - The message to use use if it the value is too long (default: 'is too long')
223
- # * :too_short - The message to use use if it the value is too short (default: 'is too short')
224
- # * :within - The array/range that must include the size of the value for it to be valid (no default)
225
- # * :wrong_length - The message to use use if it the value is not valid (default: 'is the wrong length')
226
- def self.validates_length_of(*atts)
227
- opts = {
228
- :too_long => 'is too long',
229
- :too_short => 'is too short',
230
- :wrong_length => 'is the wrong length'
231
- }.merge!(atts.extract_options!)
232
-
233
- opts[:tag] ||= ([:length] + [:maximum, :minimum, :is, :within].reject{|x| !opts.include?(x)}).join('-').to_sym
234
- atts << opts
235
- validates_each(*atts) do |o, a, v|
236
- if m = opts[:maximum]
237
- o.errors[a] << (opts[:message] || opts[:too_long]) unless v && v.size <= m
238
- end
239
- if m = opts[:minimum]
240
- o.errors[a] << (opts[:message] || opts[:too_short]) unless v && v.size >= m
241
- end
242
- if i = opts[:is]
243
- o.errors[a] << (opts[:message] || opts[:wrong_length]) unless v && v.size == i
244
- end
245
- if w = opts[:within]
246
- o.errors[a] << (opts[:message] || opts[:wrong_length]) unless v && w.include?(v.size)
247
- end
248
- end
249
- end
250
-
251
- # Validates whether an attribute is not a string. This is generally useful
252
- # in conjunction with raise_on_typecast_failure = false, where you are
253
- # passing in string values for non-string attributes (such as numbers and dates).
254
- # If typecasting fails (invalid number or date), the value of the attribute will
255
- # be a string in an invalid format, and if typecasting succeeds, the value will
256
- # not be a string.
257
- #
258
- # Possible Options:
259
- # * :message - The message to use (default: 'is a string' or 'is not a valid (integer|datetime|etc.)' if the type is known)
260
- def self.validates_not_string(*atts)
261
- opts = {
262
- :tag => :not_string,
263
- }.merge!(atts.extract_options!)
264
- atts << opts
265
- validates_each(*atts) do |o, a, v|
266
- if v.is_a?(String)
267
- unless message = opts[:message]
268
- message = if sch = o.db_schema[a] and typ = sch[:type]
269
- "is not a valid #{typ}"
270
- else
271
- "is a string"
272
- end
273
- end
274
- o.errors[a] << message
275
- end
276
- end
277
- end
278
-
279
- # Validates whether an attribute is a number.
280
- #
281
- # Possible Options:
282
- # * :message - The message to use (default: 'is not a number')
283
- # * :only_integer - Whether only integers are valid values (default: false)
284
- def self.validates_numericality_of(*atts)
285
- opts = {
286
- :message => 'is not a number',
287
- :tag => :numericality,
288
- }.merge!(atts.extract_options!)
289
- atts << opts
290
- validates_each(*atts) do |o, a, v|
291
- begin
292
- if opts[:only_integer]
293
- Kernel.Integer(v.to_s)
294
- else
295
- Kernel.Float(v.to_s)
296
- end
297
- rescue
298
- o.errors[a] << opts[:message]
299
- end
300
- end
301
- end
302
-
303
- # Validates the presence of an attribute. Requires the value not be blank,
304
- # with false considered present instead of absent.
305
- #
306
- # Possible Options:
307
- # * :message - The message to use (default: 'is not present')
308
- def self.validates_presence_of(*atts)
309
- opts = {
310
- :message => 'is not present',
311
- :tag => :presence,
312
- }.merge!(atts.extract_options!)
313
- atts << opts
314
- validates_each(*atts) do |o, a, v|
315
- o.errors[a] << opts[:message] if v.blank? && v != false
316
- end
317
- end
318
-
319
- # Validates that an attribute is within a specified range or set of values.
320
- #
321
- # Possible Options:
322
- # * :in - An array or range of values to check for validity (required)
323
- # * :message - The message to use (default: 'is not in range or set: <specified range>')
324
- def self.validates_inclusion_of(*atts)
325
- opts = atts.extract_options!
326
- unless opts[:in] && opts[:in].respond_to?(:include?)
327
- raise ArgumentError, "The :in parameter is required, and respond to include?"
328
- end
329
- opts[:message] ||= "is not in range or set: #{opts[:in].inspect}"
330
- atts << opts
331
- validates_each(*atts) do |o, a, v|
332
- o.errors[a] << opts[:message] unless opts[:in].include?(v)
333
- end
334
- end
335
-
336
- # Validates only if the fields in the model (specified by atts) are
337
- # unique in the database. Pass an array of fields instead of multiple
338
- # fields to specify that the combination of fields must be unique,
339
- # instead of that each field should have a unique value.
340
- #
341
- # This means that the code:
342
- # validates_uniqueness_of([:column1, :column2])
343
- # validates the grouping of column1 and column2 while
344
- # validates_uniqueness_of(:column1, :column2)
345
- # validates them separately.
346
- #
347
- # You should also add a unique index in the
348
- # database, as this suffers from a fairly obvious race condition.
349
- #
350
- # Possible Options:
351
- # * :message - The message to use (default: 'is already taken')
352
- def self.validates_uniqueness_of(*atts)
353
- opts = {
354
- :message => 'is already taken',
355
- :tag => :uniqueness,
356
- }.merge!(atts.extract_options!)
357
-
358
- atts << opts
359
- validates_each(*atts) do |o, a, v|
360
- error_field = a
361
- a = Array(a)
362
- v = Array(v)
363
- ds = o.class.filter(a.zip(v))
364
- num_dups = ds.count
365
- allow = if num_dups == 0
366
- # No unique value in the database
367
- true
368
- elsif num_dups > 1
369
- # Multiple "unique" values in the database!!
370
- # Someone didn't add a unique index
371
- false
372
- elsif o.new?
373
- # New record, but unique value already exists in the database
374
- false
375
- elsif ds.first === o
376
- # Unique value exists in database, but for the same record, so the update won't cause a duplicate record
377
- true
378
- else
379
- false
380
- end
381
- o.errors[error_field] << opts[:message] unless allow
382
- end
383
- end
384
-
385
- # Returns the validations hash for the class.
386
- def self.validations
387
- @validations ||= Hash.new {|h, k| h[k] = []}
388
- end
389
-
390
- ### Private Class Methods ###
391
-
392
- def self.if_proc(opts) # :nodoc:
393
- case opts[:if]
394
- when Symbol then proc{send opts[:if]}
395
- when Proc then opts[:if]
396
- when nil then DEFAULT_VALIDATION_IF_PROC
397
- else raise(::Sequel::Error, "invalid value for :if validation option")
398
- end
399
- end
400
-
401
- private_class_method :if_proc
402
-
403
- ### Instance Methods ###
404
-
405
- # Returns the validation errors associated with the object.
406
- def errors
407
- @errors ||= Validation::Errors.new
408
- end
409
-
410
- # Validates the object.
411
- def validate
412
- errors.clear
413
- if before_validation == false
414
- save_failure(:validation)
415
- false
416
- else
417
- self.class.validate(self)
418
- after_validation
419
- nil
420
- end
421
- end
422
-
423
- # Validates the object and returns true if no errors are reported.
424
- def valid?
425
- return false if validate == false
426
- errors.empty?
427
- end
428
- end
429
- end