sbf-dm-validations 1.3.0.beta

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 (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