dm-validations 0.9.11 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (159) hide show
  1. data/{History.txt → History.rdoc} +10 -3
  2. data/LICENSE +1 -0
  3. data/Manifest.txt +103 -33
  4. data/README.rdoc +172 -0
  5. data/Rakefile +4 -5
  6. data/lib/dm-validations.rb +73 -75
  7. data/lib/dm-validations/auto_validate.rb +98 -56
  8. data/lib/dm-validations/contextual_validators.rb +37 -14
  9. data/lib/dm-validations/exceptions.rb +3 -0
  10. data/lib/dm-validations/formats/url.rb +3 -1
  11. data/lib/dm-validations/support/context.rb +54 -0
  12. data/lib/dm-validations/validation_errors.rb +51 -27
  13. data/lib/dm-validations/{absent_field_validator.rb → validators/absent_field_validator.rb} +2 -8
  14. data/lib/dm-validations/{acceptance_validator.rb → validators/acceptance_validator.rb} +7 -7
  15. data/lib/dm-validations/{block_validator.rb → validators/block_validator.rb} +0 -0
  16. data/lib/dm-validations/{confirmation_validator.rb → validators/confirmation_validator.rb} +7 -7
  17. data/lib/dm-validations/{format_validator.rb → validators/format_validator.rb} +10 -16
  18. data/lib/dm-validations/{generic_validator.rb → validators/generic_validator.rb} +28 -6
  19. data/lib/dm-validations/validators/length_validator.rb +214 -0
  20. data/lib/dm-validations/validators/method_validator.rb +68 -0
  21. data/lib/dm-validations/validators/numeric_validator.rb +171 -0
  22. data/lib/dm-validations/{primitive_validator.rb → validators/primitive_validator.rb} +2 -7
  23. data/lib/dm-validations/{required_field_validator.rb → validators/required_field_validator.rb} +0 -6
  24. data/lib/dm-validations/{uniqueness_validator.rb → validators/uniqueness_validator.rb} +9 -20
  25. data/lib/dm-validations/{within_validator.rb → validators/within_validator.rb} +4 -4
  26. data/lib/dm-validations/version.rb +1 -1
  27. data/spec/fixtures/barcode.rb +32 -0
  28. data/spec/fixtures/basketball_court.rb +58 -0
  29. data/spec/fixtures/basketball_player.rb +37 -0
  30. data/spec/fixtures/beta_tester_account.rb +33 -0
  31. data/spec/fixtures/bill_of_landing.rb +42 -0
  32. data/spec/fixtures/boat_dock.rb +26 -0
  33. data/spec/fixtures/city.rb +25 -0
  34. data/spec/fixtures/company.rb +95 -0
  35. data/spec/fixtures/corporate_world.rb +47 -0
  36. data/spec/fixtures/country.rb +25 -0
  37. data/spec/fixtures/currency.rb +42 -0
  38. data/spec/fixtures/ethernet_frame.rb +56 -0
  39. data/spec/fixtures/event.rb +44 -0
  40. data/spec/fixtures/g3_concert.rb +57 -0
  41. data/spec/fixtures/jabberwock.rb +27 -0
  42. data/spec/fixtures/kayak.rb +28 -0
  43. data/spec/fixtures/lernean_hydra.rb +39 -0
  44. data/spec/fixtures/mathematical_function.rb +34 -0
  45. data/spec/fixtures/memory_object.rb +30 -0
  46. data/spec/fixtures/mittelschnauzer.rb +35 -0
  47. data/spec/fixtures/motor_launch.rb +21 -0
  48. data/spec/fixtures/page.rb +32 -0
  49. data/spec/fixtures/phone_number.rb +28 -0
  50. data/spec/fixtures/pirogue.rb +28 -0
  51. data/spec/fixtures/programming_language.rb +83 -0
  52. data/spec/fixtures/reservation.rb +38 -0
  53. data/spec/fixtures/scm_operation.rb +67 -0
  54. data/spec/fixtures/sms_message.rb +22 -0
  55. data/spec/fixtures/udp_packet.rb +49 -0
  56. data/spec/integration/absent_field_validator/absent_field_validator_spec.rb +86 -0
  57. data/spec/integration/absent_field_validator/spec_helper.rb +7 -0
  58. data/spec/integration/acceptance_validator/acceptance_validator_spec.rb +196 -0
  59. data/spec/integration/acceptance_validator/spec_helper.rb +7 -0
  60. data/spec/integration/automatic_validation/custom_messages_for_inferred_validation_spec.rb +47 -0
  61. data/spec/integration/automatic_validation/disabling_inferred_validation_spec.rb +41 -0
  62. data/spec/integration/automatic_validation/inferred_boolean_properties_validation_spec.rb +104 -0
  63. data/spec/integration/automatic_validation/inferred_float_property_validation_spec.rb +35 -0
  64. data/spec/integration/automatic_validation/inferred_format_validation_spec.rb +33 -0
  65. data/spec/integration/automatic_validation/inferred_integer_properties_validation_spec.rb +73 -0
  66. data/spec/integration/automatic_validation/inferred_length_validation_spec.rb +121 -0
  67. data/spec/integration/automatic_validation/inferred_presence_validation_spec.rb +41 -0
  68. data/spec/integration/automatic_validation/inferred_primitive_validation_spec.rb +20 -0
  69. data/spec/integration/automatic_validation/inferred_within_validation_spec.rb +33 -0
  70. data/spec/integration/automatic_validation/spec_helper.rb +74 -0
  71. data/spec/integration/block_validator/block_validator_spec.rb +32 -0
  72. data/spec/integration/block_validator/spec_helper.rb +5 -0
  73. data/spec/integration/conditional_validation/if_condition_spec.rb +61 -0
  74. data/spec/integration/conditional_validation/spec_helper.rb +5 -0
  75. data/spec/integration/confirmation_validator/confirmation_validator_spec.rb +74 -0
  76. data/spec/integration/confirmation_validator/spec_helper.rb +5 -0
  77. data/spec/integration/datamapper_models/association_validation_spec.rb +23 -0
  78. data/spec/integration/datamapper_models/inheritance_spec.rb +78 -0
  79. data/spec/integration/duplicated_validations/duplicated_validations_spec.rb +22 -0
  80. data/spec/integration/duplicated_validations/spec_helper.rb +5 -0
  81. data/spec/integration/format_validator/email_format_validator_spec.rb +113 -0
  82. data/spec/integration/format_validator/format_validator_spec.rb +60 -0
  83. data/spec/integration/format_validator/regexp_validator_spec.rb +29 -0
  84. data/spec/integration/format_validator/spec_helper.rb +5 -0
  85. data/spec/integration/format_validator/url_format_validator_spec.rb +63 -0
  86. data/spec/integration/length_validator/default_value_spec.rb +12 -0
  87. data/spec/integration/length_validator/equality_spec.rb +79 -0
  88. data/spec/integration/length_validator/error_message_spec.rb +15 -18
  89. data/spec/integration/length_validator/maximum_spec.rb +40 -24
  90. data/spec/integration/length_validator/minimum_spec.rb +32 -21
  91. data/spec/integration/length_validator/range_spec.rb +50 -60
  92. data/spec/integration/length_validator/spec_helper.rb +7 -12
  93. data/spec/integration/method_validator/method_validator_spec.rb +239 -0
  94. data/spec/integration/method_validator/spec_helper.rb +5 -0
  95. data/spec/integration/numeric_validator/equality_with_float_type_spec.rb +61 -0
  96. data/spec/integration/numeric_validator/equality_with_integer_type_spec.rb +37 -0
  97. data/spec/integration/numeric_validator/float_type_spec.rb +42 -56
  98. data/spec/integration/numeric_validator/gt_with_float_type_spec.rb +35 -0
  99. data/spec/integration/numeric_validator/gte_with_float_type_spec.rb +35 -0
  100. data/spec/integration/numeric_validator/integer_only_true_spec.rb +3 -6
  101. data/spec/integration/numeric_validator/integer_type_spec.rb +8 -24
  102. data/spec/integration/numeric_validator/lt_with_float_type_spec.rb +35 -0
  103. data/spec/integration/numeric_validator/lte_with_float_type_spec.rb +35 -0
  104. data/spec/integration/numeric_validator/spec_helper.rb +5 -77
  105. data/spec/integration/primitive_validator/primitive_validator_spec.rb +90 -0
  106. data/spec/integration/primitive_validator/spec_helper.rb +5 -0
  107. data/spec/integration/pure_ruby_objects/plain_old_ruby_object_validation_spec.rb +118 -0
  108. data/spec/integration/required_field_validator/association_spec.rb +5 -8
  109. data/spec/integration/required_field_validator/boolean_type_value_spec.rb +4 -7
  110. data/spec/integration/required_field_validator/date_type_value_spec.rb +3 -6
  111. data/spec/integration/required_field_validator/datetime_type_value_spec.rb +3 -6
  112. data/spec/integration/required_field_validator/float_type_value_spec.rb +3 -6
  113. data/spec/integration/required_field_validator/integer_type_value_spec.rb +2 -5
  114. data/spec/integration/required_field_validator/plain_old_ruby_object_spec.rb +5 -8
  115. data/spec/integration/required_field_validator/shared_examples.rb +1 -1
  116. data/spec/integration/required_field_validator/spec_helper.rb +6 -67
  117. data/spec/integration/required_field_validator/string_type_value_spec.rb +2 -5
  118. data/spec/integration/required_field_validator/text_type_value_spec.rb +2 -5
  119. data/spec/integration/shared/default_validation_context.rb +13 -0
  120. data/spec/integration/shared/valid_and_invalid_model.rb +27 -0
  121. data/spec/integration/uniqueness_validator/spec_helper.rb +5 -0
  122. data/spec/integration/uniqueness_validator/uniqueness_validator_spec.rb +114 -0
  123. data/spec/integration/within_validator/spec_helper.rb +5 -0
  124. data/spec/integration/within_validator/within_validator_spec.rb +164 -0
  125. data/spec/public/resource_spec.rb +68 -0
  126. data/spec/spec.opts +1 -0
  127. data/spec/spec_helper.rb +12 -4
  128. data/spec/unit/contextual_validators/emptiness_spec.rb +50 -0
  129. data/spec/unit/contextual_validators/execution_spec.rb +55 -0
  130. data/spec/unit/contextual_validators/spec_helper.rb +37 -0
  131. data/spec/unit/generic_validator/equality_operator_spec.rb +26 -0
  132. data/spec/unit/validation_errors/adding_spec.rb +54 -0
  133. data/spec/unit/validation_errors/emptiness_spec.rb +38 -0
  134. data/spec/unit/validation_errors/enumerable_spec.rb +32 -0
  135. data/tasks/install.rb +1 -1
  136. data/tasks/spec.rb +4 -4
  137. metadata +118 -55
  138. data/README.txt +0 -72
  139. data/lib/dm-validations/custom_validator.rb +0 -72
  140. data/lib/dm-validations/length_validator.rb +0 -113
  141. data/lib/dm-validations/method_validator.rb +0 -68
  142. data/lib/dm-validations/numeric_validator.rb +0 -83
  143. data/spec/integration/absent_field_validator_spec.rb +0 -36
  144. data/spec/integration/acceptance_validator_spec.rb +0 -87
  145. data/spec/integration/auto_validate_spec.rb +0 -342
  146. data/spec/integration/block_validator_spec.rb +0 -30
  147. data/spec/integration/confirmation_validator_spec.rb +0 -105
  148. data/spec/integration/contextual_validators_spec.rb +0 -27
  149. data/spec/integration/custom_validator_spec.rb +0 -9
  150. data/spec/integration/format_validator_spec.rb +0 -193
  151. data/spec/integration/generic_validator_spec.rb +0 -17
  152. data/spec/integration/length_validator/valid_objects_spec.rb +0 -13
  153. data/spec/integration/method_validator_spec.rb +0 -58
  154. data/spec/integration/numeric_validator_spec.rb +0 -253
  155. data/spec/integration/primitive_validator_spec.rb +0 -30
  156. data/spec/integration/uniqueness_validator_spec.rb +0 -97
  157. data/spec/integration/validation_errors_spec.rb +0 -18
  158. data/spec/integration/validation_spec.rb +0 -404
  159. data/spec/integration/within_validator_spec.rb +0 -79
@@ -6,16 +6,10 @@ module DataMapper
6
6
  # @author Guy van den Berg
7
7
  # @since 0.9
8
8
  class AbsentFieldValidator < GenericValidator
9
-
10
- def initialize(field_name, options={})
11
- super
12
- @field_name, @options = field_name, options
13
- end
14
-
15
9
  def call(target)
16
- return true if target.send(field_name).blank?
10
+ return true if target.send(self.field_name).blank?
17
11
 
18
- error_message = @options[:message] || ValidationErrors.default_error_message(:absent, field_name)
12
+ error_message = self.options[:message] || ValidationErrors.default_error_message(:absent, field_name)
19
13
  add_error(target, error_message, field_name)
20
14
 
21
15
  return false
@@ -9,10 +9,10 @@ module DataMapper
9
9
 
10
10
  def initialize(field_name, options = {})
11
11
  super
12
- @options = options
13
- @field_name = field_name
14
- @options[:allow_nil] = true unless @options.include?(:allow_nil)
15
- @options[:accept] ||= ["1", 1, "true", true, "t"]
12
+
13
+ # ||= true makes value true if it used to be false
14
+ @options[:allow_nil] = true unless(options.include?(:allow_nil) && [false, nil, "false", "f"].include?(options[:allow_nil]))
15
+ @options[:accept] ||= ["1", 1, "true", true, "t"]
16
16
  @options[:accept] = Array(@options[:accept])
17
17
  end
18
18
 
@@ -27,9 +27,9 @@ module DataMapper
27
27
  end
28
28
 
29
29
  def valid?(target)
30
- field_value = target.send(field_name)
31
- return true if @options[:allow_nil] && field_value.nil?
32
- return false if !@options[:allow_nil] && field_value.nil?
30
+ field_value = target.validation_property_value(field_name)
31
+ # Allow empty values
32
+ return true if @options[:allow_nil] && field_value.blank?
33
33
 
34
34
  @options[:accept].include?(field_value)
35
35
  end
@@ -1,3 +1,4 @@
1
+ # -*- coding: utf-8 -*-
1
2
  module DataMapper
2
3
  module Validate
3
4
 
@@ -9,8 +10,7 @@ module DataMapper
9
10
 
10
11
  def initialize(field_name, options = {})
11
12
  super
12
- @options = options
13
- @field_name, @confirm_field_name = field_name, (options[:confirm] || "#{field_name}_confirmation").to_sym
13
+ @confirm_field_name = (options[:confirm] || "#{field_name}_confirmation").to_sym
14
14
  @options[:allow_nil] = true unless @options.has_key?(:allow_nil)
15
15
  end
16
16
 
@@ -26,10 +26,10 @@ module DataMapper
26
26
 
27
27
  def valid?(target)
28
28
  field_value = target.send(field_name)
29
- return true if @options[:allow_nil] && field_value.nil?
30
- return false if !@options[:allow_nil] && field_value.nil?
29
+ return true if @options[:allow_nil] && field_value.blank?
30
+ return false if !@options[:allow_nil] && field_value.blank?
31
31
 
32
- if target.class.properties.has_property?(field_name)
32
+ if target.model.properties.named?(field_name)
33
33
  return true unless target.attribute_dirty?(field_name)
34
34
  end
35
35
 
@@ -44,8 +44,8 @@ module DataMapper
44
44
  ##
45
45
  # Validates that the given attribute is confirmed by another attribute.
46
46
  # A common use case scenario is when you require a user to confirm their
47
- # password, for which you use both password and password_confirmation
48
- # attributes.
47
+ # password, for which you use both password and password_confirmation
48
+ # attributes.
49
49
  #
50
50
  # @option :allow_nil<Boolean> true/false (default is true)
51
51
  # @option :confirm<Symbol> the attribute that you want to validate
@@ -1,8 +1,8 @@
1
1
  #require File.dirname(__FILE__) + '/formats/email'
2
2
 
3
3
  require 'pathname'
4
- require Pathname(__FILE__).dirname.expand_path + 'formats/email'
5
- require Pathname(__FILE__).dirname.expand_path + 'formats/url'
4
+ require Pathname(__FILE__).dirname.expand_path + ".." + 'formats/email'
5
+ require Pathname(__FILE__).dirname.expand_path + ".." + 'formats/url'
6
6
 
7
7
  module DataMapper
8
8
  module Validate
@@ -17,15 +17,15 @@ module DataMapper
17
17
  include DataMapper::Validate::Format::Email
18
18
  include DataMapper::Validate::Format::Url
19
19
 
20
- def initialize(field_name, options = {}, &b)
21
- super(field_name, options)
22
- @field_name, @options = field_name, options
23
- @options[:allow_nil] = false unless @options.has_key?(:allow_nil)
20
+ def initialize(field_name, options = {})
21
+ super
22
+
23
+ @options[:allow_nil] = true unless @options.include?(:allow_nil)
24
24
  end
25
25
 
26
26
  def call(target)
27
27
  value = target.validation_property_value(field_name)
28
- return true if @options[:allow_nil] && value.nil?
28
+ return true if @options[:allow_nil] && value.blank?
29
29
 
30
30
  validation = @options[:as] || @options[:with]
31
31
 
@@ -34,7 +34,7 @@ module DataMapper
34
34
 
35
35
  valid = case validator
36
36
  when Proc then validator.call(value)
37
- when Regexp then value =~ validator
37
+ when Regexp then (value.is_a?(Fixnum) ? value.to_s : value) =~ validator
38
38
  else
39
39
  raise UnknownValidationFormat, "Can't determine how to validate #{target.class}##{field_name} with #{validator.inspect}"
40
40
  end
@@ -42,19 +42,13 @@ module DataMapper
42
42
  return true if valid
43
43
 
44
44
  error_message = @options[:message] || ValidationErrors.default_error_message(:invalid, field_name)
45
-
46
- field = Extlib::Inflection.humanize(field_name)
47
- error_message = error_message.call(field, value) if error_message.respond_to?(:call)
48
-
49
- add_error(target, error_message, field_name)
45
+ add_error(target, error_message.try_call(humanized_field_name, value), field_name)
50
46
 
51
47
  false
52
48
  end
53
-
54
- #class UnknownValidationFormat < StandardError; end
55
-
56
49
  end # class FormatValidator
57
50
 
51
+
58
52
  module ValidatesFormat
59
53
 
60
54
  ##
@@ -15,7 +15,7 @@ module DataMapper
15
15
  class GenericValidator
16
16
 
17
17
  attr_accessor :if_clause, :unless_clause
18
- attr_reader :field_name
18
+ attr_reader :field_name, :options, :humanized_field_name
19
19
 
20
20
  # Construct a validator. Capture the :if and :unless clauses when present.
21
21
  #
@@ -28,9 +28,12 @@ module DataMapper
28
28
  # All additional key/value pairs are passed through to the validator
29
29
  # that is sub-classing this GenericValidator
30
30
  #
31
- def initialize(field, opts = {})
32
- @if_clause = opts.delete(:if)
33
- @unless_clause = opts.delete(:unless)
31
+ def initialize(field_name, options = {})
32
+ @if_clause = options.delete(:if)
33
+ @unless_clause = options.delete(:unless)
34
+
35
+ @field_name, @options = field_name, options
36
+ @humanized_field_name = Extlib::Inflection.humanize(@field_name)
34
37
  end
35
38
 
36
39
  # Add an error message to a target resource. If the error corresponds to a
@@ -83,15 +86,34 @@ module DataMapper
83
86
  true
84
87
  end
85
88
 
89
+ # Returns true if validators are equal
90
+ #
91
+ # Note that this intentionally do
92
+ # validate options equality
93
+ #
94
+ # even though it is hard to imagine a situation
95
+ # when multiple validations will be used
96
+ # on the same field with the same conditions
97
+ # but different options,
98
+ # it happens to be the case every once in a while
99
+ # with inferred validations for strings/text and
100
+ # explicitly given validations with different option
101
+ # (usually as Range vs. max limit for inferred validation)
102
+ #
103
+ # @semipublic
86
104
  def ==(other)
87
105
  self.class == other.class &&
88
106
  self.field_name == other.field_name &&
89
- self.class == other.class &&
90
107
  self.if_clause == other.if_clause &&
91
108
  self.unless_clause == other.unless_clause &&
92
109
  self.instance_variable_get(:@options) == other.instance_variable_get(:@options)
93
110
  end
94
111
 
112
+ def inspect
113
+ "<##{self.class.name} @field_name='#{@field_name}' @if_clause=#{@if_clause.inspect} @unless_clause=#{@unless_clause.inspect} @options=#{@options.inspect}>"
114
+ end
115
+
116
+ alias to_s inspect
95
117
  end # class GenericValidator
96
118
  end # module Validate
97
- end # module DataMapper
119
+ end # module DataMapper
@@ -0,0 +1,214 @@
1
+ module DataMapper
2
+ module Validate
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
+ # @param [Hash] options
10
+ # the validator options
11
+ #
12
+ # @return [undefined]
13
+ #
14
+ # @api semipublic
15
+ def initialize(field_name, options)
16
+ super
17
+
18
+ @equal = options[:is] || options[:equals]
19
+ @range = options[:within] || options[:in]
20
+ @min = options[:minimum] || options[:min]
21
+ @max = options[:maximum] || options[:max]
22
+
23
+ if @min && @max
24
+ @range ||= @min..@max
25
+ end
26
+ end
27
+
28
+ # Test the resource field for validity
29
+ #
30
+ # @example when the resource field is valid
31
+ # validator.call(valid_resource) # => true
32
+ #
33
+ # @example when the resource field is not valid
34
+ # validator.call(invalid_resource) # => false
35
+ #
36
+ #
37
+ # @param [Resource] target
38
+ # the Resource to test
39
+ #
40
+ # @return [Boolean]
41
+ # true if the field is valid, false if not
42
+ #
43
+ # @api semipublic
44
+ def call(target)
45
+ value = target.validation_property_value(field_name)
46
+ return true if allow_nil? && value.blank?
47
+
48
+ return true unless error_message = error_message_for(value)
49
+
50
+ add_error(target, error_message, field_name)
51
+
52
+ false
53
+ end
54
+
55
+ private
56
+
57
+ # Return if the validation allows nil values
58
+ #
59
+ # @return [Boolean]
60
+ # true if the validation allows nil, false if not
61
+ #
62
+ # @api private
63
+ def allow_nil?
64
+ options.fetch(:allow_nil, false)
65
+ end
66
+
67
+ # Return the error messages for the value if it is invalid
68
+ #
69
+ # @param [#to_s] value
70
+ # the value to test
71
+ #
72
+ # @return [String, nil]
73
+ # the error message if invalid, nil if not
74
+ #
75
+ # @api private
76
+ def error_message_for(value)
77
+ if error_message = send(validation_method, value_length(value.to_s))
78
+ @options.fetch(:message, error_message)
79
+ end
80
+ end
81
+
82
+ # Return the method to validate the value with
83
+ #
84
+ # @return [Symbol]
85
+ # the validation method
86
+ #
87
+ # @api private
88
+ def validation_method
89
+ @validation_method ||=
90
+ if @equal then :validate_equals
91
+ elsif @range then :validate_range
92
+ elsif @min then :validate_min
93
+ elsif @max then :validate_max
94
+ end
95
+ end
96
+
97
+ # Return the length in characters
98
+ #
99
+ # @param [#to_str] value
100
+ # the string to get the number of characters for
101
+ #
102
+ # @return [Integer]
103
+ # the number of characters in the string
104
+ #
105
+ # @api private
106
+ def value_length(value)
107
+ value.to_str.split(//).size
108
+ end
109
+
110
+ # Validate the value length is equal to the expected length
111
+ #
112
+ # @param [Integer] length
113
+ # the value length
114
+ #
115
+ # @return [String, nil]
116
+ # the error message if invalid, nil if not
117
+ #
118
+ # @api private
119
+ def validate_equals(length)
120
+ return if length == @equal
121
+ ValidationErrors.default_error_message(:wrong_length, humanized_field_name, @equal)
122
+ end
123
+
124
+ # Validate the value length is within expected range
125
+ #
126
+ # @param [Integer] length
127
+ # the value length
128
+ #
129
+ # @return [String, nil]
130
+ # the error message if invalid, nil if not
131
+ #
132
+ # @api private
133
+ def validate_range(length)
134
+ return if @range.include?(length)
135
+ ValidationErrors.default_error_message(:length_between, humanized_field_name, @range.min, @range.max)
136
+ end
137
+
138
+ # Validate the minimum expected value length
139
+ #
140
+ # @param [Integer] length
141
+ # the value length
142
+ #
143
+ # @return [String, nil]
144
+ # the error message if invalid, nil if not
145
+ #
146
+ # @api private
147
+ def validate_min(length)
148
+ return if length >= @min
149
+ ValidationErrors.default_error_message(:too_short, humanized_field_name, @min)
150
+ end
151
+
152
+ # Validate the maximum expected value length
153
+ #
154
+ # @param [Integer] length
155
+ # the value length
156
+ #
157
+ # @return [String, nil]
158
+ # the error message if invalid, nil if not
159
+ #
160
+ # @api private
161
+ def validate_max(length)
162
+ return if length <= @max
163
+ ValidationErrors.default_error_message(:too_long, humanized_field_name, @max)
164
+ end
165
+
166
+ end # class LengthValidator
167
+
168
+ module ValidatesLength
169
+
170
+ # Validates that the length of the attribute is equal to, less than,
171
+ # greater than or within a certain range (depending upon the options
172
+ # you specify).
173
+ #
174
+ # @option :allow_nil<Boolean> true/false (default is true)
175
+ # @option :minimum ensures that the attribute's length is greater than
176
+ # or equal to the supplied value
177
+ # @option :min alias for :minimum
178
+ # @option :maximum ensures the attribute's length is less than or equal
179
+ # to the supplied value
180
+ # @option :max alias for :maximum
181
+ # @option :equals ensures the attribute's length is equal to the
182
+ # supplied value
183
+ # @option :is alias for :equals
184
+ # @option :in<Range> given a Range, ensures that the attributes length is
185
+ # include?'ed in the Range
186
+ # @option :within<Range> alias for :in
187
+ #
188
+ # @example [Usage]
189
+ # require 'dm-validations'
190
+ #
191
+ # class Page
192
+ # include DataMapper::Resource
193
+ #
194
+ # property high, Integer
195
+ # property low, Integer
196
+ # property just_right, Integer
197
+ #
198
+ # validates_length :high, :min => 100000000000
199
+ # validates_length :low, :equals => 0
200
+ # validates_length :just_right, :within => 1..10
201
+ #
202
+ # # a call to valid? will return false unless:
203
+ # # high is greater than or equal to 100000000000
204
+ # # low is equal to 0
205
+ # # just_right is between 1 and 10 (inclusive of both 1 and 10)
206
+ #
207
+ def validates_length(*fields)
208
+ opts = opts_from_validator_args(fields)
209
+ add_validator_to_context(opts, fields, DataMapper::Validate::LengthValidator)
210
+ end
211
+
212
+ end # module ValidatesLength
213
+ end # module Validate
214
+ end # module DataMapper
@@ -0,0 +1,68 @@
1
+ module DataMapper
2
+ module Validate
3
+
4
+ ##
5
+ #
6
+ # @author Guy van den Berg
7
+ # @since 0.9
8
+ class MethodValidator < GenericValidator
9
+
10
+ def initialize(field_name, options={})
11
+ super
12
+ @options[:method] = @field_name unless @options.has_key?(:method)
13
+ end
14
+
15
+ def call(target)
16
+ result, message = target.send(@options[:method])
17
+ add_error(target, message, field_name) unless result
18
+ result
19
+ end
20
+
21
+ def ==(other)
22
+ @options[:method] == other.instance_variable_get(:@options)[:method] && super
23
+ end
24
+ end # class MethodValidator
25
+
26
+ module ValidatesWithMethod
27
+
28
+ ##
29
+ # Validate using method called on validated object. The method must to return
30
+ # either true, or a pair of [false, error message string], and is specified
31
+ # as a symbol passed with :method option.
32
+ #
33
+ # This validator does support multiple fields being specified at a time,
34
+ # but we encourage you to use it with one property/method at a time.
35
+ #
36
+ # Real world experience shows that method validation is often useful when
37
+ # attribute needs to be virtual and not a property name.
38
+ #
39
+ # @example [Usage]
40
+ # require 'dm-validations'
41
+ #
42
+ # class Page
43
+ # include DataMapper::Resource
44
+ #
45
+ # property :zip_code, String
46
+ #
47
+ # validates_with_method :zip_code, :method => :in_the_right_location?
48
+ #
49
+ # def in_the_right_location?
50
+ # if @zip_code == "94301"
51
+ # return true
52
+ # else
53
+ # return [false, "You're in the wrong zip code"]
54
+ # end
55
+ # end
56
+ #
57
+ # # A call to valid? will return false and
58
+ # # populate the object's errors with "You're in the
59
+ # # wrong zip code" unless zip_code == "94301"
60
+ # end
61
+ def validates_with_method(*fields)
62
+ opts = opts_from_validator_args(fields)
63
+ add_validator_to_context(opts, fields, DataMapper::Validate::MethodValidator)
64
+ end
65
+
66
+ end # module ValidatesWithMethod
67
+ end # module Validate
68
+ end # module DataMapper