dm-validations 0.10.1 → 0.10.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. data/Rakefile +23 -18
  2. data/VERSION +1 -0
  3. data/dm-validations.gemspec +188 -0
  4. data/lib/dm-validations.rb +25 -75
  5. data/lib/dm-validations/auto_validate.rb +7 -7
  6. data/lib/dm-validations/contextual_validators.rb +4 -18
  7. data/lib/dm-validations/exceptions.rb +3 -1
  8. data/lib/dm-validations/support/context.rb +40 -1
  9. data/lib/dm-validations/validation_errors.rb +2 -13
  10. data/lib/dm-validations/validators/absent_field_validator.rb +2 -2
  11. data/lib/dm-validations/validators/acceptance_validator.rb +14 -16
  12. data/lib/dm-validations/validators/confirmation_validator.rb +17 -15
  13. data/lib/dm-validations/validators/format_validator.rb +18 -11
  14. data/lib/dm-validations/validators/generic_validator.rb +45 -15
  15. data/lib/dm-validations/validators/length_validator.rb +2 -11
  16. data/lib/dm-validations/validators/method_validator.rb +2 -2
  17. data/lib/dm-validations/validators/numeric_validator.rb +8 -6
  18. data/lib/dm-validations/validators/primitive_validator.rb +1 -1
  19. data/lib/dm-validations/validators/uniqueness_validator.rb +13 -9
  20. data/lib/dm-validations/validators/within_validator.rb +4 -3
  21. data/spec/fixtures/basketball_player.rb +0 -3
  22. data/spec/fixtures/bill_of_landing.rb +1 -1
  23. data/spec/fixtures/city.rb +0 -1
  24. data/spec/fixtures/company.rb +1 -3
  25. data/spec/fixtures/corporate_world.rb +0 -6
  26. data/spec/fixtures/country.rb +0 -1
  27. data/spec/fixtures/event.rb +1 -1
  28. data/spec/fixtures/page.rb +2 -2
  29. data/spec/fixtures/reservation.rb +1 -1
  30. data/spec/fixtures/scm_operation.rb +0 -8
  31. data/spec/integration/absent_field_validator/absent_field_validator_spec.rb +6 -2
  32. data/spec/integration/acceptance_validator/acceptance_validator_spec.rb +36 -36
  33. data/spec/integration/automatic_validation/custom_messages_for_inferred_validation_spec.rb +2 -2
  34. data/spec/integration/automatic_validation/disabling_inferred_validation_spec.rb +4 -4
  35. data/spec/integration/automatic_validation/inferred_boolean_properties_validation_spec.rb +2 -2
  36. data/spec/integration/automatic_validation/inferred_float_property_validation_spec.rb +2 -0
  37. data/spec/integration/automatic_validation/inferred_format_validation_spec.rb +3 -1
  38. data/spec/integration/automatic_validation/inferred_integer_properties_validation_spec.rb +2 -0
  39. data/spec/integration/automatic_validation/inferred_length_validation_spec.rb +6 -2
  40. data/spec/integration/automatic_validation/inferred_presence_validation_spec.rb +8 -4
  41. data/spec/integration/automatic_validation/inferred_primitive_validation_spec.rb +3 -1
  42. data/spec/integration/automatic_validation/spec_helper.rb +6 -6
  43. data/spec/integration/block_validator/block_validator_spec.rb +1 -1
  44. data/spec/integration/conditional_validation/if_condition_spec.rb +3 -1
  45. data/spec/integration/confirmation_validator/confirmation_validator_spec.rb +3 -1
  46. data/spec/integration/datamapper_models/association_validation_spec.rb +4 -1
  47. data/spec/integration/datamapper_models/inheritance_spec.rb +6 -2
  48. data/spec/integration/duplicated_validations/duplicated_validations_spec.rb +3 -1
  49. data/spec/integration/format_validator/email_format_validator_spec.rb +5 -1
  50. data/spec/integration/format_validator/format_validator_spec.rb +5 -1
  51. data/spec/integration/format_validator/regexp_validator_spec.rb +5 -1
  52. data/spec/integration/format_validator/url_format_validator_spec.rb +8 -4
  53. data/spec/integration/length_validator/default_value_spec.rb +4 -2
  54. data/spec/integration/length_validator/equality_spec.rb +3 -1
  55. data/spec/integration/length_validator/error_message_spec.rb +3 -1
  56. data/spec/integration/length_validator/maximum_spec.rb +3 -1
  57. data/spec/integration/length_validator/minimum_spec.rb +3 -1
  58. data/spec/integration/length_validator/range_spec.rb +3 -1
  59. data/spec/integration/method_validator/method_validator_spec.rb +3 -1
  60. data/spec/integration/numeric_validator/equality_with_float_type_spec.rb +5 -1
  61. data/spec/integration/numeric_validator/equality_with_integer_type_spec.rb +5 -1
  62. data/spec/integration/numeric_validator/float_type_spec.rb +3 -1
  63. data/spec/integration/numeric_validator/gt_with_float_type_spec.rb +3 -1
  64. data/spec/integration/numeric_validator/gte_with_float_type_spec.rb +3 -1
  65. data/spec/integration/numeric_validator/integer_only_true_spec.rb +11 -9
  66. data/spec/integration/numeric_validator/integer_type_spec.rb +10 -8
  67. data/spec/integration/numeric_validator/lt_with_float_type_spec.rb +3 -1
  68. data/spec/integration/numeric_validator/lte_with_float_type_spec.rb +3 -1
  69. data/spec/integration/primitive_validator/primitive_validator_spec.rb +3 -1
  70. data/spec/integration/pure_ruby_objects/plain_old_ruby_object_validation_spec.rb +6 -6
  71. data/spec/integration/required_field_validator/association_spec.rb +9 -4
  72. data/spec/integration/required_field_validator/boolean_type_value_spec.rb +24 -18
  73. data/spec/integration/required_field_validator/date_type_value_spec.rb +21 -19
  74. data/spec/integration/required_field_validator/datetime_type_value_spec.rb +21 -19
  75. data/spec/integration/required_field_validator/float_type_value_spec.rb +21 -19
  76. data/spec/integration/required_field_validator/integer_type_value_spec.rb +17 -15
  77. data/spec/integration/required_field_validator/shared_examples.rb +5 -3
  78. data/spec/integration/required_field_validator/string_type_value_spec.rb +20 -16
  79. data/spec/integration/required_field_validator/text_type_value_spec.rb +10 -6
  80. data/spec/integration/uniqueness_validator/uniqueness_validator_spec.rb +23 -25
  81. data/spec/integration/within_validator/within_validator_spec.rb +7 -3
  82. data/spec/public/resource_spec.rb +5 -6
  83. data/spec/rcov.opts +6 -0
  84. data/spec/spec.opts +2 -0
  85. data/spec/unit/contextual_validators/emptiness_spec.rb +1 -1
  86. data/spec/unit/contextual_validators/execution_spec.rb +9 -16
  87. data/spec/unit/generic_validator/equality_operator_spec.rb +3 -3
  88. data/spec/unit/validation_errors/adding_spec.rb +1 -1
  89. data/spec/unit/validation_errors/emptiness_spec.rb +1 -1
  90. data/spec/unit/validation_errors/enumerable_spec.rb +1 -1
  91. data/tasks/ci.rake +1 -0
  92. data/tasks/metrics.rake +36 -0
  93. data/tasks/spec.rake +41 -0
  94. data/tasks/yard.rake +9 -0
  95. data/tasks/yardstick.rake +19 -0
  96. metadata +36 -18
  97. data/History.rdoc +0 -45
  98. data/Manifest.txt +0 -139
  99. data/TODO +0 -16
  100. data/lib/dm-validations/version.rb +0 -5
  101. data/tasks/install.rb +0 -13
  102. data/tasks/spec.rb +0 -25
@@ -1,3 +1,5 @@
1
1
  module DataMapper
2
- class ValidationError < StandardError; end
2
+ class ValidationError < StandardError; end
3
+
4
+ class InvalidContextError < StandardError; end
3
5
  end
@@ -24,6 +24,7 @@ module DataMapper
24
24
  #
25
25
  # @api private
26
26
  def validation_context(context = default_validation_context)
27
+ assert_valid_context(context)
27
28
  validation_context_stack << context
28
29
  begin
29
30
  yield
@@ -43,9 +44,47 @@ module DataMapper
43
44
  # Returns the current validation context or nil if none has been pushed
44
45
  # @api private
45
46
  def current_validation_context
46
- validation_context_stack.last
47
+ context = validation_context_stack.last
48
+ valid_context?(context) ? context : :default
47
49
  end
48
50
 
51
+ # Return the contexts for the model
52
+ #
53
+ # @return [Hash]
54
+ # the hash of contexts for the model
55
+ #
56
+ # @api private
57
+ def contexts
58
+ model.validators.contexts
59
+ end
60
+
61
+ # Test if the context is valid for the model
62
+ #
63
+ # @param [Symbol] context
64
+ # the context to test
65
+ #
66
+ # @return [Boolean]
67
+ # true if the context is valid for the model
68
+ #
69
+ # @api private
70
+ def valid_context?(context)
71
+ contexts.empty? || contexts.key?(context)
72
+ end
73
+
74
+ # Assert that the context is valid for this model
75
+ #
76
+ # @param [Symbol] context
77
+ # the context to test
78
+ #
79
+ # @raise [InvalidContextError]
80
+ # raised if the context is not valid for this model
81
+ #
82
+ # @api private
83
+ def assert_valid_context(context)
84
+ unless valid_context?(context)
85
+ raise InvalidContextError, "#{context} is an invalid context, known contexts are #{contexts.keys.inspect}"
86
+ end
87
+ end
49
88
  end
50
89
 
51
90
  include Context
@@ -48,7 +48,7 @@ module DataMapper
48
48
 
49
49
  def initialize(resource)
50
50
  @resource = resource
51
- @errors = Dictionary.new
51
+ @errors = Dictionary.new { |h,k| h[k] = [] }
52
52
  end
53
53
 
54
54
  # Clear existing validation errors.
@@ -84,17 +84,6 @@ module DataMapper
84
84
  end
85
85
  end
86
86
 
87
- # Return validation errors for a particular field name or an empty array
88
- #
89
- # This method is a necessary requirement for active_model compatibility.
90
- #
91
- # @param [Symbol] field_name the name of the field you want an error for
92
- # @return [Array<Array<String>>]
93
- # array of validation errors or empty array, if there are no errors on given field
94
- def [](field_name)
95
- errors[field_name] ||= []
96
- end
97
-
98
87
  # Return validation errors for a particular field_name.
99
88
  #
100
89
  # @param [Symbol] field_name the name of the field you want an error for
@@ -113,7 +102,7 @@ module DataMapper
113
102
  end
114
103
 
115
104
  def empty?
116
- @errors.empty?
105
+ @errors.all? { |property_name, errors| errors.empty? }
117
106
  end
118
107
 
119
108
  def method_missing(meth, *args, &block)
@@ -7,12 +7,12 @@ module DataMapper
7
7
  # @since 0.9
8
8
  class AbsentFieldValidator < GenericValidator
9
9
  def call(target)
10
- return true if target.send(self.field_name).blank?
10
+ return true if target.validation_property_value(field_name).blank?
11
11
 
12
12
  error_message = self.options[:message] || ValidationErrors.default_error_message(:absent, field_name)
13
13
  add_error(target, error_message, field_name)
14
14
 
15
- return false
15
+ false
16
16
  end
17
17
  end # class AbsentFieldValidator
18
18
 
@@ -10,30 +10,28 @@ module DataMapper
10
10
  def initialize(field_name, options = {})
11
11
  super
12
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"]
13
+ @options[:allow_nil] = true unless @options.key?(:allow_nil)
14
+
15
+ @options[:accept] ||= [ '1', 1, 'true', true, 't' ]
16
16
  @options[:accept] = Array(@options[:accept])
17
17
  end
18
18
 
19
19
  def call(target)
20
- unless valid?(target)
21
- error_message = @options[:message] || ValidationErrors.default_error_message(:accepted, field_name)
22
- add_error(target, error_message, field_name)
23
- return false
24
- end
20
+ return true if valid?(target)
21
+
22
+ error_message = @options[:message] || ValidationErrors.default_error_message(:accepted, field_name)
23
+ add_error(target, error_message, field_name)
25
24
 
26
- return true
25
+ false
27
26
  end
28
27
 
29
- def valid?(target)
30
- field_value = target.validation_property_value(field_name)
31
- # Allow empty values
32
- return true if @options[:allow_nil] && field_value.blank?
28
+ private
33
29
 
34
- @options[:accept].include?(field_value)
30
+ def valid?(target)
31
+ value = target.validation_property_value(field_name)
32
+ return true if allow_nil?(value)
33
+ @options[:accept].include?(value)
35
34
  end
36
-
37
35
  end # class AcceptanceValidator
38
36
 
39
37
  module ValidatesIsAccepted
@@ -41,7 +39,7 @@ module DataMapper
41
39
  ##
42
40
  # Validates that the attributes's value is in the set of accepted values.
43
41
  #
44
- # @option :allow_nil<Boolean> true if nil is allowed, false if nil is not
42
+ # @option :allow_nil<Boolean> true if nil is allowed, false if not
45
43
  # allowed. Default is true.
46
44
  # @option :accept<Array> a list of accepted values.
47
45
  # Default are ["1", 1, "true", true, "t"]).
@@ -10,33 +10,34 @@ module DataMapper
10
10
 
11
11
  def initialize(field_name, options = {})
12
12
  super
13
+
14
+ set_optional_by_default
15
+
13
16
  @confirm_field_name = (options[:confirm] || "#{field_name}_confirmation").to_sym
14
- @options[:allow_nil] = true unless @options.has_key?(:allow_nil)
15
17
  end
16
18
 
17
19
  def call(target)
18
- unless valid?(target)
19
- error_message = @options[:message] || ValidationErrors.default_error_message(:confirmation, field_name)
20
- add_error(target, error_message, field_name)
21
- return false
22
- end
20
+ return true if valid?(target)
23
21
 
24
- return true
22
+ error_message = @options[:message] || ValidationErrors.default_error_message(:confirmation, field_name)
23
+ add_error(target, error_message, field_name)
24
+
25
+ false
25
26
  end
26
27
 
28
+ private
29
+
27
30
  def valid?(target)
28
- field_value = target.send(field_name)
29
- return true if @options[:allow_nil] && field_value.blank?
30
- return false if !@options[:allow_nil] && field_value.blank?
31
+ value = target.validation_property_value(field_name)
32
+ return true if optional?(value)
31
33
 
32
34
  if target.model.properties.named?(field_name)
33
35
  return true unless target.attribute_dirty?(field_name)
34
36
  end
35
37
 
36
38
  confirm_value = target.instance_variable_get("@#{@confirm_field_name}")
37
- field_value == confirm_value
39
+ value == confirm_value
38
40
  end
39
-
40
41
  end # class ConfirmationValidator
41
42
 
42
43
  module ValidatesIsConfirmed
@@ -47,9 +48,10 @@ module DataMapper
47
48
  # password, for which you use both password and password_confirmation
48
49
  # attributes.
49
50
  #
50
- # @option :allow_nil<Boolean> true/false (default is true)
51
- # @option :confirm<Symbol> the attribute that you want to validate
52
- # against (default is firstattr_confirmation)
51
+ # @option :allow_nil<Boolean> true/false (default is true)
52
+ # @option :allow_blank<Boolean> true/false (default is true)
53
+ # @option :confirm<Symbol> the attribute that you want to validate
54
+ # against (default is firstattr_confirmation)
53
55
  #
54
56
  # @example [Usage]
55
57
  # require 'dm-validations'
@@ -20,31 +20,37 @@ module DataMapper
20
20
  def initialize(field_name, options = {})
21
21
  super
22
22
 
23
- @options[:allow_nil] = true unless @options.include?(:allow_nil)
23
+ set_optional_by_default
24
24
  end
25
25
 
26
26
  def call(target)
27
+ return true if valid?(target)
28
+
29
+ value = target.validation_property_value(field_name)
30
+
31
+ error_message = @options[:message] || ValidationErrors.default_error_message(:invalid, field_name)
32
+ add_error(target, error_message.try_call(humanized_field_name, value), field_name)
33
+
34
+ false
35
+ end
36
+
37
+ private
38
+
39
+ def valid?(target)
27
40
  value = target.validation_property_value(field_name)
28
- return true if @options[:allow_nil] && value.blank?
41
+ return true if optional?(value)
29
42
 
30
43
  validation = @options[:as] || @options[:with]
31
44
 
32
45
  raise "No such predefined format '#{validation}'" if validation.is_a?(Symbol) && !FORMATS.has_key?(validation)
33
46
  validator = validation.is_a?(Symbol) ? FORMATS[validation][0] : validation
34
47
 
35
- valid = case validator
48
+ case validator
36
49
  when Proc then validator.call(value)
37
- when Regexp then (value.is_a?(Fixnum) ? value.to_s : value) =~ validator
50
+ when Regexp then (value.kind_of?(Numeric) ? value.to_s : value) =~ validator
38
51
  else
39
52
  raise UnknownValidationFormat, "Can't determine how to validate #{target.class}##{field_name} with #{validator.inspect}"
40
53
  end
41
-
42
- return true if valid
43
-
44
- error_message = @options[:message] || ValidationErrors.default_error_message(:invalid, field_name)
45
- add_error(target, error_message.try_call(humanized_field_name, value), field_name)
46
-
47
- false
48
54
  end
49
55
  end # class FormatValidator
50
56
 
@@ -58,6 +64,7 @@ module DataMapper
58
64
  # via a Proc or Regexp passed to the the :as or :with options.
59
65
  #
60
66
  # @option :allow_nil<Boolean> true/false (default is true)
67
+ # @option :allow_blank<Boolean> true/false (default is true)
61
68
  # @option :as<Format, Proc, Regexp> the pre-defined format, Proc or Regexp to validate against
62
69
  # @option :with<Format, Proc, Regexp> an alias for :as
63
70
  #
@@ -29,10 +29,10 @@ module DataMapper
29
29
  # that is sub-classing this GenericValidator
30
30
  #
31
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
32
+ @field_name = field_name
33
+ @options = options.except(:if, :unless)
34
+ @if_clause = options[:if]
35
+ @unless_clause = options[:unless]
36
36
  @humanized_field_name = Extlib::Inflection.humanize(@field_name)
37
37
  end
38
38
 
@@ -57,7 +57,7 @@ module DataMapper
57
57
  # against
58
58
  # @return <Boolean> true if valid, otherwise false
59
59
  def call(target)
60
- raise NotImplementedError, "DataMapper::Validate::GenericValidator::call must be overriden in a subclass"
60
+ raise NotImplementedError, "#{self.class}#call must be implemented"
61
61
  end
62
62
 
63
63
  # Determines if this validator should be run against the
@@ -68,24 +68,54 @@ module DataMapper
68
68
  # @return <Boolean> true if should be run, otherwise false
69
69
  def execute?(target)
70
70
  if unless_clause = self.unless_clause
71
- if unless_clause.is_a?(Symbol)
72
- return false if target.send(unless_clause)
73
- elsif unless_clause.respond_to?(:call)
74
- return false if unless_clause.call(target)
75
- end
71
+ return !target.__send__(unless_clause) if unless_clause.kind_of?(Symbol)
72
+ return !unless_clause.call(target) if unless_clause.respond_to?(:call)
76
73
  end
77
74
 
78
75
  if if_clause = self.if_clause
79
- if if_clause.is_a?(Symbol)
80
- return target.send(if_clause)
81
- elsif if_clause.respond_to?(:call)
82
- return if_clause.call(target)
83
- end
76
+ return target.__send__(if_clause) if if_clause.kind_of?(Symbol)
77
+ return if_clause.call(target) if if_clause.respond_to?(:call)
84
78
  end
85
79
 
86
80
  true
87
81
  end
88
82
 
83
+ # Set the default value for allow_nil and allow_blank
84
+ #
85
+ # @param [Boolean] default value
86
+ # @return <undefined>
87
+ def set_optional_by_default(default = true)
88
+ [ :allow_nil, :allow_blank ].each do |key|
89
+ @options[key] = true unless options.key?(key)
90
+ end
91
+ end
92
+
93
+ # Test the value to see if it is blank or nil, and if it is allowed
94
+ #
95
+ # @param <Object> value to test
96
+ # @return <Boolean> true if blank/nil is allowed, and the value is blank/nil
97
+ def optional?(value)
98
+ return allow_nil?(value) if value.nil?
99
+ return allow_blank?(value) if value.blank?
100
+ false
101
+ end
102
+
103
+ # Test if the value is nil and is allowed
104
+ #
105
+ # @param <Object> value to test
106
+ # @return <Boolean> true if nil is allowed and value is nil
107
+ def allow_nil?(value)
108
+ @options[:allow_nil] if value.nil?
109
+ end
110
+
111
+ # Test if the value is blank and is allowed
112
+ #
113
+ # @param <Object> value to test
114
+ # @return <Boolean> true if blank is allowed and value is blank
115
+ def allow_blank?(value)
116
+ @options[:allow_blank] if value.blank?
117
+ end
118
+
89
119
  # Returns true if validators are equal
90
120
  #
91
121
  # Note that this intentionally do
@@ -43,7 +43,7 @@ module DataMapper
43
43
  # @api semipublic
44
44
  def call(target)
45
45
  value = target.validation_property_value(field_name)
46
- return true if allow_nil? && value.blank?
46
+ return true if optional?(value)
47
47
 
48
48
  return true unless error_message = error_message_for(value)
49
49
 
@@ -54,16 +54,6 @@ module DataMapper
54
54
 
55
55
  private
56
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
57
  # Return the error messages for the value if it is invalid
68
58
  #
69
59
  # @param [#to_s] value
@@ -172,6 +162,7 @@ module DataMapper
172
162
  # you specify).
173
163
  #
174
164
  # @option :allow_nil<Boolean> true/false (default is true)
165
+ # @option :allow_blank<Boolean> true/false (default is true)
175
166
  # @option :minimum ensures that the attribute's length is greater than
176
167
  # or equal to the supplied value
177
168
  # @option :min alias for :minimum
@@ -9,11 +9,11 @@ module DataMapper
9
9
 
10
10
  def initialize(field_name, options={})
11
11
  super
12
- @options[:method] = @field_name unless @options.has_key?(:method)
12
+ @options[:method] = @field_name unless @options.key?(:method)
13
13
  end
14
14
 
15
15
  def call(target)
16
- result, message = target.send(@options[:method])
16
+ result, message = target.__send__(@options[:method])
17
17
  add_error(target, message, field_name) unless result
18
18
  result
19
19
  end
@@ -8,8 +8,8 @@ module DataMapper
8
8
  class NumericValidator < GenericValidator
9
9
 
10
10
  def call(target)
11
- value = target.send(field_name)
12
- return true if allow_nil? && value.blank?
11
+ value = target.validation_property_value(field_name)
12
+ return true if optional?(value)
13
13
 
14
14
  errors = []
15
15
 
@@ -31,10 +31,6 @@ module DataMapper
31
31
 
32
32
  private
33
33
 
34
- def allow_nil?
35
- options.fetch(:allow_nil, false)
36
- end
37
-
38
34
  def integer_only?
39
35
  options.fetch(:integer_only, false)
40
36
  end
@@ -131,6 +127,12 @@ module DataMapper
131
127
  #
132
128
  # Options are:
133
129
  #
130
+ # :allow_nil => true | false
131
+ # true if number can be nil, false if not
132
+ #
133
+ # :allow_blank => true | false
134
+ # true if number can be blank, false if not
135
+ #
134
136
  # :message => "Error message for %s"
135
137
  # Custom error message, also can be a callable object that takes
136
138
  # an object (for pure Ruby objects) or object and property (for DM resources)