epugh-sequel 0.0.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 (134) hide show
  1. data/README.rdoc +652 -0
  2. data/VERSION.yml +4 -0
  3. data/bin/sequel +104 -0
  4. data/lib/sequel.rb +1 -0
  5. data/lib/sequel/adapters/ado.rb +85 -0
  6. data/lib/sequel/adapters/db2.rb +132 -0
  7. data/lib/sequel/adapters/dbi.rb +101 -0
  8. data/lib/sequel/adapters/do.rb +197 -0
  9. data/lib/sequel/adapters/do/mysql.rb +38 -0
  10. data/lib/sequel/adapters/do/postgres.rb +92 -0
  11. data/lib/sequel/adapters/do/sqlite.rb +31 -0
  12. data/lib/sequel/adapters/firebird.rb +307 -0
  13. data/lib/sequel/adapters/informix.rb +75 -0
  14. data/lib/sequel/adapters/jdbc.rb +485 -0
  15. data/lib/sequel/adapters/jdbc/h2.rb +62 -0
  16. data/lib/sequel/adapters/jdbc/mysql.rb +56 -0
  17. data/lib/sequel/adapters/jdbc/oracle.rb +23 -0
  18. data/lib/sequel/adapters/jdbc/postgresql.rb +101 -0
  19. data/lib/sequel/adapters/jdbc/sqlite.rb +43 -0
  20. data/lib/sequel/adapters/mysql.rb +370 -0
  21. data/lib/sequel/adapters/odbc.rb +184 -0
  22. data/lib/sequel/adapters/openbase.rb +57 -0
  23. data/lib/sequel/adapters/oracle.rb +140 -0
  24. data/lib/sequel/adapters/postgres.rb +453 -0
  25. data/lib/sequel/adapters/shared/mssql.rb +93 -0
  26. data/lib/sequel/adapters/shared/mysql.rb +341 -0
  27. data/lib/sequel/adapters/shared/oracle.rb +62 -0
  28. data/lib/sequel/adapters/shared/postgres.rb +743 -0
  29. data/lib/sequel/adapters/shared/progress.rb +34 -0
  30. data/lib/sequel/adapters/shared/sqlite.rb +263 -0
  31. data/lib/sequel/adapters/sqlite.rb +243 -0
  32. data/lib/sequel/adapters/utils/date_format.rb +21 -0
  33. data/lib/sequel/adapters/utils/stored_procedures.rb +75 -0
  34. data/lib/sequel/adapters/utils/unsupported.rb +62 -0
  35. data/lib/sequel/connection_pool.rb +258 -0
  36. data/lib/sequel/core.rb +204 -0
  37. data/lib/sequel/core_sql.rb +185 -0
  38. data/lib/sequel/database.rb +687 -0
  39. data/lib/sequel/database/schema_generator.rb +324 -0
  40. data/lib/sequel/database/schema_methods.rb +164 -0
  41. data/lib/sequel/database/schema_sql.rb +324 -0
  42. data/lib/sequel/dataset.rb +422 -0
  43. data/lib/sequel/dataset/convenience.rb +237 -0
  44. data/lib/sequel/dataset/prepared_statements.rb +220 -0
  45. data/lib/sequel/dataset/sql.rb +1105 -0
  46. data/lib/sequel/deprecated.rb +529 -0
  47. data/lib/sequel/exceptions.rb +44 -0
  48. data/lib/sequel/extensions/blank.rb +42 -0
  49. data/lib/sequel/extensions/inflector.rb +288 -0
  50. data/lib/sequel/extensions/pagination.rb +96 -0
  51. data/lib/sequel/extensions/pretty_table.rb +78 -0
  52. data/lib/sequel/extensions/query.rb +48 -0
  53. data/lib/sequel/extensions/string_date_time.rb +47 -0
  54. data/lib/sequel/metaprogramming.rb +44 -0
  55. data/lib/sequel/migration.rb +212 -0
  56. data/lib/sequel/model.rb +142 -0
  57. data/lib/sequel/model/association_reflection.rb +263 -0
  58. data/lib/sequel/model/associations.rb +1024 -0
  59. data/lib/sequel/model/base.rb +911 -0
  60. data/lib/sequel/model/deprecated.rb +188 -0
  61. data/lib/sequel/model/deprecated_hooks.rb +103 -0
  62. data/lib/sequel/model/deprecated_inflector.rb +335 -0
  63. data/lib/sequel/model/deprecated_validations.rb +384 -0
  64. data/lib/sequel/model/errors.rb +37 -0
  65. data/lib/sequel/model/exceptions.rb +7 -0
  66. data/lib/sequel/model/inflections.rb +230 -0
  67. data/lib/sequel/model/plugins.rb +74 -0
  68. data/lib/sequel/object_graph.rb +230 -0
  69. data/lib/sequel/plugins/caching.rb +122 -0
  70. data/lib/sequel/plugins/hook_class_methods.rb +122 -0
  71. data/lib/sequel/plugins/schema.rb +53 -0
  72. data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
  73. data/lib/sequel/plugins/validation_class_methods.rb +373 -0
  74. data/lib/sequel/sql.rb +854 -0
  75. data/lib/sequel/version.rb +11 -0
  76. data/lib/sequel_core.rb +1 -0
  77. data/lib/sequel_model.rb +1 -0
  78. data/spec/adapters/ado_spec.rb +46 -0
  79. data/spec/adapters/firebird_spec.rb +376 -0
  80. data/spec/adapters/informix_spec.rb +96 -0
  81. data/spec/adapters/mysql_spec.rb +875 -0
  82. data/spec/adapters/oracle_spec.rb +272 -0
  83. data/spec/adapters/postgres_spec.rb +692 -0
  84. data/spec/adapters/spec_helper.rb +10 -0
  85. data/spec/adapters/sqlite_spec.rb +550 -0
  86. data/spec/core/connection_pool_spec.rb +526 -0
  87. data/spec/core/core_ext_spec.rb +156 -0
  88. data/spec/core/core_sql_spec.rb +528 -0
  89. data/spec/core/database_spec.rb +1214 -0
  90. data/spec/core/dataset_spec.rb +3513 -0
  91. data/spec/core/expression_filters_spec.rb +363 -0
  92. data/spec/core/migration_spec.rb +261 -0
  93. data/spec/core/object_graph_spec.rb +280 -0
  94. data/spec/core/pretty_table_spec.rb +58 -0
  95. data/spec/core/schema_generator_spec.rb +167 -0
  96. data/spec/core/schema_spec.rb +778 -0
  97. data/spec/core/spec_helper.rb +82 -0
  98. data/spec/core/version_spec.rb +7 -0
  99. data/spec/extensions/blank_spec.rb +67 -0
  100. data/spec/extensions/caching_spec.rb +201 -0
  101. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  102. data/spec/extensions/inflector_spec.rb +122 -0
  103. data/spec/extensions/pagination_spec.rb +99 -0
  104. data/spec/extensions/pretty_table_spec.rb +91 -0
  105. data/spec/extensions/query_spec.rb +85 -0
  106. data/spec/extensions/schema_spec.rb +111 -0
  107. data/spec/extensions/single_table_inheritance_spec.rb +53 -0
  108. data/spec/extensions/spec_helper.rb +90 -0
  109. data/spec/extensions/string_date_time_spec.rb +93 -0
  110. data/spec/extensions/validation_class_methods_spec.rb +1054 -0
  111. data/spec/integration/dataset_test.rb +160 -0
  112. data/spec/integration/eager_loader_test.rb +683 -0
  113. data/spec/integration/prepared_statement_test.rb +130 -0
  114. data/spec/integration/schema_test.rb +183 -0
  115. data/spec/integration/spec_helper.rb +75 -0
  116. data/spec/integration/type_test.rb +96 -0
  117. data/spec/model/association_reflection_spec.rb +93 -0
  118. data/spec/model/associations_spec.rb +1780 -0
  119. data/spec/model/base_spec.rb +494 -0
  120. data/spec/model/caching_spec.rb +217 -0
  121. data/spec/model/dataset_methods_spec.rb +78 -0
  122. data/spec/model/eager_loading_spec.rb +1165 -0
  123. data/spec/model/hooks_spec.rb +472 -0
  124. data/spec/model/inflector_spec.rb +126 -0
  125. data/spec/model/model_spec.rb +588 -0
  126. data/spec/model/plugins_spec.rb +142 -0
  127. data/spec/model/record_spec.rb +1243 -0
  128. data/spec/model/schema_spec.rb +92 -0
  129. data/spec/model/spec_helper.rb +124 -0
  130. data/spec/model/validations_spec.rb +1080 -0
  131. data/spec/rcov.opts +6 -0
  132. data/spec/spec.opts +0 -0
  133. data/spec/spec_config.rb.example +10 -0
  134. metadata +202 -0
@@ -0,0 +1,384 @@
1
+ module Sequel
2
+ module Plugins
3
+ module DeprecatedValidationClassMethods
4
+ module ClassMethods
5
+ # The Generator class is used to generate validation definitions using
6
+ # the validates {} idiom.
7
+ class Generator
8
+ # Initializes a new generator.
9
+ def initialize(receiver ,&block)
10
+ @receiver = receiver
11
+ instance_eval(&block)
12
+ end
13
+
14
+ # Delegates method calls to the receiver by calling receiver.validates_xxx.
15
+ def method_missing(m, *args, &block)
16
+ @receiver.send(:"validates_#{m}", *args, &block)
17
+ end
18
+ end
19
+
20
+ # Returns true if validations are defined.
21
+ def has_validations?
22
+ !validations.empty?
23
+ end
24
+
25
+ # Instructs the model to skip validations defined in superclasses
26
+ def skip_superclass_validations
27
+ @skip_superclass_validations = true
28
+ end
29
+
30
+ # Defines validations by converting a longhand block into a series of
31
+ # shorthand definitions. For example:
32
+ #
33
+ # class MyClass < Sequel::Model
34
+ # validates do
35
+ # length_of :name, :minimum => 6
36
+ # length_of :password, :minimum => 8
37
+ # end
38
+ # end
39
+ #
40
+ # is equivalent to:
41
+ # class MyClass < Sequel::Model
42
+ # validates_length_of :name, :minimum => 6
43
+ # validates_length_of :password, :minimum => 8
44
+ # end
45
+ def validates(&block)
46
+ Deprecation.deprecate('Sequel::Model.validates', 'Use Model.plugin(:validation_class_methods) first')
47
+ Generator.new(self, &block)
48
+ end
49
+
50
+ # Validates the given instance.
51
+ def validate(o)
52
+ if superclass.respond_to?(:validate) && !@skip_superclass_validations
53
+ superclass.validate(o)
54
+ end
55
+ validations.each do |att, procs|
56
+ v = case att
57
+ when Array
58
+ att.collect{|a| o.send(a)}
59
+ else
60
+ o.send(att)
61
+ end
62
+ procs.each {|tag, p| p.call(o, att, v)}
63
+ end
64
+ end
65
+
66
+ # Validates acceptance of an attribute. Just checks that the value
67
+ # is equal to the :accept option. This method is unique in that
68
+ # :allow_nil is assumed to be true instead of false.
69
+ #
70
+ # Possible Options:
71
+ # * :accept - The value required for the object to be valid (default: '1')
72
+ # * :message - The message to use (default: 'is not accepted')
73
+ def validates_acceptance_of(*atts)
74
+ Deprecation.deprecate('Sequel::Model.validates_acceptance_of', 'Use Model.plugin(:validation_class_methods) first')
75
+ opts = {
76
+ :message => 'is not accepted',
77
+ :allow_nil => true,
78
+ :accept => '1',
79
+ :tag => :acceptance,
80
+ }.merge!(extract_options!(atts))
81
+ atts << opts
82
+ validates_each(*atts) do |o, a, v|
83
+ o.errors[a] << opts[:message] unless v == opts[:accept]
84
+ end
85
+ end
86
+
87
+ # Validates confirmation of an attribute. Checks that the object has
88
+ # a _confirmation value matching the current value. For example:
89
+ #
90
+ # validates_confirmation_of :blah
91
+ #
92
+ # Just makes sure that object.blah = object.blah_confirmation. Often used for passwords
93
+ # or email addresses on web forms.
94
+ #
95
+ # Possible Options:
96
+ # * :message - The message to use (default: 'is not confirmed')
97
+ def validates_confirmation_of(*atts)
98
+ Deprecation.deprecate('Sequel::Model.validates_confirmation_of', 'Use Model.plugin(:validation_class_methods) first')
99
+ opts = {
100
+ :message => 'is not confirmed',
101
+ :tag => :confirmation,
102
+ }.merge!(extract_options!(atts))
103
+ atts << opts
104
+ validates_each(*atts) do |o, a, v|
105
+ o.errors[a] << opts[:message] unless v == o.send(:"#{a}_confirmation")
106
+ end
107
+ end
108
+
109
+ # Adds a validation for each of the given attributes using the supplied
110
+ # block. The block must accept three arguments: instance, attribute and
111
+ # value, e.g.:
112
+ #
113
+ # validates_each :name, :password do |object, attribute, value|
114
+ # object.errors[attribute] << 'is not nice' unless value.nice?
115
+ # end
116
+ #
117
+ # Possible Options:
118
+ # * :allow_blank - Whether to skip the validation if the value is blank.
119
+ # * :allow_missing - Whether to skip the validation if the attribute isn't a key in the
120
+ # values hash. This is different from allow_nil, because Sequel only sends the attributes
121
+ # in the values when doing an insert or update. If the attribute is not present, Sequel
122
+ # doesn't specify it, so the database will use the table's default value. This is different
123
+ # from having an attribute in values with a value of nil, which Sequel will send as NULL.
124
+ # If your database table has a non NULL default, this may be a good option to use. You
125
+ # don't want to use allow_nil, because if the attribute is in values but has a value nil,
126
+ # Sequel will attempt to insert a NULL value into the database, instead of using the
127
+ # database's default.
128
+ # * :allow_nil - Whether to skip the validation if the value is nil.
129
+ # * :if - A symbol (indicating an instance_method) or proc (which is instance_evaled)
130
+ # skipping this validation if it returns nil or false.
131
+ # * :tag - The tag to use for this validation.
132
+ def validates_each(*atts, &block)
133
+ Deprecation.deprecate('Sequel::Model.validates_each', 'Use Model.plugin(:validation_class_methods) first')
134
+ opts = extract_options!(atts)
135
+ blk = if (i = opts[:if]) || (am = opts[:allow_missing]) || (an = opts[:allow_nil]) || (ab = opts[:allow_blank])
136
+ proc do |o,a,v|
137
+ next if i && !validation_if_proc(o, i)
138
+ next if an && Array(v).all?{|x| x.nil?}
139
+ next if ab && Array(v).all?{|x| x.blank?}
140
+ next if am && Array(a).all?{|x| !o.values.has_key?(x)}
141
+ block.call(o,a,v)
142
+ end
143
+ else
144
+ block
145
+ end
146
+ tag = opts[:tag]
147
+ atts.each do |a|
148
+ a_vals = validations[a]
149
+ if tag && (old = a_vals.find{|x| x[0] == tag})
150
+ old[1] = blk
151
+ else
152
+ a_vals << [tag, blk]
153
+ end
154
+ end
155
+ end
156
+
157
+ # Validates the format of an attribute, checking the string representation of the
158
+ # value against the regular expression provided by the :with option.
159
+ #
160
+ # Possible Options:
161
+ # * :message - The message to use (default: 'is invalid')
162
+ # * :with - The regular expression to validate the value with (required).
163
+ def validates_format_of(*atts)
164
+ Deprecation.deprecate('Sequel::Model.validates_format_of', 'Use Model.plugin(:validation_class_methods) first')
165
+ opts = {
166
+ :message => 'is invalid',
167
+ :tag => :format,
168
+ }.merge!(extract_options!(atts))
169
+
170
+ unless opts[:with].is_a?(Regexp)
171
+ raise ArgumentError, "A regular expression must be supplied as the :with option of the options hash"
172
+ end
173
+
174
+ atts << opts
175
+ validates_each(*atts) do |o, a, v|
176
+ o.errors[a] << opts[:message] unless v.to_s =~ opts[:with]
177
+ end
178
+ end
179
+
180
+ # Validates the length of an attribute.
181
+ #
182
+ # Possible Options:
183
+ # * :is - The exact size required for the value to be valid (no default)
184
+ # * :maximum - The maximum size allowed for the value (no default)
185
+ # * :message - The message to use (no default, overrides :too_long, :too_short, and :wrong_length
186
+ # options if present)
187
+ # * :minimum - The minimum size allowed for the value (no default)
188
+ # * :too_long - The message to use use if it the value is too long (default: 'is too long')
189
+ # * :too_short - The message to use use if it the value is too short (default: 'is too short')
190
+ # * :within - The array/range that must include the size of the value for it to be valid (no default)
191
+ # * :wrong_length - The message to use use if it the value is not valid (default: 'is the wrong length')
192
+ def validates_length_of(*atts)
193
+ Deprecation.deprecate('Sequel::Model.validates_length_of', 'Use Model.plugin(:validation_class_methods) first')
194
+ opts = {
195
+ :too_long => 'is too long',
196
+ :too_short => 'is too short',
197
+ :wrong_length => 'is the wrong length'
198
+ }.merge!(extract_options!(atts))
199
+
200
+ opts[:tag] ||= ([:length] + [:maximum, :minimum, :is, :within].reject{|x| !opts.include?(x)}).join('-').to_sym
201
+ atts << opts
202
+ validates_each(*atts) do |o, a, v|
203
+ if m = opts[:maximum]
204
+ o.errors[a] << (opts[:message] || opts[:too_long]) unless v && v.size <= m
205
+ end
206
+ if m = opts[:minimum]
207
+ o.errors[a] << (opts[:message] || opts[:too_short]) unless v && v.size >= m
208
+ end
209
+ if i = opts[:is]
210
+ o.errors[a] << (opts[:message] || opts[:wrong_length]) unless v && v.size == i
211
+ end
212
+ if w = opts[:within]
213
+ o.errors[a] << (opts[:message] || opts[:wrong_length]) unless v && w.include?(v.size)
214
+ end
215
+ end
216
+ end
217
+
218
+ # Validates whether an attribute is not a string. This is generally useful
219
+ # in conjunction with raise_on_typecast_failure = false, where you are
220
+ # passing in string values for non-string attributes (such as numbers and dates).
221
+ # If typecasting fails (invalid number or date), the value of the attribute will
222
+ # be a string in an invalid format, and if typecasting succeeds, the value will
223
+ # not be a string.
224
+ #
225
+ # Possible Options:
226
+ # * :message - The message to use (default: 'is a string' or 'is not a valid (integer|datetime|etc.)' if the type is known)
227
+ def validates_not_string(*atts)
228
+ Deprecation.deprecate('Sequel::Model.validates_not_string', 'Use Model.plugin(:validation_class_methods) first')
229
+ opts = {
230
+ :tag => :not_string,
231
+ }.merge!(extract_options!(atts))
232
+ atts << opts
233
+ validates_each(*atts) do |o, a, v|
234
+ if v.is_a?(String)
235
+ unless message = opts[:message]
236
+ message = if sch = o.db_schema[a] and typ = sch[:type]
237
+ "is not a valid #{typ}"
238
+ else
239
+ "is a string"
240
+ end
241
+ end
242
+ o.errors[a] << message
243
+ end
244
+ end
245
+ end
246
+
247
+ # Validates whether an attribute is a number.
248
+ #
249
+ # Possible Options:
250
+ # * :message - The message to use (default: 'is not a number')
251
+ # * :only_integer - Whether only integers are valid values (default: false)
252
+ def validates_numericality_of(*atts)
253
+ Deprecation.deprecate('Sequel::Model.validates_numericality_of', 'Use Model.plugin(:validation_class_methods) first')
254
+ opts = {
255
+ :message => 'is not a number',
256
+ :tag => :numericality,
257
+ }.merge!(extract_options!(atts))
258
+ atts << opts
259
+ validates_each(*atts) do |o, a, v|
260
+ begin
261
+ if opts[:only_integer]
262
+ Kernel.Integer(v.to_s)
263
+ else
264
+ Kernel.Float(v.to_s)
265
+ end
266
+ rescue
267
+ o.errors[a] << opts[:message]
268
+ end
269
+ end
270
+ end
271
+
272
+ # Validates the presence of an attribute. Requires the value not be blank,
273
+ # with false considered present instead of absent.
274
+ #
275
+ # Possible Options:
276
+ # * :message - The message to use (default: 'is not present')
277
+ def validates_presence_of(*atts)
278
+ Deprecation.deprecate('Sequel::Model.validates_presence_of', 'Use Model.plugin(:validation_class_methods) first')
279
+ opts = {
280
+ :message => 'is not present',
281
+ :tag => :presence,
282
+ }.merge!(extract_options!(atts))
283
+ atts << opts
284
+ validates_each(*atts) do |o, a, v|
285
+ o.errors[a] << opts[:message] if v.blank? && v != false
286
+ end
287
+ end
288
+
289
+ # Validates that an attribute is within a specified range or set of values.
290
+ #
291
+ # Possible Options:
292
+ # * :in - An array or range of values to check for validity (required)
293
+ # * :message - The message to use (default: 'is not in range or set: <specified range>')
294
+ def validates_inclusion_of(*atts)
295
+ Deprecation.deprecate('Sequel::Model.validates_inclusion_of', 'Use Model.plugin(:validation_class_methods) first')
296
+ opts = extract_options!(atts)
297
+ unless opts[:in] && opts[:in].respond_to?(:include?)
298
+ raise ArgumentError, "The :in parameter is required, and respond to include?"
299
+ end
300
+ opts[:message] ||= "is not in range or set: #{opts[:in].inspect}"
301
+ atts << opts
302
+ validates_each(*atts) do |o, a, v|
303
+ o.errors[a] << opts[:message] unless opts[:in].include?(v)
304
+ end
305
+ end
306
+
307
+ # Validates only if the fields in the model (specified by atts) are
308
+ # unique in the database. Pass an array of fields instead of multiple
309
+ # fields to specify that the combination of fields must be unique,
310
+ # instead of that each field should have a unique value.
311
+ #
312
+ # This means that the code:
313
+ # validates_uniqueness_of([:column1, :column2])
314
+ # validates the grouping of column1 and column2 while
315
+ # validates_uniqueness_of(:column1, :column2)
316
+ # validates them separately.
317
+ #
318
+ # You should also add a unique index in the
319
+ # database, as this suffers from a fairly obvious race condition.
320
+ #
321
+ # Possible Options:
322
+ # * :message - The message to use (default: 'is already taken')
323
+ def validates_uniqueness_of(*atts)
324
+ Deprecation.deprecate('Sequel::Model.validates_uniqueness_of', 'Use Model.plugin(:validation_class_methods) first')
325
+ opts = {
326
+ :message => 'is already taken',
327
+ :tag => :uniqueness,
328
+ }.merge!(extract_options!(atts))
329
+
330
+ atts << opts
331
+ validates_each(*atts) do |o, a, v|
332
+ error_field = a
333
+ a = Array(a)
334
+ v = Array(v)
335
+ ds = o.class.filter(a.zip(v))
336
+ num_dups = ds.count
337
+ allow = if num_dups == 0
338
+ # No unique value in the database
339
+ true
340
+ elsif num_dups > 1
341
+ # Multiple "unique" values in the database!!
342
+ # Someone didn't add a unique index
343
+ false
344
+ elsif o.new?
345
+ # New record, but unique value already exists in the database
346
+ false
347
+ elsif ds.first === o
348
+ # Unique value exists in database, but for the same record, so the update won't cause a duplicate record
349
+ true
350
+ else
351
+ false
352
+ end
353
+ o.errors[error_field] << opts[:message] unless allow
354
+ end
355
+ end
356
+
357
+ # Returns the validations hash for the class.
358
+ def validations
359
+ @validations ||= Hash.new {|h, k| h[k] = []}
360
+ end
361
+
362
+ private
363
+
364
+ def validation_if_proc(o, i)
365
+ case i
366
+ when Symbol then o.send(i)
367
+ when Proc then o.instance_eval(&i)
368
+ when nil then true
369
+ else raise(::Sequel::Error, "invalid value for :if validation option")
370
+ end
371
+ end
372
+ end
373
+
374
+ module InstanceMethods
375
+ # Validates the object.
376
+ def validate
377
+ model.validate(self)
378
+ end
379
+ end
380
+ end
381
+ end
382
+
383
+ Model.plugin :deprecated_validation_class_methods
384
+ end
@@ -0,0 +1,37 @@
1
+ module Sequel
2
+ class Model
3
+ # Errors represents validation errors, a simple hash subclass
4
+ # with a few convenience methods.
5
+ class Errors < ::Hash
6
+ # Assign an array of messages for each attribute on access
7
+ def initialize
8
+ super{|h,k| h[k] = []}
9
+ end
10
+
11
+ # Adds an error for the given attribute.
12
+ def add(att, msg)
13
+ self[att] << msg
14
+ end
15
+
16
+ # Return the total number of error messages.
17
+ def count
18
+ full_messages.length
19
+ end
20
+
21
+ # Returns an array of fully-formatted error messages.
22
+ def full_messages
23
+ inject([]) do |m, kv|
24
+ att, errors = *kv
25
+ errors.each {|e| m << "#{Array(att).join(' and ')} #{e}"}
26
+ m
27
+ end
28
+ end
29
+
30
+ # Returns the array of errors for the given attribute, or nil
31
+ # if there are no errors for the attribute.
32
+ def on(att)
33
+ self[att] if include?(att)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,7 @@
1
+ module Sequel
2
+ # This exception will be raised when raise_on_save_failure is set and a validation fails
3
+ class ValidationFailed < Error;end
4
+
5
+ # This exception will be raised when raise_on_save_failure is set and a before hook fails
6
+ class BeforeHookFailed < Error;end
7
+ end
@@ -0,0 +1,230 @@
1
+ module Sequel
2
+ # Yield the Inflections module if a block is given, and return
3
+ # the Inflections module.
4
+ def self.inflections
5
+ yield Inflections if block_given?
6
+ Inflections
7
+ end
8
+
9
+ # This module acts as a singleton returned/yielded by String.inflections,
10
+ # which is used to override or specify additional inflection rules. Examples:
11
+ #
12
+ # String.inflections do |inflect|
13
+ # inflect.plural /^(ox)$/i, '\1\2en'
14
+ # inflect.singular /^(ox)en/i, '\1'
15
+ #
16
+ # inflect.irregular 'octopus', 'octopi'
17
+ #
18
+ # inflect.uncountable "equipment"
19
+ # end
20
+ #
21
+ # New rules are added at the top. So in the example above, the irregular rule for octopus will now be the first of the
22
+ # pluralization and singularization rules that is runs. This guarantees that your rules run before any of the rules that may
23
+ # already have been loaded.
24
+ module Inflections
25
+ @plurals, @singulars, @uncountables = [], [], []
26
+
27
+ class << self
28
+ attr_reader :plurals, :singulars, :uncountables
29
+ end
30
+
31
+ # Clears the loaded inflections within a given scope (default is :all). Give the scope as a symbol of the inflection type,
32
+ # the options are: :plurals, :singulars, :uncountables
33
+ #
34
+ # Examples:
35
+ # clear :all
36
+ # clear :plurals
37
+ def self.clear(scope = :all)
38
+ case scope
39
+ when :all
40
+ @plurals, @singulars, @uncountables = [], [], []
41
+ else
42
+ instance_variable_set("@#{scope}", [])
43
+ end
44
+ end
45
+
46
+ # Specifies a new irregular that applies to both pluralization and singularization at the same time. This can only be used
47
+ # for strings, not regular expressions. You simply pass the irregular in singular and plural form.
48
+ #
49
+ # Examples:
50
+ # irregular 'octopus', 'octopi'
51
+ # irregular 'person', 'people'
52
+ def self.irregular(singular, plural)
53
+ plural(Regexp.new("(#{singular[0,1]})#{singular[1..-1]}$", "i"), '\1' + plural[1..-1])
54
+ singular(Regexp.new("(#{plural[0,1]})#{plural[1..-1]}$", "i"), '\1' + singular[1..-1])
55
+ end
56
+
57
+ # Specifies a new pluralization rule and its replacement. The rule can either be a string or a regular expression.
58
+ # The replacement should always be a string that may include references to the matched data from the rule.
59
+ #
60
+ # Example:
61
+ # plural(/(x|ch|ss|sh)$/i, '\1es')
62
+ def self.plural(rule, replacement)
63
+ @plurals.insert(0, [rule, replacement])
64
+ end
65
+
66
+ # Specifies a new singularization rule and its replacement. The rule can either be a string or a regular expression.
67
+ # The replacement should always be a string that may include references to the matched data from the rule.
68
+ #
69
+ # Example:
70
+ # singular(/([^aeiouy]|qu)ies$/i, '\1y')
71
+ def self.singular(rule, replacement)
72
+ @singulars.insert(0, [rule, replacement])
73
+ end
74
+
75
+ # Add uncountable words that shouldn't be attempted inflected.
76
+ #
77
+ # Examples:
78
+ # uncountable "money"
79
+ # uncountable "money", "information"
80
+ # uncountable %w( money information rice )
81
+ def self.uncountable(*words)
82
+ (@uncountables << words).flatten!
83
+ end
84
+
85
+ # Setup the default inflections
86
+ # Commented out until the deprecated inflector is removed
87
+ =begin
88
+ plural(/$/, 's')
89
+ plural(/s$/i, 's')
90
+ plural(/(ax|test)is$/i, '\1es')
91
+ plural(/(octop|vir)us$/i, '\1i')
92
+ plural(/(alias|status)$/i, '\1es')
93
+ plural(/(bu)s$/i, '\1ses')
94
+ plural(/(buffal|tomat)o$/i, '\1oes')
95
+ plural(/([ti])um$/i, '\1a')
96
+ plural(/sis$/i, 'ses')
97
+ plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
98
+ plural(/(hive)$/i, '\1s')
99
+ plural(/([^aeiouy]|qu)y$/i, '\1ies')
100
+ plural(/(x|ch|ss|sh)$/i, '\1es')
101
+ plural(/(matr|vert|ind)ix|ex$/i, '\1ices')
102
+ plural(/([m|l])ouse$/i, '\1ice')
103
+ plural(/^(ox)$/i, '\1en')
104
+ plural(/(quiz)$/i, '\1zes')
105
+
106
+ singular(/s$/i, '')
107
+ singular(/(n)ews$/i, '\1ews')
108
+ singular(/([ti])a$/i, '\1um')
109
+ singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i, '\1\2sis')
110
+ singular(/(^analy)ses$/i, '\1sis')
111
+ singular(/([^f])ves$/i, '\1fe')
112
+ singular(/(hive)s$/i, '\1')
113
+ singular(/(tive)s$/i, '\1')
114
+ singular(/([lr])ves$/i, '\1f')
115
+ singular(/([^aeiouy]|qu)ies$/i, '\1y')
116
+ singular(/(s)eries$/i, '\1eries')
117
+ singular(/(m)ovies$/i, '\1ovie')
118
+ singular(/(x|ch|ss|sh)es$/i, '\1')
119
+ singular(/([m|l])ice$/i, '\1ouse')
120
+ singular(/(bus)es$/i, '\1')
121
+ singular(/(o)es$/i, '\1')
122
+ singular(/(shoe)s$/i, '\1')
123
+ singular(/(cris|ax|test)es$/i, '\1is')
124
+ singular(/(octop|vir)i$/i, '\1us')
125
+ singular(/(alias|status)es$/i, '\1')
126
+ singular(/^(ox)en/i, '\1')
127
+ singular(/(vert|ind)ices$/i, '\1ex')
128
+ singular(/(matr)ices$/i, '\1ix')
129
+ singular(/(quiz)zes$/i, '\1')
130
+
131
+ irregular('person', 'people')
132
+ irregular('man', 'men')
133
+ irregular('child', 'children')
134
+ irregular('sex', 'sexes')
135
+ irregular('move', 'moves')
136
+
137
+ uncountable(%w(equipment information rice money species series fish sheep))
138
+ =end
139
+ private
140
+
141
+ # By default, camelize converts the string to UpperCamelCase. If the argument to camelize
142
+ # is set to :lower then camelize produces lowerCamelCase.
143
+ #
144
+ # camelize will also convert '/' to '::' which is useful for converting paths to namespaces
145
+ #
146
+ # Examples
147
+ # "active_record".camelize #=> "ActiveRecord"
148
+ # "active_record".camelize(:lower) #=> "activeRecord"
149
+ # "active_record/errors".camelize #=> "ActiveRecord::Errors"
150
+ # "active_record/errors".camelize(:lower) #=> "activeRecord::Errors"
151
+ def camelize(s)
152
+ s = s.to_s
153
+ # return s.camelize if s.respond_to?(:camelize)
154
+ s = s.gsub(/\/(.?)/){|x| "::#{x[-1..-1].upcase unless x == '/'}"}.gsub(/(^|_)(.)/){|x| x[-1..-1].upcase}
155
+ s
156
+ end
157
+
158
+ # Constantize tries to find a declared constant with the name specified
159
+ # in the string. It raises a NameError when the name is not in CamelCase
160
+ # or is not initialized.
161
+ #
162
+ # Examples
163
+ # "Module".constantize #=> Module
164
+ # "Class".constantize #=> Class
165
+ def constantize(s)
166
+ s = s.to_s
167
+ # return s.constantize if s.respond_to?(:constantize)
168
+ raise(NameError, "#{inspect} is not a valid constant name!") unless m = /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/.match(s.to_s)
169
+ Object.module_eval("::#{m[1]}", __FILE__, __LINE__)
170
+ end
171
+
172
+ # Removes the module part from the expression in the string
173
+ #
174
+ # Examples
175
+ # "ActiveRecord::CoreExtensions::String::Inflections".demodulize #=> "Inflections"
176
+ # "Inflections".demodulize #=> "Inflections"
177
+ def demodulize(s)
178
+ s = s.to_s
179
+ # return s.demodulize if s.respond_to?(:demodulize)
180
+ s.gsub(/^.*::/, '')
181
+ end
182
+
183
+ # Returns the plural form of the word in the string.
184
+ #
185
+ # Examples
186
+ # "post".pluralize #=> "posts"
187
+ # "octopus".pluralize #=> "octopi"
188
+ # "sheep".pluralize #=> "sheep"
189
+ # "words".pluralize #=> "words"
190
+ # "the blue mailman".pluralize #=> "the blue mailmen"
191
+ # "CamelOctopus".pluralize #=> "CamelOctopi"
192
+ def pluralize(s)
193
+ s = s.to_s
194
+ # return s.pluralize if s.respond_to?(:pluralize)
195
+ result = s.dup
196
+ Inflections.plurals.each{|(rule, replacement)| break if result.gsub!(rule, replacement)} unless Inflections.uncountables.include?(s.downcase)
197
+ result
198
+ end
199
+
200
+ # The reverse of pluralize, returns the singular form of a word in a string.
201
+ #
202
+ # Examples
203
+ # "posts".singularize #=> "post"
204
+ # "octopi".singularize #=> "octopus"
205
+ # "sheep".singluarize #=> "sheep"
206
+ # "word".singluarize #=> "word"
207
+ # "the blue mailmen".singularize #=> "the blue mailman"
208
+ # "CamelOctopi".singularize #=> "CamelOctopus"
209
+ def singularize(s)
210
+ s = s.to_s
211
+ # return s.singularize if s.respond_to?(:singularize)
212
+ result = s.dup
213
+ Inflections.singulars.each{|(rule, replacement)| break if result.gsub!(rule, replacement)} unless Inflections.uncountables.include?(s.downcase)
214
+ result
215
+ end
216
+
217
+ # The reverse of camelize. Makes an underscored form from the expression in the string.
218
+ # Also changes '::' to '/' to convert namespaces to paths.
219
+ #
220
+ # Examples
221
+ # "ActiveRecord".underscore #=> "active_record"
222
+ # "ActiveRecord::Errors".underscore #=> active_record/errors
223
+ def underscore(s)
224
+ s = s.to_s
225
+ # return s.underscore if s.respond_to?(:underscore)
226
+ s.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
227
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
228
+ end
229
+ end
230
+ end