sbf-dm-validations 1.3.0.beta

Sign up to get free protection for your applications and to get access to all the features.
Files changed (185) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +38 -0
  3. data/.rspec +1 -0
  4. data/.rubocop.yml +468 -0
  5. data/.travis.yml +51 -0
  6. data/Gemfile +60 -0
  7. data/LICENSE +21 -0
  8. data/README.rdoc +122 -0
  9. data/Rakefile +4 -0
  10. data/dm-validations.gemspec +20 -0
  11. data/lib/data_mapper/core.rb +1 -0
  12. data/lib/data_mapper/support/assertions.rb +1 -0
  13. data/lib/data_mapper/support/equalizer.rb +1 -0
  14. data/lib/data_mapper/support/ordered_set.rb +2 -0
  15. data/lib/data_mapper/validation/backward.rb +205 -0
  16. data/lib/data_mapper/validation/context.rb +57 -0
  17. data/lib/data_mapper/validation/contextual_rule_set.rb +210 -0
  18. data/lib/data_mapper/validation/exceptions.rb +7 -0
  19. data/lib/data_mapper/validation/inferred.rb +264 -0
  20. data/lib/data_mapper/validation/macros.rb +449 -0
  21. data/lib/data_mapper/validation/message_transformer.rb +111 -0
  22. data/lib/data_mapper/validation/model_extensions.rb +17 -0
  23. data/lib/data_mapper/validation/resource_extensions.rb +131 -0
  24. data/lib/data_mapper/validation/rule/absence.rb +31 -0
  25. data/lib/data_mapper/validation/rule/acceptance.rb +49 -0
  26. data/lib/data_mapper/validation/rule/block.rb +37 -0
  27. data/lib/data_mapper/validation/rule/confirmation.rb +47 -0
  28. data/lib/data_mapper/validation/rule/format/proc.rb +34 -0
  29. data/lib/data_mapper/validation/rule/format/regexp.rb +51 -0
  30. data/lib/data_mapper/validation/rule/format.rb +86 -0
  31. data/lib/data_mapper/validation/rule/formats/email.rb +54 -0
  32. data/lib/data_mapper/validation/rule/formats/url.rb +13 -0
  33. data/lib/data_mapper/validation/rule/length/equal.rb +48 -0
  34. data/lib/data_mapper/validation/rule/length/maximum.rb +50 -0
  35. data/lib/data_mapper/validation/rule/length/minimum.rb +50 -0
  36. data/lib/data_mapper/validation/rule/length/range.rb +50 -0
  37. data/lib/data_mapper/validation/rule/length.rb +96 -0
  38. data/lib/data_mapper/validation/rule/method.rb +42 -0
  39. data/lib/data_mapper/validation/rule/numericalness/equal.rb +34 -0
  40. data/lib/data_mapper/validation/rule/numericalness/greater_than.rb +34 -0
  41. data/lib/data_mapper/validation/rule/numericalness/greater_than_or_equal.rb +34 -0
  42. data/lib/data_mapper/validation/rule/numericalness/integer.rb +41 -0
  43. data/lib/data_mapper/validation/rule/numericalness/less_than.rb +34 -0
  44. data/lib/data_mapper/validation/rule/numericalness/less_than_or_equal.rb +34 -0
  45. data/lib/data_mapper/validation/rule/numericalness/not_equal.rb +34 -0
  46. data/lib/data_mapper/validation/rule/numericalness/numeric.rb +68 -0
  47. data/lib/data_mapper/validation/rule/numericalness.rb +91 -0
  48. data/lib/data_mapper/validation/rule/presence.rb +52 -0
  49. data/lib/data_mapper/validation/rule/primitive_type.rb +32 -0
  50. data/lib/data_mapper/validation/rule/uniqueness.rb +64 -0
  51. data/lib/data_mapper/validation/rule/within/range/bounded.rb +29 -0
  52. data/lib/data_mapper/validation/rule/within/range/unbounded_begin.rb +29 -0
  53. data/lib/data_mapper/validation/rule/within/range/unbounded_end.rb +29 -0
  54. data/lib/data_mapper/validation/rule/within/range.rb +55 -0
  55. data/lib/data_mapper/validation/rule/within/set.rb +45 -0
  56. data/lib/data_mapper/validation/rule/within.rb +32 -0
  57. data/lib/data_mapper/validation/rule.rb +232 -0
  58. data/lib/data_mapper/validation/rule_set.rb +157 -0
  59. data/lib/data_mapper/validation/support/object.rb +19 -0
  60. data/lib/data_mapper/validation/support/ordered_hash.rb +434 -0
  61. data/lib/data_mapper/validation/version.rb +5 -0
  62. data/lib/data_mapper/validation/violation.rb +136 -0
  63. data/lib/data_mapper/validation/violation_set.rb +115 -0
  64. data/lib/data_mapper/validation.rb +105 -0
  65. data/lib/dm-validations.rb +24 -0
  66. data/spec/data_mapper/validation/resource_extensions/save_spec.rb +56 -0
  67. data/spec/data_mapper/validation/resource_extensions/validate_spec.rb +103 -0
  68. data/spec/fixtures/barcode.rb +40 -0
  69. data/spec/fixtures/basketball_court.rb +58 -0
  70. data/spec/fixtures/basketball_player.rb +34 -0
  71. data/spec/fixtures/beta_tester_account.rb +33 -0
  72. data/spec/fixtures/bill_of_landing.rb +47 -0
  73. data/spec/fixtures/boat_dock.rb +26 -0
  74. data/spec/fixtures/city.rb +24 -0
  75. data/spec/fixtures/company.rb +93 -0
  76. data/spec/fixtures/corporate_world.rb +39 -0
  77. data/spec/fixtures/country.rb +24 -0
  78. data/spec/fixtures/ethernet_frame.rb +56 -0
  79. data/spec/fixtures/event.rb +44 -0
  80. data/spec/fixtures/g3_concert.rb +57 -0
  81. data/spec/fixtures/integer_dumped_as_string_property.rb +24 -0
  82. data/spec/fixtures/jabberwock.rb +27 -0
  83. data/spec/fixtures/kayak.rb +28 -0
  84. data/spec/fixtures/lernean_hydra.rb +39 -0
  85. data/spec/fixtures/llama_spaceship.rb +15 -0
  86. data/spec/fixtures/mathematical_function.rb +34 -0
  87. data/spec/fixtures/memory_object.rb +35 -0
  88. data/spec/fixtures/mittelschnauzer.rb +39 -0
  89. data/spec/fixtures/motor_launch.rb +21 -0
  90. data/spec/fixtures/multibyte.rb +16 -0
  91. data/spec/fixtures/page.rb +32 -0
  92. data/spec/fixtures/phone_number.rb +28 -0
  93. data/spec/fixtures/pirogue.rb +28 -0
  94. data/spec/fixtures/programming_language.rb +83 -0
  95. data/spec/fixtures/reservation.rb +38 -0
  96. data/spec/fixtures/scm_operation.rb +56 -0
  97. data/spec/fixtures/sms_message.rb +22 -0
  98. data/spec/fixtures/udp_packet.rb +49 -0
  99. data/spec/integration/absent_field_validator/absent_field_validator_spec.rb +90 -0
  100. data/spec/integration/absent_field_validator/spec_helper.rb +7 -0
  101. data/spec/integration/acceptance_validator/acceptance_validator_spec.rb +196 -0
  102. data/spec/integration/acceptance_validator/spec_helper.rb +7 -0
  103. data/spec/integration/automatic_validation/custom_messages_for_inferred_validation_spec.rb +57 -0
  104. data/spec/integration/automatic_validation/disabling_inferred_validation_spec.rb +49 -0
  105. data/spec/integration/automatic_validation/inferred_boolean_properties_validation_spec.rb +100 -0
  106. data/spec/integration/automatic_validation/inferred_float_property_validation_spec.rb +45 -0
  107. data/spec/integration/automatic_validation/inferred_format_validation_spec.rb +35 -0
  108. data/spec/integration/automatic_validation/inferred_integer_properties_validation_spec.rb +70 -0
  109. data/spec/integration/automatic_validation/inferred_length_validation_spec.rb +142 -0
  110. data/spec/integration/automatic_validation/inferred_presence_validation_spec.rb +45 -0
  111. data/spec/integration/automatic_validation/inferred_primitive_validation_spec.rb +22 -0
  112. data/spec/integration/automatic_validation/inferred_uniqueness_validation_spec.rb +48 -0
  113. data/spec/integration/automatic_validation/inferred_within_validation_spec.rb +35 -0
  114. data/spec/integration/automatic_validation/spec_helper.rb +57 -0
  115. data/spec/integration/block_validator/spec_helper.rb +5 -0
  116. data/spec/integration/conditional_validation/if_condition_spec.rb +63 -0
  117. data/spec/integration/conditional_validation/spec_helper.rb +5 -0
  118. data/spec/integration/confirmation_validator/confirmation_validator_spec.rb +76 -0
  119. data/spec/integration/confirmation_validator/spec_helper.rb +5 -0
  120. data/spec/integration/datamapper_models/association_validation_spec.rb +29 -0
  121. data/spec/integration/datamapper_models/inheritance_spec.rb +82 -0
  122. data/spec/integration/dirty_attributes/dirty_attributes_spec.rb +13 -0
  123. data/spec/integration/duplicated_validations/duplicated_validations_spec.rb +24 -0
  124. data/spec/integration/duplicated_validations/spec_helper.rb +5 -0
  125. data/spec/integration/format_validator/email_format_validator_spec.rb +139 -0
  126. data/spec/integration/format_validator/format_validator_spec.rb +64 -0
  127. data/spec/integration/format_validator/regexp_validator_spec.rb +33 -0
  128. data/spec/integration/format_validator/spec_helper.rb +5 -0
  129. data/spec/integration/format_validator/url_format_validator_spec.rb +91 -0
  130. data/spec/integration/length_validator/default_value_spec.rb +14 -0
  131. data/spec/integration/length_validator/equality_spec.rb +83 -0
  132. data/spec/integration/length_validator/error_message_spec.rb +22 -0
  133. data/spec/integration/length_validator/maximum_spec.rb +47 -0
  134. data/spec/integration/length_validator/minimum_spec.rb +54 -0
  135. data/spec/integration/length_validator/range_spec.rb +87 -0
  136. data/spec/integration/length_validator/spec_helper.rb +7 -0
  137. data/spec/integration/method_validator/method_validator_spec.rb +243 -0
  138. data/spec/integration/method_validator/spec_helper.rb +5 -0
  139. data/spec/integration/numeric_validator/equality_with_float_type_spec.rb +65 -0
  140. data/spec/integration/numeric_validator/equality_with_integer_type_spec.rb +41 -0
  141. data/spec/integration/numeric_validator/float_type_spec.rb +90 -0
  142. data/spec/integration/numeric_validator/gt_with_float_type_spec.rb +37 -0
  143. data/spec/integration/numeric_validator/gte_with_float_type_spec.rb +36 -0
  144. data/spec/integration/numeric_validator/integer_only_true_spec.rb +91 -0
  145. data/spec/integration/numeric_validator/integer_type_spec.rb +86 -0
  146. data/spec/integration/numeric_validator/lt_with_float_type_spec.rb +37 -0
  147. data/spec/integration/numeric_validator/lte_with_float_type_spec.rb +37 -0
  148. data/spec/integration/numeric_validator/spec_helper.rb +5 -0
  149. data/spec/integration/primitive_validator/primitive_validator_spec.rb +112 -0
  150. data/spec/integration/primitive_validator/spec_helper.rb +5 -0
  151. data/spec/integration/pure_ruby_objects/plain_old_ruby_object_validation_spec.rb +118 -0
  152. data/spec/integration/required_field_validator/association_spec.rb +69 -0
  153. data/spec/integration/required_field_validator/boolean_type_value_spec.rb +164 -0
  154. data/spec/integration/required_field_validator/date_type_value_spec.rb +127 -0
  155. data/spec/integration/required_field_validator/datetime_type_value_spec.rb +127 -0
  156. data/spec/integration/required_field_validator/float_type_value_spec.rb +131 -0
  157. data/spec/integration/required_field_validator/integer_type_value_spec.rb +99 -0
  158. data/spec/integration/required_field_validator/plain_old_ruby_object_spec.rb +35 -0
  159. data/spec/integration/required_field_validator/shared_examples.rb +27 -0
  160. data/spec/integration/required_field_validator/spec_helper.rb +7 -0
  161. data/spec/integration/required_field_validator/string_type_value_spec.rb +167 -0
  162. data/spec/integration/required_field_validator/text_type_value_spec.rb +49 -0
  163. data/spec/integration/shared/default_validation_context.rb +13 -0
  164. data/spec/integration/shared/valid_and_invalid_model.rb +35 -0
  165. data/spec/integration/uniqueness_validator/spec_helper.rb +5 -0
  166. data/spec/integration/uniqueness_validator/uniqueness_validator_spec.rb +116 -0
  167. data/spec/integration/within_validator/spec_helper.rb +5 -0
  168. data/spec/integration/within_validator/within_validator_spec.rb +168 -0
  169. data/spec/public/resource_spec.rb +113 -0
  170. data/spec/spec_helper.rb +28 -0
  171. data/spec/unit/contextual_validators/emptiness_spec.rb +50 -0
  172. data/spec/unit/contextual_validators/execution_spec.rb +48 -0
  173. data/spec/unit/contextual_validators/spec_helper.rb +37 -0
  174. data/spec/unit/generic_validator/equality_operator_spec.rb +26 -0
  175. data/spec/unit/generic_validator/optional_spec.rb +54 -0
  176. data/spec/unit/validators/within_validator_spec.rb +23 -0
  177. data/spec/unit/violation_set/adding_spec.rb +54 -0
  178. data/spec/unit/violation_set/emptiness_spec.rb +38 -0
  179. data/spec/unit/violation_set/enumerable_spec.rb +32 -0
  180. data/spec/unit/violation_set/reading_spec.rb +35 -0
  181. data/spec/unit/violation_set/respond_to_spec.rb +15 -0
  182. data/tasks/spec.rake +21 -0
  183. data/tasks/yard.rake +9 -0
  184. data/tasks/yardstick.rake +19 -0
  185. metadata +245 -0
@@ -0,0 +1,264 @@
1
+ module DataMapper
2
+ # for options_with_message
3
+ # TODO: rename :auto_validation => :infer_validation
4
+ Property.accept_options :auto_validation, :validates, :set, :format, :message, :messages
5
+
6
+ module Validation
7
+ module Inferred
8
+
9
+ # @api private
10
+ def property(*)
11
+ property = super
12
+
13
+ if property.options.fetch(:auto_validation, true) && !disabled_auto_validations?
14
+ rule_definitions = Validation::Inferred.rules_for_property(property)
15
+ rule_definitions.each do |rule_class, attribute_name, options|
16
+ validation_rules.add(rule_class, [attribute_name], options)
17
+ end
18
+ end
19
+
20
+ # FIXME: explicit return needed for YARD to parse this properly
21
+ return property
22
+ end
23
+
24
+ # attr_accessor :infer_validations
25
+
26
+ # TODO: replace the @disabled_auto_validations reader methods with
27
+ # a positive statement instead of negative (instead of skip/disable, etc)
28
+ # eg., @infer_validations, #infer_validations?
29
+ #
30
+ # Checks whether auto validations are currently
31
+ # disabled (see +disable_auto_validations+ method
32
+ # that takes a block)
33
+ #
34
+ # @return [TrueClass, FalseClass]
35
+ # true if auto validation is currently disabled
36
+ #
37
+ # @api public
38
+ # def infer_validations?
39
+ # defined?(@infer_validations) ? @infer_validations : true
40
+ # end
41
+
42
+ # TODO: why are there 3 entry points to this ivar?
43
+ # #disable_auto_validations, #disabled_auto_validations?, #auto_validations_disabled?
44
+ attr_reader :disable_auto_validations
45
+
46
+ # Checks whether auto validations are currently
47
+ # disabled (see +disable_auto_validations+ method
48
+ # that takes a block)
49
+ #
50
+ # @return [TrueClass, FalseClass]
51
+ # true if auto validation is currently disabled
52
+ #
53
+ # @api semipublic
54
+ def disabled_auto_validations?
55
+ @disable_auto_validations || false
56
+ end
57
+
58
+ # TODO: deprecate all but one of these 3 variants
59
+ alias_method :auto_validations_disabled?, :disabled_auto_validations?
60
+
61
+ # Disable generation of validations for the duration of the given block
62
+ #
63
+ # @api public
64
+ def without_auto_validations
65
+ previous, @disable_auto_validations = @disable_auto_validations, true
66
+ yield
67
+ ensure
68
+ @disable_auto_validations = previous
69
+ self
70
+ end
71
+
72
+ # Infer validations for a given property. This will only occur
73
+ # if the option :auto_validation is either true or left undefined.
74
+ #
75
+ # Triggers that generate validator creation
76
+ #
77
+ # :required => true
78
+ # Setting the option :required to true causes a Rule::Presence
79
+ # to be created for the property
80
+ #
81
+ # :length => 20
82
+ # Setting the option :length causes a Rule::Length to be created
83
+ # for the property.
84
+ # If the value is a Integer the Rule will have :maximum => value.
85
+ # If the value is a Range the Rule will have :within => value.
86
+ #
87
+ # :format => :predefined / lambda / Proc
88
+ # Setting the :format option causes a Rule::Format to be created
89
+ # for the property
90
+ #
91
+ # :set => ["foo", "bar", "baz"]
92
+ # Setting the :set option causes a Rule::Within to be created
93
+ # for the property
94
+ #
95
+ # Integer type
96
+ # Using a Integer type causes a Rule::Numericalness to be created
97
+ # for the property. The Rule's :integer_only option is set to true
98
+ #
99
+ # BigDecimal or Float type
100
+ # Using a Integer type causes a Rule::Numericalness to be created
101
+ # for the property. The Rule's :integer_only option will be set
102
+ # to false, and precision/scale will be set to match the Property
103
+ #
104
+ #
105
+ # Messages
106
+ #
107
+ # :messages => {..}
108
+ # Setting :messages hash replaces standard error messages
109
+ # with custom ones. For instance:
110
+ # :messages => {:presence => "Field is required",
111
+ # :format => "Field has invalid format"}
112
+ # Hash keys are: :presence, :format, :length, :is_unique,
113
+ # :is_number, :is_primitive
114
+ #
115
+ # :message => "Some message"
116
+ # It is just shortcut if only one validation option is set
117
+ #
118
+ # @api private
119
+ def self.rules_for_property(property)
120
+ rule_definitions = []
121
+
122
+ # all inferred rules should not be skipped when the value is nil
123
+ # (aside from Rule::Presence/Rule::Absence)
124
+ opts = { :allow_nil => true }
125
+
126
+ if property.options.key?(:validates)
127
+ opts[:context] = property.options[:validates]
128
+ end
129
+
130
+ rule_definitions << infer_presence( property, opts.dup)
131
+ rule_definitions << infer_length( property, opts.dup)
132
+ rule_definitions << infer_format( property, opts.dup)
133
+ rule_definitions << infer_uniqueness(property, opts.dup)
134
+ rule_definitions << infer_within( property, opts.dup)
135
+ rule_definitions << infer_type( property, opts.dup)
136
+
137
+ rule_definitions.compact
138
+ end
139
+
140
+ private
141
+
142
+ # @api private
143
+ def self.infer_presence(property, options)
144
+ return if property.allow_blank? || property.serial?
145
+
146
+ validation_options = options_with_message(options, property, :presence)
147
+
148
+ [Rule::Presence, property.name, validation_options]
149
+ end
150
+
151
+ # @api private
152
+ def self.infer_length(property, options)
153
+ # TODO: return unless property.primitive <= String (?)
154
+ return unless (property.kind_of?(Property::String) ||
155
+ property.kind_of?(Property::Text))
156
+ length = property.options.fetch(:length, Property::String.length)
157
+
158
+
159
+ if length.is_a?(Range)
160
+ if length.last == Infinity
161
+ raise ArgumentError, "Infinity is not a valid upper bound for a length range"
162
+ end
163
+ options[:within] = length
164
+ else
165
+ options[:maximum] = length
166
+ end
167
+
168
+ validation_options = options_with_message(options, property, :length)
169
+
170
+ [Rule::Length, property.name, validation_options]
171
+ end
172
+
173
+ # @api private
174
+ def self.infer_format(property, options)
175
+ return unless property.options.key?(:format)
176
+
177
+ options[:with] = property.options[:format]
178
+
179
+ validation_options = options_with_message(options, property, :format)
180
+
181
+ [Rule::Format, property.name, validation_options]
182
+ end
183
+
184
+ # @api private
185
+ def self.infer_uniqueness(property, options)
186
+ return unless property.options.key?(:unique)
187
+
188
+ case value = property.options[:unique]
189
+ when Array, Symbol
190
+ # TODO: fix this to behave like :unique_index
191
+ options[:scope] = Array(value)
192
+
193
+ validation_options = options_with_message(options, property, :is_unique)
194
+ [Rule::Uniqueness, property.name, validation_options]
195
+ when TrueClass
196
+ validation_options = options_with_message(options, property, :is_unique)
197
+ [Rule::Uniqueness, property.name, validation_options]
198
+ end
199
+ end
200
+
201
+ # @api private
202
+ def self.infer_within(property, options)
203
+ return unless property.options.key?(:set)
204
+
205
+ options[:set] = property.options[:set]
206
+
207
+ validation_options = options_with_message(options, property, :within)
208
+ [Rule::Within, property.name, validation_options]
209
+ end
210
+
211
+ # @api private
212
+ def self.infer_type(property, options)
213
+ return if property.respond_to?(:custom?) && property.custom?
214
+
215
+ if property.kind_of?(Property::Numeric)
216
+ options[:gte] = property.min if property.min
217
+ options[:lte] = property.max if property.max
218
+ end
219
+
220
+ if Integer == property.load_as
221
+ options[:integer_only] = true
222
+
223
+ validation_options = options_with_message(options, property, :is_number)
224
+ [Rule::Numericalness, property.name, validation_options]
225
+ elsif (BigDecimal == property.load_as ||
226
+ Float == property.load_as)
227
+ options[:precision] = property.precision
228
+ options[:scale] = property.scale
229
+
230
+ validation_options = options_with_message(options, property, :is_number)
231
+ [Rule::Numericalness, property.name, validation_options]
232
+ else
233
+ # We only need this in the case we don't already
234
+ # have a numeric validator, because otherwise
235
+ # it will cause duplicate validation errors
236
+ validation_options = options_with_message(options, property, :is_primitive)
237
+ [Rule::PrimitiveType, property.name, validation_options]
238
+ end
239
+ end
240
+
241
+ # TODO: eliminate this;
242
+ # mutating one arg based on a non-obvious interaction of the other two...
243
+ # well, it makes my skin crawl.
244
+ #
245
+ # @api private
246
+ def self.options_with_message(base_options, property, validator_name)
247
+ options = base_options.clone
248
+ opts = property.options
249
+
250
+ if opts.key?(:messages)
251
+ options[:message] = opts[:messages][validator_name]
252
+ elsif opts.key?(:message)
253
+ options[:message] = opts[:message]
254
+ end
255
+
256
+ options
257
+ end
258
+
259
+ end # module Inferred
260
+ end # module Validation
261
+
262
+ Model.append_extensions Validation::Inferred
263
+
264
+ end # module DataMapper
@@ -0,0 +1,449 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require 'data_mapper/validation/rule'
4
+
5
+ require 'data_mapper/validation/rule/absence'
6
+ require 'data_mapper/validation/rule/acceptance'
7
+ require 'data_mapper/validation/rule/block'
8
+ require 'data_mapper/validation/rule/confirmation'
9
+ require 'data_mapper/validation/rule/format'
10
+ require 'data_mapper/validation/rule/length'
11
+ require 'data_mapper/validation/rule/method'
12
+ require 'data_mapper/validation/rule/numericalness'
13
+ require 'data_mapper/validation/rule/presence'
14
+ require 'data_mapper/validation/rule/primitive_type'
15
+ require 'data_mapper/validation/rule/uniqueness'
16
+ require 'data_mapper/validation/rule/within'
17
+
18
+ module DataMapper
19
+ module Validation
20
+ module Macros
21
+ def self.extract_options(arguments)
22
+ arguments.last.kind_of?(Hash) ? arguments.pop : {}
23
+ end
24
+
25
+ # Validates that the specified attribute is "blank" via the
26
+ # attribute's #blank? method.
27
+ #
28
+ # @note
29
+ # dm-core's support lib adds the #blank? method to many classes,
30
+ # @see lib/dm-core/support/blank.rb (dm-core) for more information.
31
+ #
32
+ # @example [Usage]
33
+ # require 'dm-validations'
34
+ #
35
+ # class Page
36
+ # include DataMapper::Resource
37
+ #
38
+ # property :unwanted_attribute, String
39
+ # property :another_unwanted, String
40
+ # property :yet_again, String
41
+ #
42
+ # validates_absence_of :unwanted_attribute
43
+ # validates_absence_of :another_unwanted, :yet_again
44
+ #
45
+ # # a call to #validate will return false unless
46
+ # # all three attributes are blank
47
+ # end
48
+ #
49
+ def validates_absence_of(*attribute_names)
50
+ options = Macros.extract_options(attribute_names)
51
+ validation_rules.add(Rule::Absence, attribute_names, options)
52
+ end
53
+
54
+ # Validates that the attributes's value is in the set of accepted
55
+ # values.
56
+ #
57
+ # @option [Boolean] :allow_nil (true)
58
+ # true if nil is allowed, false if not allowed.
59
+ #
60
+ # @option [Array] :accept (["1", 1, "true", true, "t"])
61
+ # A list of accepted values.
62
+ #
63
+ # @example Usage
64
+ # require 'dm-validations'
65
+ #
66
+ # class Page
67
+ # include DataMapper::Resource
68
+ #
69
+ # property :license_agreement_accepted, String
70
+ # property :terms_accepted, String
71
+ # validates_acceptance_of :license_agreement, :accept => "1"
72
+ # validates_acceptance_of :terms_accepted, :allow_nil => false
73
+ #
74
+ # # a call to valid? will return false unless:
75
+ # # license_agreement is nil or "1"
76
+ # # and
77
+ # # terms_accepted is one of ["1", 1, "true", true, "t"]
78
+ #
79
+ def validates_acceptance_of(*attribute_names)
80
+ options = Macros.extract_options(attribute_names)
81
+ validation_rules.add(Rule::Acceptance, attribute_names, options)
82
+ end
83
+
84
+ # Validate using the given block. The block given needs to return:
85
+ # [result::<Boolean>, Error Message::<String>]
86
+ #
87
+ # @example [Usage]
88
+ # require 'dm-validations'
89
+ #
90
+ # class Page
91
+ # include DataMapper::Resource
92
+ #
93
+ # property :zip_code, String
94
+ #
95
+ # validates_with_block do
96
+ # if @zip_code == "94301"
97
+ # true
98
+ # else
99
+ # [false, "You're in the wrong zip code"]
100
+ # end
101
+ # end
102
+ #
103
+ # # A call to valid? will return false and
104
+ # # populate the object's errors with "You're in the
105
+ # # wrong zip code" unless zip_code == "94301"
106
+ #
107
+ # # You can also specify field:
108
+ #
109
+ # validates_with_block :zip_code do
110
+ # if @zip_code == "94301"
111
+ # true
112
+ # else
113
+ # [false, "You're in the wrong zip code"]
114
+ # end
115
+ # end
116
+ #
117
+ # # it will add returned error message to :zip_code field
118
+ #
119
+ def validates_with_block(*attribute_names, &block)
120
+ unless block_given?
121
+ raise ArgumentError, 'You need to pass a block to validates_with_block'
122
+ end
123
+
124
+ options = Macros.extract_options(attribute_names)
125
+ validation_rules.add(Rule::Block, attribute_names, options, &block)
126
+ end
127
+
128
+ # Validates that the given attribute is confirmed by another
129
+ # attribute. A common use case scenario is when you require a user to
130
+ # confirm their password, for which you use both password and
131
+ # password_confirmation attributes.
132
+ #
133
+ # @option [Boolean] :allow_nil (true)
134
+ # true or false.
135
+ #
136
+ # @option [Boolean] :allow_blank (true)
137
+ # true or false.
138
+ #
139
+ # @option [Symbol] :confirm (firstattr_confirmation)
140
+ # The attribute that you want to validate against.
141
+ #
142
+ # @example Usage
143
+ # require 'dm-validations'
144
+ #
145
+ # class Page
146
+ # include DataMapper::Resource
147
+ #
148
+ # property :password, String
149
+ # property :email, String
150
+ # attr_accessor :password_confirmation
151
+ # attr_accessor :email_repeated
152
+ #
153
+ # validates_confirmation_of :password
154
+ # validates_confirmation_of :email, :confirm => :email_repeated
155
+ #
156
+ # # a call to valid? will return false unless:
157
+ # # password == password_confirmation
158
+ # # and
159
+ # # email == email_repeated
160
+ #
161
+ def validates_confirmation_of(*attribute_names)
162
+ options = Macros.extract_options(attribute_names)
163
+ validation_rules.add(Rule::Confirmation, attribute_names, options)
164
+ end
165
+
166
+ # Validates that the attribute is in the specified format. You may
167
+ # use the :as (or :with, it's an alias) option to specify the
168
+ # pre-defined format that you want to validate against. You may also
169
+ # specify your own format via a Proc or Regexp passed to the the :as
170
+ # or :with options.
171
+ #
172
+ # @option [Boolean] :allow_nil (true)
173
+ # true or false.
174
+ #
175
+ # @option [Boolean] :allow_blank (true)
176
+ # true or false.
177
+ #
178
+ # @option [Format, Proc, Regexp] :as
179
+ # The pre-defined format, Proc or Regexp to validate against.
180
+ #
181
+ # @option [Format, Proc, Regexp] :with
182
+ # An alias for :as.
183
+ #
184
+ # :email_address (format is specified in DataMapper::Validation::Format::Email - note that unicode emails will *not* be matched under MRI1.8.7)
185
+ # :url (format is specified in DataMapper::Validation::Format::Url)
186
+ #
187
+ # @example Usage
188
+ # require 'dm-validations'
189
+ #
190
+ # class Page
191
+ # include DataMapper::Resource
192
+ #
193
+ # property :email, String
194
+ # property :zip_code, String
195
+ #
196
+ # validates_format_of :email, :as => :email_address
197
+ # validates_format_of :zip_code, :with => /^\d{5}$/
198
+ #
199
+ # # a call to valid? will return false unless:
200
+ # # email is formatted like an email address
201
+ # # and
202
+ # # zip_code is a string of 5 digits
203
+ #
204
+ def validates_format_of(*attribute_names)
205
+ options = Macros.extract_options(attribute_names)
206
+ validation_rules.add(Rule::Format, attribute_names, options)
207
+ end
208
+
209
+ # Validates that the length of the attribute is equal to, less than,
210
+ # greater than or within a certain range (depending upon the options
211
+ # you specify).
212
+ #
213
+ # @option [Boolean] :allow_nil (true)
214
+ # true or false.
215
+ #
216
+ # @option [Boolean] :allow_blank (true)
217
+ # true or false.
218
+ #
219
+ # @option [Boolean] :minimum
220
+ # Ensures that the attribute's length is greater than or equal to
221
+ # the supplied value.
222
+ #
223
+ # @option [Boolean] :min
224
+ # Alias for :minimum.
225
+ #
226
+ # @option [Boolean] :maximum
227
+ # Ensures the attribute's length is less than or equal to the
228
+ # supplied value.
229
+ #
230
+ # @option [Boolean] :max
231
+ # Alias for :maximum.
232
+ #
233
+ # @option [Boolean] :equals
234
+ # Ensures the attribute's length is equal to the supplied value.
235
+ #
236
+ # @option [Boolean] :is
237
+ # Alias for :equals.
238
+ #
239
+ # @option [Range] :in
240
+ # Given a Range, ensures that the attributes length is include?'ed
241
+ # in the Range.
242
+ #
243
+ # @option [Range] :within
244
+ # Alias for :in.
245
+ #
246
+ # @example Usage
247
+ # require 'dm-validations'
248
+ #
249
+ # class Page
250
+ # include DataMapper::Resource
251
+ #
252
+ # property high, Integer
253
+ # property low, Integer
254
+ # property just_right, Integer
255
+ #
256
+ # validates_length_of :high, :min => 100000000000
257
+ # validates_length_of :low, :equals => 0
258
+ # validates_length_of :just_right, :within => 1..10
259
+ #
260
+ # # a call to valid? will return false unless:
261
+ # # high is greater than or equal to 100000000000
262
+ # # low is equal to 0
263
+ # # just_right is between 1 and 10 (inclusive of both 1 and 10)
264
+ #
265
+ def validates_length_of(*attribute_names)
266
+ options = Macros.extract_options(attribute_names)
267
+ validation_rules.add(Rule::Length, attribute_names, options)
268
+ end
269
+
270
+ # Validate using method called on validated object. The method must
271
+ # to return either true, or a pair of [false, error message string],
272
+ # and is specified as a symbol passed with :method option.
273
+ #
274
+ # This validator does support multiple attribute_names being specified at a
275
+ # time, but we encourage you to use it with one property/method at a
276
+ # time.
277
+ #
278
+ # Real world experience shows that method validation is often useful
279
+ # when attribute needs to be virtual and not a property name.
280
+ #
281
+ # @example Usage
282
+ # require 'dm-validations'
283
+ #
284
+ # class Page
285
+ # include DataMapper::Resource
286
+ #
287
+ # property :zip_code, String
288
+ #
289
+ # validates_with_method :zip_code,
290
+ # :method => :in_the_right_location?
291
+ #
292
+ # def in_the_right_location?
293
+ # if @zip_code == "94301"
294
+ # return true
295
+ # else
296
+ # return [false, "You're in the wrong zip code"]
297
+ # end
298
+ # end
299
+ #
300
+ # # A call to valid? will return false and
301
+ # # populate the object's errors with "You're in the
302
+ # # wrong zip code" unless zip_code == "94301"
303
+ # end
304
+ def validates_with_method(*attribute_names)
305
+ options = Macros.extract_options(attribute_names)
306
+ validation_rules.add(Rule::Method, attribute_names, options)
307
+ end
308
+
309
+ # Validate whether a field is numeric.
310
+ #
311
+ # @option [Boolean] :allow_nil
312
+ # true if number can be nil, false if not.
313
+ #
314
+ # @option [Boolean] :allow_blank
315
+ # true if number can be blank, false if not.
316
+ #
317
+ # @option [String] :message
318
+ # Custom error message, also can be a callable object that takes
319
+ # an object (for pure Ruby objects) or object and property
320
+ # (for DM resources).
321
+ #
322
+ # @option [Numeric] :precision
323
+ # Required precision of a value.
324
+ #
325
+ # @option [Numeric] :scale
326
+ # Required scale of a value.
327
+ #
328
+ # @option [Numeric] :gte
329
+ # 'Greater than or equal to' requirement.
330
+ #
331
+ # @option [Numeric] :lte
332
+ # 'Less than or equal to' requirement.
333
+ #
334
+ # @option [Numeric] :lt
335
+ # 'Less than' requirement.
336
+ #
337
+ # @option [Numeric] :gt
338
+ # 'Greater than' requirement.
339
+ #
340
+ # @option [Numeric] :eq
341
+ # 'Equal' requirement.
342
+ #
343
+ # @option [Numeric] :ne
344
+ # 'Not equal' requirement.
345
+ #
346
+ # @option [Boolean] :integer_only
347
+ # Use to restrict allowed values to integers.
348
+ #
349
+ def validates_numericalness_of(*attribute_names)
350
+ options = Macros.extract_options(attribute_names)
351
+ validation_rules.add(Rule::Numericalness, attribute_names, options)
352
+ end
353
+
354
+ # Validates that the specified attribute is present.
355
+ #
356
+ # For most property types "being present" is the same as being "not
357
+ # blank" as determined by the attribute's #blank? method. However, in
358
+ # the case of Boolean, "being present" means not nil; i.e. true or
359
+ # false.
360
+ #
361
+ # @note
362
+ # dm-core's support lib adds the blank? method to many classes,
363
+ #
364
+ # @see lib/dm-core/support/blank.rb (dm-core) for more information.
365
+ #
366
+ # @example Usage
367
+ # require 'dm-validations'
368
+ #
369
+ # class Page
370
+ # include DataMapper::Resource
371
+ #
372
+ # property :required_attribute, String
373
+ # property :another_required, String
374
+ # property :yet_again, String
375
+ #
376
+ # validates_presence_of :required_attribute
377
+ # validates_presence_of :another_required, :yet_again
378
+ #
379
+ # # a call to valid? will return false unless
380
+ # # all three attributes are !blank?
381
+ # end
382
+ def validates_presence_of(*attribute_names)
383
+ options = attribute_names.last.kind_of?(Hash) ? attribute_names.pop : {}
384
+ validation_rules.add(Rule::Presence, attribute_names, options)
385
+ end
386
+
387
+ # Validates that the specified attribute is of the correct primitive
388
+ # type.
389
+ #
390
+ # @example Usage
391
+ # require 'dm-validations'
392
+ #
393
+ # class Person
394
+ # include DataMapper::Resource
395
+ #
396
+ # property :birth_date, Date
397
+ #
398
+ # validates_primitive_type_of :birth_date
399
+ #
400
+ # # a call to valid? will return false unless
401
+ # # the birth_date is something that can be properly
402
+ # # casted into a Date object.
403
+ # end
404
+ def validates_primitive_type_of(*attribute_names)
405
+ options = Macros.extract_options(attribute_names)
406
+ validation_rules.add(Rule::PrimitiveType, attribute_names, options)
407
+ end
408
+
409
+ # Validate the uniqueness of a field
410
+ #
411
+ # TODO: YARDoc for this method
412
+ def validates_uniqueness_of(*attribute_names)
413
+ options = Macros.extract_options(attribute_names)
414
+ validation_rules.add(Rule::Uniqueness, attribute_names, options)
415
+ end
416
+
417
+ # Validates that the value of a field is within a range/set.
418
+ #
419
+ # This validation is defined by passing a field along with a :set
420
+ # parameter. The :set can be a Range or any object which responds
421
+ # to the #include? method (an array, for example).
422
+ #
423
+ # @example Usage
424
+ # require 'dm-validations'
425
+ #
426
+ # class Review
427
+ # include DataMapper::Resource
428
+ #
429
+ # STATES = ['new', 'in_progress', 'published', 'archived']
430
+ #
431
+ # property :title, String
432
+ # property :body, String
433
+ # property :review_state, String
434
+ # property :rating, Integer
435
+ #
436
+ # validates_within :review_state, :set => STATES
437
+ # validates_within :rating, :set => 1..5
438
+ #
439
+ # # a call to valid? will return false unless
440
+ # # the two properties conform to their sets
441
+ # end
442
+ def validates_within(*attribute_names)
443
+ options = Macros.extract_options(attribute_names)
444
+ validation_rules.add(Rule::Within, attribute_names, options)
445
+ end
446
+
447
+ end # module Macros
448
+ end # module Validation
449
+ end # module DataMapper