dm-validations 0.9.11 → 0.10.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 (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