ardm-validations 1.2.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 (153) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +35 -0
  3. data/.travis.yml +11 -0
  4. data/Gemfile +51 -0
  5. data/LICENSE +21 -0
  6. data/README.rdoc +122 -0
  7. data/Rakefile +4 -0
  8. data/ardm-validations.gemspec +28 -0
  9. data/lib/ardm-validations.rb +1 -0
  10. data/lib/dm-validations.rb +169 -0
  11. data/lib/dm-validations/auto_validate.rb +252 -0
  12. data/lib/dm-validations/context.rb +66 -0
  13. data/lib/dm-validations/contextual_validators.rb +220 -0
  14. data/lib/dm-validations/exceptions.rb +5 -0
  15. data/lib/dm-validations/formats/email.rb +65 -0
  16. data/lib/dm-validations/formats/url.rb +27 -0
  17. data/lib/dm-validations/support/object.rb +18 -0
  18. data/lib/dm-validations/support/ordered_hash.rb +434 -0
  19. data/lib/dm-validations/validation_errors.rb +137 -0
  20. data/lib/dm-validations/validators/absent_field_validator.rb +58 -0
  21. data/lib/dm-validations/validators/acceptance_validator.rb +79 -0
  22. data/lib/dm-validations/validators/block_validator.rb +61 -0
  23. data/lib/dm-validations/validators/confirmation_validator.rb +92 -0
  24. data/lib/dm-validations/validators/format_validator.rb +124 -0
  25. data/lib/dm-validations/validators/generic_validator.rb +184 -0
  26. data/lib/dm-validations/validators/length_validator.rb +249 -0
  27. data/lib/dm-validations/validators/method_validator.rb +64 -0
  28. data/lib/dm-validations/validators/numeric_validator.rb +182 -0
  29. data/lib/dm-validations/validators/primitive_validator.rb +58 -0
  30. data/lib/dm-validations/validators/required_field_validator.rb +83 -0
  31. data/lib/dm-validations/validators/uniqueness_validator.rb +67 -0
  32. data/lib/dm-validations/validators/within_validator.rb +74 -0
  33. data/lib/dm-validations/version.rb +5 -0
  34. data/spec/fixtures/barcode.rb +40 -0
  35. data/spec/fixtures/basketball_court.rb +58 -0
  36. data/spec/fixtures/basketball_player.rb +34 -0
  37. data/spec/fixtures/beta_tester_account.rb +33 -0
  38. data/spec/fixtures/bill_of_landing.rb +47 -0
  39. data/spec/fixtures/boat_dock.rb +26 -0
  40. data/spec/fixtures/city.rb +24 -0
  41. data/spec/fixtures/company.rb +93 -0
  42. data/spec/fixtures/corporate_world.rb +39 -0
  43. data/spec/fixtures/country.rb +24 -0
  44. data/spec/fixtures/ethernet_frame.rb +56 -0
  45. data/spec/fixtures/event.rb +44 -0
  46. data/spec/fixtures/g3_concert.rb +57 -0
  47. data/spec/fixtures/jabberwock.rb +27 -0
  48. data/spec/fixtures/kayak.rb +28 -0
  49. data/spec/fixtures/lernean_hydra.rb +39 -0
  50. data/spec/fixtures/llama_spaceship.rb +15 -0
  51. data/spec/fixtures/mathematical_function.rb +34 -0
  52. data/spec/fixtures/memory_object.rb +30 -0
  53. data/spec/fixtures/mittelschnauzer.rb +39 -0
  54. data/spec/fixtures/motor_launch.rb +21 -0
  55. data/spec/fixtures/multibyte.rb +16 -0
  56. data/spec/fixtures/page.rb +32 -0
  57. data/spec/fixtures/phone_number.rb +28 -0
  58. data/spec/fixtures/pirogue.rb +28 -0
  59. data/spec/fixtures/programming_language.rb +83 -0
  60. data/spec/fixtures/reservation.rb +38 -0
  61. data/spec/fixtures/scm_operation.rb +56 -0
  62. data/spec/fixtures/sms_message.rb +22 -0
  63. data/spec/fixtures/udp_packet.rb +49 -0
  64. data/spec/integration/absent_field_validator/absent_field_validator_spec.rb +90 -0
  65. data/spec/integration/absent_field_validator/spec_helper.rb +7 -0
  66. data/spec/integration/acceptance_validator/acceptance_validator_spec.rb +196 -0
  67. data/spec/integration/acceptance_validator/spec_helper.rb +7 -0
  68. data/spec/integration/automatic_validation/custom_messages_for_inferred_validation_spec.rb +57 -0
  69. data/spec/integration/automatic_validation/disabling_inferred_validation_spec.rb +49 -0
  70. data/spec/integration/automatic_validation/inferred_boolean_properties_validation_spec.rb +100 -0
  71. data/spec/integration/automatic_validation/inferred_float_property_validation_spec.rb +45 -0
  72. data/spec/integration/automatic_validation/inferred_format_validation_spec.rb +35 -0
  73. data/spec/integration/automatic_validation/inferred_integer_properties_validation_spec.rb +70 -0
  74. data/spec/integration/automatic_validation/inferred_length_validation_spec.rb +142 -0
  75. data/spec/integration/automatic_validation/inferred_presence_validation_spec.rb +45 -0
  76. data/spec/integration/automatic_validation/inferred_primitive_validation_spec.rb +22 -0
  77. data/spec/integration/automatic_validation/inferred_uniqueness_validation_spec.rb +52 -0
  78. data/spec/integration/automatic_validation/inferred_within_validation_spec.rb +39 -0
  79. data/spec/integration/automatic_validation/spec_helper.rb +57 -0
  80. data/spec/integration/block_validator/block_validator_spec.rb +32 -0
  81. data/spec/integration/block_validator/spec_helper.rb +5 -0
  82. data/spec/integration/conditional_validation/if_condition_spec.rb +63 -0
  83. data/spec/integration/conditional_validation/spec_helper.rb +5 -0
  84. data/spec/integration/confirmation_validator/confirmation_validator_spec.rb +76 -0
  85. data/spec/integration/confirmation_validator/spec_helper.rb +5 -0
  86. data/spec/integration/datamapper_models/association_validation_spec.rb +29 -0
  87. data/spec/integration/datamapper_models/inheritance_spec.rb +82 -0
  88. data/spec/integration/dirty_attributes/dirty_attributes_spec.rb +13 -0
  89. data/spec/integration/duplicated_validations/duplicated_validations_spec.rb +24 -0
  90. data/spec/integration/duplicated_validations/spec_helper.rb +5 -0
  91. data/spec/integration/format_validator/email_format_validator_spec.rb +139 -0
  92. data/spec/integration/format_validator/format_validator_spec.rb +64 -0
  93. data/spec/integration/format_validator/regexp_validator_spec.rb +33 -0
  94. data/spec/integration/format_validator/spec_helper.rb +5 -0
  95. data/spec/integration/format_validator/url_format_validator_spec.rb +93 -0
  96. data/spec/integration/length_validator/default_value_spec.rb +14 -0
  97. data/spec/integration/length_validator/equality_spec.rb +87 -0
  98. data/spec/integration/length_validator/error_message_spec.rb +22 -0
  99. data/spec/integration/length_validator/maximum_spec.rb +49 -0
  100. data/spec/integration/length_validator/minimum_spec.rb +54 -0
  101. data/spec/integration/length_validator/range_spec.rb +87 -0
  102. data/spec/integration/length_validator/spec_helper.rb +7 -0
  103. data/spec/integration/method_validator/method_validator_spec.rb +241 -0
  104. data/spec/integration/method_validator/spec_helper.rb +5 -0
  105. data/spec/integration/numeric_validator/equality_with_float_type_spec.rb +65 -0
  106. data/spec/integration/numeric_validator/equality_with_integer_type_spec.rb +41 -0
  107. data/spec/integration/numeric_validator/float_type_spec.rb +90 -0
  108. data/spec/integration/numeric_validator/gt_with_float_type_spec.rb +37 -0
  109. data/spec/integration/numeric_validator/gte_with_float_type_spec.rb +37 -0
  110. data/spec/integration/numeric_validator/integer_only_true_spec.rb +91 -0
  111. data/spec/integration/numeric_validator/integer_type_spec.rb +86 -0
  112. data/spec/integration/numeric_validator/lt_with_float_type_spec.rb +37 -0
  113. data/spec/integration/numeric_validator/lte_with_float_type_spec.rb +37 -0
  114. data/spec/integration/numeric_validator/spec_helper.rb +5 -0
  115. data/spec/integration/primitive_validator/primitive_validator_spec.rb +92 -0
  116. data/spec/integration/primitive_validator/spec_helper.rb +5 -0
  117. data/spec/integration/pure_ruby_objects/plain_old_ruby_object_validation_spec.rb +118 -0
  118. data/spec/integration/required_field_validator/association_spec.rb +72 -0
  119. data/spec/integration/required_field_validator/boolean_type_value_spec.rb +155 -0
  120. data/spec/integration/required_field_validator/date_type_value_spec.rb +127 -0
  121. data/spec/integration/required_field_validator/datetime_type_value_spec.rb +127 -0
  122. data/spec/integration/required_field_validator/float_type_value_spec.rb +131 -0
  123. data/spec/integration/required_field_validator/integer_type_value_spec.rb +99 -0
  124. data/spec/integration/required_field_validator/plain_old_ruby_object_spec.rb +35 -0
  125. data/spec/integration/required_field_validator/shared_examples.rb +26 -0
  126. data/spec/integration/required_field_validator/spec_helper.rb +7 -0
  127. data/spec/integration/required_field_validator/string_type_value_spec.rb +167 -0
  128. data/spec/integration/required_field_validator/text_type_value_spec.rb +49 -0
  129. data/spec/integration/shared/default_validation_context.rb +13 -0
  130. data/spec/integration/shared/valid_and_invalid_model.rb +35 -0
  131. data/spec/integration/uniqueness_validator/spec_helper.rb +5 -0
  132. data/spec/integration/uniqueness_validator/uniqueness_validator_spec.rb +116 -0
  133. data/spec/integration/within_validator/spec_helper.rb +5 -0
  134. data/spec/integration/within_validator/within_validator_spec.rb +168 -0
  135. data/spec/public/resource_spec.rb +105 -0
  136. data/spec/rcov.opts +6 -0
  137. data/spec/spec.opts +4 -0
  138. data/spec/spec_helper.rb +29 -0
  139. data/spec/unit/contextual_validators/emptiness_spec.rb +50 -0
  140. data/spec/unit/contextual_validators/execution_spec.rb +48 -0
  141. data/spec/unit/contextual_validators/spec_helper.rb +37 -0
  142. data/spec/unit/generic_validator/equality_operator_spec.rb +26 -0
  143. data/spec/unit/generic_validator/optional_spec.rb +54 -0
  144. data/spec/unit/validation_errors/adding_spec.rb +54 -0
  145. data/spec/unit/validation_errors/emptiness_spec.rb +38 -0
  146. data/spec/unit/validation_errors/enumerable_spec.rb +32 -0
  147. data/spec/unit/validation_errors/reading_spec.rb +35 -0
  148. data/spec/unit/validation_errors/respond_to_spec.rb +15 -0
  149. data/spec/unit/validators/within_validator_spec.rb +23 -0
  150. data/tasks/spec.rake +38 -0
  151. data/tasks/yard.rake +9 -0
  152. data/tasks/yardstick.rake +19 -0
  153. metadata +256 -0
@@ -0,0 +1,184 @@
1
+ # -*- coding: utf-8 -*-
2
+ module DataMapper
3
+ module Validations
4
+ # All validators extend this base class. Validators must:
5
+ #
6
+ # * Implement the initialize method to capture its parameters, also
7
+ # calling super to have this parent class capture the optional,
8
+ # general :if and :unless parameters.
9
+ # * Implement the call method, returning true or false. The call method
10
+ # provides the validation logic.
11
+ #
12
+ # @author Guy van den Berg
13
+ # @since 0.9
14
+ class GenericValidator
15
+
16
+ attr_accessor :if_clause, :unless_clause
17
+ attr_reader :field_name, :options, :humanized_field_name
18
+
19
+ # Construct a validator. Capture the :if and :unless clauses when
20
+ # present.
21
+ #
22
+ # @param [String, Symbol] field
23
+ # The property specified for validation.
24
+ #
25
+ # @option [Symbol, Proc] :if
26
+ # The name of a method or a Proc to call to determine if the
27
+ # validation should occur.
28
+ #
29
+ # @option [Symbol, Proc] :unless
30
+ # The name of a method or a Proc to call to determine if the
31
+ # validation should not occur.
32
+ #
33
+ # @note
34
+ # All additional key/value pairs are passed through to the validator
35
+ # that is sub-classing this GenericValidator
36
+ #
37
+ def initialize(field_name, options = {})
38
+ @field_name = field_name
39
+ @options = DataMapper::Ext::Hash.except(options, :if, :unless)
40
+ @if_clause = options[:if]
41
+ @unless_clause = options[:unless]
42
+ @humanized_field_name = DataMapper::Inflector.humanize(@field_name)
43
+ end
44
+
45
+ # Add an error message to a target resource. If the error corresponds
46
+ # to a specific field of the resource, add it to that field,
47
+ # otherwise add it as a :general message.
48
+ #
49
+ # @param [Object] target
50
+ # The resource that has the error.
51
+ #
52
+ # @param [String] message
53
+ # The message to add.
54
+ #
55
+ # @param [Symbol] field_name
56
+ # The name of the field that caused the error.
57
+ #
58
+ def add_error(target, message, field_name = :general)
59
+ # TODO: should the field_name for a general message be :default???
60
+ target.errors.add(field_name, message)
61
+ end
62
+
63
+ # Call the validator. "call" is used so the operation is BoundMethod
64
+ # and Block compatible. This must be implemented in all concrete
65
+ # classes.
66
+ #
67
+ # @param [Object] target
68
+ # The resource that the validator must be called against.
69
+ #
70
+ # @return [Boolean]
71
+ # true if valid, otherwise false.
72
+ #
73
+ def call(target)
74
+ raise NotImplementedError, "#{self.class}#call must be implemented"
75
+ end
76
+
77
+ # Determines if this validator should be run against the
78
+ # target by evaluating the :if and :unless clauses
79
+ # optionally passed while specifying any validator.
80
+ #
81
+ # @param [Object] target
82
+ # The resource that we check against.
83
+ #
84
+ # @return [Boolean]
85
+ # true if should be run, otherwise false.
86
+ #
87
+ # @api private
88
+ def execute?(target)
89
+ if unless_clause = self.unless_clause
90
+ !evaluate_conditional_clause(target, unless_clause)
91
+ elsif if_clause = self.if_clause
92
+ evaluate_conditional_clause(target, if_clause)
93
+ else
94
+ true
95
+ end
96
+ end
97
+
98
+ # @api private
99
+ def evaluate_conditional_clause(target, clause)
100
+ if clause.kind_of?(Symbol)
101
+ target.__send__(clause)
102
+ elsif clause.respond_to?(:call)
103
+ clause.call(target)
104
+ end
105
+ end
106
+
107
+ # Set the default value for allow_nil and allow_blank
108
+ #
109
+ # @param [Boolean] default value
110
+ #
111
+ # @api private
112
+ def set_optional_by_default(default = true)
113
+ [ :allow_nil, :allow_blank ].each do |key|
114
+ @options[key] = true unless options.key?(key)
115
+ end
116
+ end
117
+
118
+ # Test the value to see if it is blank or nil, and if it is allowed.
119
+ # Note that allowing blank without explicitly denying nil allows nil
120
+ # values, since nil.blank? is true.
121
+ #
122
+ # @param [Object] value
123
+ # The value to test.
124
+ #
125
+ # @return [Boolean]
126
+ # true if blank/nil is allowed, and the value is blank/nil.
127
+ #
128
+ # @api private
129
+ def optional?(value)
130
+ if value.nil?
131
+ @options[:allow_nil] ||
132
+ (@options[:allow_blank] && !@options.has_key?(:allow_nil))
133
+ elsif DataMapper::Ext.blank?(value)
134
+ @options[:allow_blank]
135
+ end
136
+ end
137
+
138
+ # Returns true if validators are equal
139
+ #
140
+ # Note that this intentionally do
141
+ # validate options equality
142
+ #
143
+ # even though it is hard to imagine a situation
144
+ # when multiple validations will be used
145
+ # on the same field with the same conditions
146
+ # but different options,
147
+ # it happens to be the case every once in a while
148
+ # with inferred validations for strings/text and
149
+ # explicitly given validations with different option
150
+ # (usually as Range vs. max limit for inferred validation)
151
+ #
152
+ # @api semipublic
153
+ def ==(other)
154
+ self.class == other.class &&
155
+ self.field_name == other.field_name &&
156
+ self.if_clause == other.if_clause &&
157
+ self.unless_clause == other.unless_clause &&
158
+ self.options == other.options
159
+ end
160
+
161
+ def inspect
162
+ "<##{self.class.name} @field_name='#{@field_name}' @if_clause=#{@if_clause.inspect} @unless_clause=#{@unless_clause.inspect} @options=#{@options.inspect}>"
163
+ end
164
+
165
+ alias_method :to_s, :inspect
166
+
167
+ private
168
+
169
+ # Get the corresponding Resource property, if it exists.
170
+ #
171
+ # Note: DataMapper validations can be used on non-DataMapper resources.
172
+ # In such cases, the return value will be nil.
173
+ #
174
+ # @api private
175
+ def get_resource_property(resource, property_name)
176
+ model = resource.model if resource.respond_to?(:model)
177
+ repository = resource.repository if model
178
+ properties = model.properties(repository.name) if model
179
+ properties[property_name] if properties
180
+ end
181
+
182
+ end # class GenericValidator
183
+ end # module Validations
184
+ end # module DataMapper
@@ -0,0 +1,249 @@
1
+ module DataMapper
2
+ module Validations
3
+ class LengthValidator < GenericValidator
4
+
5
+ # Initialize a length validator
6
+ #
7
+ # @param [Symbol] field_name
8
+ # the name of the field to validate
9
+ #
10
+ # @param [Hash] options
11
+ # the validator options
12
+ #
13
+ # @api semipublic
14
+ def initialize(field_name, options)
15
+ super
16
+
17
+ @equal = options[:is] || options[:equals]
18
+ @range = options[:within] || options[:in]
19
+ @min = options[:minimum] || options[:min]
20
+ @max = options[:maximum] || options[:max]
21
+
22
+ if @min && @max
23
+ @range ||= @min..@max
24
+ end
25
+ end
26
+
27
+ # Test the resource field for validity
28
+ #
29
+ # @example when the resource field is valid
30
+ # validator.call(valid_resource) # => true
31
+ #
32
+ # @example when the resource field is not valid
33
+ # validator.call(invalid_resource) # => false
34
+ #
35
+ #
36
+ # @param [Resource] target
37
+ # the Resource to test
38
+ #
39
+ # @return [Boolean]
40
+ # true if the field is valid, false if not
41
+ #
42
+ # @api semipublic
43
+ def call(target)
44
+ value = target.validation_property_value(field_name)
45
+ return true if optional?(value)
46
+
47
+ return true unless error_message = error_message_for(value)
48
+
49
+ add_error(target, error_message, field_name)
50
+ false
51
+ end
52
+
53
+ private
54
+
55
+ # Return the error messages for the value if it is invalid
56
+ #
57
+ # @param [#to_s] value
58
+ # the value to test
59
+ #
60
+ # @return [String, nil]
61
+ # the error message if invalid, nil if not
62
+ #
63
+ # @api private
64
+ def error_message_for(value)
65
+ if error_message = send(validation_method, value_length(value.to_s))
66
+ @options.fetch(:message, error_message)
67
+ end
68
+ end
69
+
70
+ # Return the method to validate the value with
71
+ #
72
+ # @return [Symbol]
73
+ # the validation method
74
+ #
75
+ # @api private
76
+ def validation_method
77
+ @validation_method ||=
78
+ if @equal then :validate_equals
79
+ elsif @range then :validate_range
80
+ elsif @min then :validate_min
81
+ elsif @max then :validate_max
82
+ end
83
+ end
84
+
85
+ # Return the length in characters
86
+ #
87
+ # @param [#to_str] value
88
+ # the string to get the number of characters for
89
+ #
90
+ # @return [Integer]
91
+ # the number of characters in the string
92
+ #
93
+ # @api private
94
+ def value_length(value)
95
+ value.to_str.length
96
+ end
97
+
98
+ if RUBY_VERSION < '1.9'
99
+ def value_length(value)
100
+ value.to_str.scan(/./u).size
101
+ end
102
+ end
103
+
104
+ # Validate the value length is equal to the expected length
105
+ #
106
+ # @param [Integer] length
107
+ # the value length
108
+ #
109
+ # @return [String, nil]
110
+ # the error message if invalid, nil if not
111
+ #
112
+ # @api private
113
+ def validate_equals(length)
114
+ return if length == @equal
115
+
116
+ ValidationErrors.default_error_message(
117
+ :wrong_length,
118
+ humanized_field_name,
119
+ @equal
120
+ )
121
+ end
122
+
123
+ # Validate the value length is within expected range
124
+ #
125
+ # @param [Integer] length
126
+ # the value length
127
+ #
128
+ # @return [String, nil]
129
+ # the error message if invalid, nil if not
130
+ #
131
+ # @api private
132
+ def validate_range(length)
133
+ return if @range.include?(length)
134
+
135
+ ValidationErrors.default_error_message(
136
+ :length_between,
137
+ humanized_field_name,
138
+ @range.min,
139
+ @range.max
140
+ )
141
+ end
142
+
143
+ # Validate the minimum expected value length
144
+ #
145
+ # @param [Integer] length
146
+ # the value length
147
+ #
148
+ # @return [String, nil]
149
+ # the error message if invalid, nil if not
150
+ #
151
+ # @api private
152
+ def validate_min(length)
153
+ return if length >= @min
154
+
155
+ ValidationErrors.default_error_message(
156
+ :too_short,
157
+ humanized_field_name,
158
+ @min
159
+ )
160
+ end
161
+
162
+ # Validate the maximum expected value length
163
+ #
164
+ # @param [Integer] length
165
+ # the value length
166
+ #
167
+ # @return [String, nil]
168
+ # the error message if invalid, nil if not
169
+ #
170
+ # @api private
171
+ def validate_max(length)
172
+ return if length <= @max
173
+
174
+ ValidationErrors.default_error_message(
175
+ :too_long,
176
+ humanized_field_name,
177
+ @max
178
+ )
179
+ end
180
+
181
+ end # class LengthValidator
182
+
183
+ module ValidatesLength
184
+ extend Deprecate
185
+
186
+ # Validates that the length of the attribute is equal to, less than,
187
+ # greater than or within a certain range (depending upon the options
188
+ # you specify).
189
+ #
190
+ # @option [Boolean] :allow_nil (true)
191
+ # true or false.
192
+ #
193
+ # @option [Boolean] :allow_blank (true)
194
+ # true or false.
195
+ #
196
+ # @option [Boolean] :minimum
197
+ # Ensures that the attribute's length is greater than or equal to
198
+ # the supplied value.
199
+ #
200
+ # @option [Boolean] :min
201
+ # Alias for :minimum.
202
+ #
203
+ # @option [Boolean] :maximum
204
+ # Ensures the attribute's length is less than or equal to the
205
+ # supplied value.
206
+ #
207
+ # @option [Boolean] :max
208
+ # Alias for :maximum.
209
+ #
210
+ # @option [Boolean] :equals
211
+ # Ensures the attribute's length is equal to the supplied value.
212
+ #
213
+ # @option [Boolean] :is
214
+ # Alias for :equals.
215
+ #
216
+ # @option [Range] :in
217
+ # Given a Range, ensures that the attributes length is include?'ed
218
+ # in the Range.
219
+ #
220
+ # @option [Range] :within
221
+ # Alias for :in.
222
+ #
223
+ # @example Usage
224
+ # require 'dm-validations'
225
+ #
226
+ # class Page
227
+ # include DataMapper::Resource
228
+ #
229
+ # property high, Integer
230
+ # property low, Integer
231
+ # property just_right, Integer
232
+ #
233
+ # validates_length_of :high, :min => 100000000000
234
+ # validates_length_of :low, :equals => 0
235
+ # validates_length_of :just_right, :within => 1..10
236
+ #
237
+ # # a call to valid? will return false unless:
238
+ # # high is greater than or equal to 100000000000
239
+ # # low is equal to 0
240
+ # # just_right is between 1 and 10 (inclusive of both 1 and 10)
241
+ #
242
+ def validates_length_of(*fields)
243
+ validators.add(LengthValidator, *fields)
244
+ end
245
+
246
+ deprecate :validates_length, :validates_length_of
247
+ end # module ValidatesLength
248
+ end # module Validations
249
+ end # module DataMapper
@@ -0,0 +1,64 @@
1
+ module DataMapper
2
+ module Validations
3
+ # @author Guy van den Berg
4
+ # @since 0.9
5
+ class MethodValidator < GenericValidator
6
+
7
+ def initialize(field_name, options={})
8
+ super
9
+ @options[:method] = @field_name unless @options.key?(:method)
10
+ end
11
+
12
+ def call(target)
13
+ result, message = target.__send__(@options[:method])
14
+ add_error(target, message, field_name) unless result
15
+ result
16
+ end
17
+
18
+ def ==(other)
19
+ @options[:method] == other.instance_variable_get(:@options)[:method] && super
20
+ end
21
+
22
+ end # class MethodValidator
23
+
24
+ module ValidatesWithMethod
25
+ # Validate using method called on validated object. The method must
26
+ # to return either true, or a pair of [false, error message string],
27
+ # and is specified as a symbol passed with :method option.
28
+ #
29
+ # This validator does support multiple fields being specified at a
30
+ # time, but we encourage you to use it with one property/method at a
31
+ # time.
32
+ #
33
+ # Real world experience shows that method validation is often useful
34
+ # when attribute needs to be virtual and not a property name.
35
+ #
36
+ # @example Usage
37
+ # require 'dm-validations'
38
+ #
39
+ # class Page
40
+ # include DataMapper::Resource
41
+ #
42
+ # property :zip_code, String
43
+ #
44
+ # validates_with_method :zip_code,
45
+ # :method => :in_the_right_location?
46
+ #
47
+ # def in_the_right_location?
48
+ # if @zip_code == "94301"
49
+ # return true
50
+ # else
51
+ # return [false, "You're in the wrong zip code"]
52
+ # end
53
+ # end
54
+ #
55
+ # # A call to valid? will return false and
56
+ # # populate the object's errors with "You're in the
57
+ # # wrong zip code" unless zip_code == "94301"
58
+ # end
59
+ def validates_with_method(*fields)
60
+ validators.add(MethodValidator, *fields)
61
+ end
62
+ end # module ValidatesWithMethod
63
+ end # module Validations
64
+ end # module DataMapper