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.
- data/{History.txt → History.rdoc} +10 -3
- data/LICENSE +1 -0
- data/Manifest.txt +103 -33
- data/README.rdoc +172 -0
- data/Rakefile +4 -5
- data/lib/dm-validations.rb +73 -75
- data/lib/dm-validations/auto_validate.rb +98 -56
- data/lib/dm-validations/contextual_validators.rb +37 -14
- data/lib/dm-validations/exceptions.rb +3 -0
- data/lib/dm-validations/formats/url.rb +3 -1
- data/lib/dm-validations/support/context.rb +54 -0
- data/lib/dm-validations/validation_errors.rb +51 -27
- data/lib/dm-validations/{absent_field_validator.rb → validators/absent_field_validator.rb} +2 -8
- data/lib/dm-validations/{acceptance_validator.rb → validators/acceptance_validator.rb} +7 -7
- data/lib/dm-validations/{block_validator.rb → validators/block_validator.rb} +0 -0
- data/lib/dm-validations/{confirmation_validator.rb → validators/confirmation_validator.rb} +7 -7
- data/lib/dm-validations/{format_validator.rb → validators/format_validator.rb} +10 -16
- data/lib/dm-validations/{generic_validator.rb → validators/generic_validator.rb} +28 -6
- data/lib/dm-validations/validators/length_validator.rb +214 -0
- data/lib/dm-validations/validators/method_validator.rb +68 -0
- data/lib/dm-validations/validators/numeric_validator.rb +171 -0
- data/lib/dm-validations/{primitive_validator.rb → validators/primitive_validator.rb} +2 -7
- data/lib/dm-validations/{required_field_validator.rb → validators/required_field_validator.rb} +0 -6
- data/lib/dm-validations/{uniqueness_validator.rb → validators/uniqueness_validator.rb} +9 -20
- data/lib/dm-validations/{within_validator.rb → validators/within_validator.rb} +4 -4
- data/lib/dm-validations/version.rb +1 -1
- data/spec/fixtures/barcode.rb +32 -0
- data/spec/fixtures/basketball_court.rb +58 -0
- data/spec/fixtures/basketball_player.rb +37 -0
- data/spec/fixtures/beta_tester_account.rb +33 -0
- data/spec/fixtures/bill_of_landing.rb +42 -0
- data/spec/fixtures/boat_dock.rb +26 -0
- data/spec/fixtures/city.rb +25 -0
- data/spec/fixtures/company.rb +95 -0
- data/spec/fixtures/corporate_world.rb +47 -0
- data/spec/fixtures/country.rb +25 -0
- data/spec/fixtures/currency.rb +42 -0
- data/spec/fixtures/ethernet_frame.rb +56 -0
- data/spec/fixtures/event.rb +44 -0
- data/spec/fixtures/g3_concert.rb +57 -0
- data/spec/fixtures/jabberwock.rb +27 -0
- data/spec/fixtures/kayak.rb +28 -0
- data/spec/fixtures/lernean_hydra.rb +39 -0
- data/spec/fixtures/mathematical_function.rb +34 -0
- data/spec/fixtures/memory_object.rb +30 -0
- data/spec/fixtures/mittelschnauzer.rb +35 -0
- data/spec/fixtures/motor_launch.rb +21 -0
- data/spec/fixtures/page.rb +32 -0
- data/spec/fixtures/phone_number.rb +28 -0
- data/spec/fixtures/pirogue.rb +28 -0
- data/spec/fixtures/programming_language.rb +83 -0
- data/spec/fixtures/reservation.rb +38 -0
- data/spec/fixtures/scm_operation.rb +67 -0
- data/spec/fixtures/sms_message.rb +22 -0
- data/spec/fixtures/udp_packet.rb +49 -0
- data/spec/integration/absent_field_validator/absent_field_validator_spec.rb +86 -0
- data/spec/integration/absent_field_validator/spec_helper.rb +7 -0
- data/spec/integration/acceptance_validator/acceptance_validator_spec.rb +196 -0
- data/spec/integration/acceptance_validator/spec_helper.rb +7 -0
- data/spec/integration/automatic_validation/custom_messages_for_inferred_validation_spec.rb +47 -0
- data/spec/integration/automatic_validation/disabling_inferred_validation_spec.rb +41 -0
- data/spec/integration/automatic_validation/inferred_boolean_properties_validation_spec.rb +104 -0
- data/spec/integration/automatic_validation/inferred_float_property_validation_spec.rb +35 -0
- data/spec/integration/automatic_validation/inferred_format_validation_spec.rb +33 -0
- data/spec/integration/automatic_validation/inferred_integer_properties_validation_spec.rb +73 -0
- data/spec/integration/automatic_validation/inferred_length_validation_spec.rb +121 -0
- data/spec/integration/automatic_validation/inferred_presence_validation_spec.rb +41 -0
- data/spec/integration/automatic_validation/inferred_primitive_validation_spec.rb +20 -0
- data/spec/integration/automatic_validation/inferred_within_validation_spec.rb +33 -0
- data/spec/integration/automatic_validation/spec_helper.rb +74 -0
- data/spec/integration/block_validator/block_validator_spec.rb +32 -0
- data/spec/integration/block_validator/spec_helper.rb +5 -0
- data/spec/integration/conditional_validation/if_condition_spec.rb +61 -0
- data/spec/integration/conditional_validation/spec_helper.rb +5 -0
- data/spec/integration/confirmation_validator/confirmation_validator_spec.rb +74 -0
- data/spec/integration/confirmation_validator/spec_helper.rb +5 -0
- data/spec/integration/datamapper_models/association_validation_spec.rb +23 -0
- data/spec/integration/datamapper_models/inheritance_spec.rb +78 -0
- data/spec/integration/duplicated_validations/duplicated_validations_spec.rb +22 -0
- data/spec/integration/duplicated_validations/spec_helper.rb +5 -0
- data/spec/integration/format_validator/email_format_validator_spec.rb +113 -0
- data/spec/integration/format_validator/format_validator_spec.rb +60 -0
- data/spec/integration/format_validator/regexp_validator_spec.rb +29 -0
- data/spec/integration/format_validator/spec_helper.rb +5 -0
- data/spec/integration/format_validator/url_format_validator_spec.rb +63 -0
- data/spec/integration/length_validator/default_value_spec.rb +12 -0
- data/spec/integration/length_validator/equality_spec.rb +79 -0
- data/spec/integration/length_validator/error_message_spec.rb +15 -18
- data/spec/integration/length_validator/maximum_spec.rb +40 -24
- data/spec/integration/length_validator/minimum_spec.rb +32 -21
- data/spec/integration/length_validator/range_spec.rb +50 -60
- data/spec/integration/length_validator/spec_helper.rb +7 -12
- data/spec/integration/method_validator/method_validator_spec.rb +239 -0
- data/spec/integration/method_validator/spec_helper.rb +5 -0
- data/spec/integration/numeric_validator/equality_with_float_type_spec.rb +61 -0
- data/spec/integration/numeric_validator/equality_with_integer_type_spec.rb +37 -0
- data/spec/integration/numeric_validator/float_type_spec.rb +42 -56
- data/spec/integration/numeric_validator/gt_with_float_type_spec.rb +35 -0
- data/spec/integration/numeric_validator/gte_with_float_type_spec.rb +35 -0
- data/spec/integration/numeric_validator/integer_only_true_spec.rb +3 -6
- data/spec/integration/numeric_validator/integer_type_spec.rb +8 -24
- data/spec/integration/numeric_validator/lt_with_float_type_spec.rb +35 -0
- data/spec/integration/numeric_validator/lte_with_float_type_spec.rb +35 -0
- data/spec/integration/numeric_validator/spec_helper.rb +5 -77
- data/spec/integration/primitive_validator/primitive_validator_spec.rb +90 -0
- data/spec/integration/primitive_validator/spec_helper.rb +5 -0
- data/spec/integration/pure_ruby_objects/plain_old_ruby_object_validation_spec.rb +118 -0
- data/spec/integration/required_field_validator/association_spec.rb +5 -8
- data/spec/integration/required_field_validator/boolean_type_value_spec.rb +4 -7
- data/spec/integration/required_field_validator/date_type_value_spec.rb +3 -6
- data/spec/integration/required_field_validator/datetime_type_value_spec.rb +3 -6
- data/spec/integration/required_field_validator/float_type_value_spec.rb +3 -6
- data/spec/integration/required_field_validator/integer_type_value_spec.rb +2 -5
- data/spec/integration/required_field_validator/plain_old_ruby_object_spec.rb +5 -8
- data/spec/integration/required_field_validator/shared_examples.rb +1 -1
- data/spec/integration/required_field_validator/spec_helper.rb +6 -67
- data/spec/integration/required_field_validator/string_type_value_spec.rb +2 -5
- data/spec/integration/required_field_validator/text_type_value_spec.rb +2 -5
- data/spec/integration/shared/default_validation_context.rb +13 -0
- data/spec/integration/shared/valid_and_invalid_model.rb +27 -0
- data/spec/integration/uniqueness_validator/spec_helper.rb +5 -0
- data/spec/integration/uniqueness_validator/uniqueness_validator_spec.rb +114 -0
- data/spec/integration/within_validator/spec_helper.rb +5 -0
- data/spec/integration/within_validator/within_validator_spec.rb +164 -0
- data/spec/public/resource_spec.rb +68 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +12 -4
- data/spec/unit/contextual_validators/emptiness_spec.rb +50 -0
- data/spec/unit/contextual_validators/execution_spec.rb +55 -0
- data/spec/unit/contextual_validators/spec_helper.rb +37 -0
- data/spec/unit/generic_validator/equality_operator_spec.rb +26 -0
- data/spec/unit/validation_errors/adding_spec.rb +54 -0
- data/spec/unit/validation_errors/emptiness_spec.rb +38 -0
- data/spec/unit/validation_errors/enumerable_spec.rb +32 -0
- data/tasks/install.rb +1 -1
- data/tasks/spec.rb +4 -4
- metadata +118 -55
- data/README.txt +0 -72
- data/lib/dm-validations/custom_validator.rb +0 -72
- data/lib/dm-validations/length_validator.rb +0 -113
- data/lib/dm-validations/method_validator.rb +0 -68
- data/lib/dm-validations/numeric_validator.rb +0 -83
- data/spec/integration/absent_field_validator_spec.rb +0 -36
- data/spec/integration/acceptance_validator_spec.rb +0 -87
- data/spec/integration/auto_validate_spec.rb +0 -342
- data/spec/integration/block_validator_spec.rb +0 -30
- data/spec/integration/confirmation_validator_spec.rb +0 -105
- data/spec/integration/contextual_validators_spec.rb +0 -27
- data/spec/integration/custom_validator_spec.rb +0 -9
- data/spec/integration/format_validator_spec.rb +0 -193
- data/spec/integration/generic_validator_spec.rb +0 -17
- data/spec/integration/length_validator/valid_objects_spec.rb +0 -13
- data/spec/integration/method_validator_spec.rb +0 -58
- data/spec/integration/numeric_validator_spec.rb +0 -253
- data/spec/integration/primitive_validator_spec.rb +0 -30
- data/spec/integration/uniqueness_validator_spec.rb +0 -97
- data/spec/integration/validation_errors_spec.rb +0 -18
- data/spec/integration/validation_spec.rb +0 -404
- data/spec/integration/within_validator_spec.rb +0 -79
@@ -1,29 +1,36 @@
|
|
1
1
|
module DataMapper
|
2
2
|
class Property
|
3
3
|
# for options_with_message
|
4
|
-
|
4
|
+
OPTIONS << :message << :messages << :set
|
5
5
|
end
|
6
6
|
|
7
7
|
module Validate
|
8
8
|
module AutoValidate
|
9
|
+
@disable_auto_validations = false
|
10
|
+
|
9
11
|
# adds message for validator
|
10
12
|
def options_with_message(base_options, property, validator_name)
|
11
13
|
options = base_options.clone
|
12
|
-
opts
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
elsif opts[:message]
|
20
|
-
opts[:message]
|
21
|
-
else
|
22
|
-
nil
|
14
|
+
opts = property.options
|
15
|
+
|
16
|
+
if opts.key?(:messages)
|
17
|
+
options[:message] = opts[:messages][validator_name]
|
18
|
+
elsif opts.key?(:message)
|
19
|
+
options[:message] = opts[:message]
|
23
20
|
end
|
21
|
+
|
24
22
|
options
|
25
23
|
end
|
26
24
|
|
25
|
+
attr_reader :disable_auto_validations
|
26
|
+
|
27
|
+
# disables generation of validations for
|
28
|
+
# duration of given block
|
29
|
+
def without_auto_validations(&block)
|
30
|
+
@disable_auto_validations = true
|
31
|
+
block.call
|
32
|
+
@disable_auto_validations = false
|
33
|
+
end
|
27
34
|
|
28
35
|
##
|
29
36
|
# Auto-generate validations for a given property. This will only occur
|
@@ -37,8 +44,8 @@ module DataMapper
|
|
37
44
|
# validates_presence_of validator to be automatically created on
|
38
45
|
# the property
|
39
46
|
#
|
40
|
-
# :
|
41
|
-
# Setting the option :
|
47
|
+
# :length => 20
|
48
|
+
# Setting the option :length causes a validates_length_of
|
42
49
|
# validator to be automatically created on the property. If the
|
43
50
|
# value is a Integer the validation will set :maximum => value if
|
44
51
|
# the value is a Range the validation will set :within => value
|
@@ -76,78 +83,113 @@ module DataMapper
|
|
76
83
|
# It is just shortcut if only one validation option is set
|
77
84
|
#
|
78
85
|
def auto_generate_validations(property)
|
79
|
-
|
80
|
-
|
86
|
+
return if disabled_auto_validations? || skip_auto_validation_for?(property)
|
87
|
+
|
88
|
+
# all auto-validations (aside from presence) should skip
|
89
|
+
# validation when the value is nil
|
90
|
+
opts = { :allow_nil => true }
|
81
91
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
92
|
+
if property.options.has_key?(:validates)
|
93
|
+
opts[:context] = property.options[:validates]
|
94
|
+
end
|
95
|
+
|
96
|
+
infer_presence_validation_for(property, opts.dup)
|
97
|
+
infer_length_validation_for(property, opts.dup)
|
98
|
+
infer_format_validation_for(property, opts.dup)
|
99
|
+
infer_uniqueness_validation_for(property, opts.dup)
|
100
|
+
infer_within_validation_for(property, opts.dup)
|
101
|
+
infer_numeric_validation_for(property, opts.dup)
|
102
|
+
end # auto_generate_validations
|
103
|
+
|
104
|
+
# Checks whether auto validations are currently
|
105
|
+
# disabled (see +disable_auto_validations+ method
|
106
|
+
# that takes a block)
|
107
|
+
#
|
108
|
+
# @return [TrueClass, FalseClass]
|
109
|
+
# true if auto validation is currently disabled
|
110
|
+
#
|
111
|
+
def disabled_auto_validations?
|
112
|
+
@disable_auto_validations || false
|
113
|
+
end
|
114
|
+
alias auto_validations_disabled? disabled_auto_validations?
|
86
115
|
|
87
|
-
|
88
|
-
|
116
|
+
|
117
|
+
# Checks whether or not property should be auto validated.
|
118
|
+
# It is the case for properties with :auto_validation option
|
119
|
+
# given and it's value evaluates to true
|
120
|
+
#
|
121
|
+
# @return [TrueClass, FalseClass]
|
122
|
+
# true for properties with :auto_validation option that has positive value
|
123
|
+
def skip_auto_validation_for?(property)
|
124
|
+
property.options.has_key?(:auto_validation) && !property.options[:auto_validation]
|
125
|
+
end
|
126
|
+
|
127
|
+
def infer_presence_validation_for(property, options)
|
128
|
+
unless allow_nil?(property)
|
89
129
|
# validates_present property.name, opts
|
90
|
-
validates_present property.name, options_with_message(
|
130
|
+
validates_present property.name, options_with_message(options, property, :presence)
|
91
131
|
end
|
132
|
+
end
|
92
133
|
|
93
|
-
|
94
|
-
if property.type
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
opts[:within] = len
|
99
|
-
else
|
100
|
-
opts[:maximum] = len
|
134
|
+
def infer_length_validation_for(property, options)
|
135
|
+
if [ String, DataMapper::Types::Text ].include?(property.type)
|
136
|
+
case length = property.options.fetch(:length, DataMapper::Property::DEFAULT_LENGTH)
|
137
|
+
when Range then options[:within] = length
|
138
|
+
else options[:maximum] = length
|
101
139
|
end
|
102
|
-
|
103
|
-
validates_length property.name, options_with_message(
|
140
|
+
|
141
|
+
validates_length property.name, options_with_message(options, property, :length)
|
104
142
|
end
|
143
|
+
end
|
105
144
|
|
106
|
-
|
145
|
+
def infer_format_validation_for(property, options)
|
107
146
|
if property.options.has_key?(:format)
|
108
|
-
|
109
|
-
|
110
|
-
validates_format property.name, options_with_message(opts, property, :format)
|
147
|
+
options[:with] = property.options[:format]
|
148
|
+
validates_format property.name, options_with_message(options, property, :format)
|
111
149
|
end
|
150
|
+
end
|
112
151
|
|
113
|
-
|
152
|
+
def infer_uniqueness_validation_for(property, options)
|
114
153
|
if property.options.has_key?(:unique)
|
115
154
|
value = property.options[:unique]
|
116
155
|
if value.is_a?(Array) || value.is_a?(Symbol)
|
117
|
-
# validates_is_unique property.name, :scope => Array(value)
|
118
156
|
validates_is_unique property.name, options_with_message({:scope => Array(value)}, property, :is_unique)
|
119
157
|
elsif value.is_a?(TrueClass)
|
120
|
-
# validates_is_unique property.name
|
121
158
|
validates_is_unique property.name, options_with_message({}, property, :is_unique)
|
122
159
|
end
|
123
160
|
end
|
161
|
+
end
|
124
162
|
|
125
|
-
|
126
|
-
if property.options.has_key?(:set)
|
127
|
-
|
128
|
-
|
163
|
+
def infer_within_validation_for(property, options)
|
164
|
+
validates_within property.name, options_with_message({:set => property.options[:set]}, property, :within) if property.options.has_key?(:set)
|
165
|
+
end
|
166
|
+
|
167
|
+
def infer_numeric_validation_for(property, options)
|
168
|
+
options[:gte] = property.min if property.min
|
169
|
+
options[:lte] = property.max if property.max
|
129
170
|
|
130
|
-
# numeric validator
|
131
171
|
if Integer == property.type
|
132
|
-
|
133
|
-
|
134
|
-
validates_is_number property.name, options_with_message(
|
172
|
+
options[:integer_only] = true
|
173
|
+
|
174
|
+
validates_is_number property.name, options_with_message(options, property, :is_number)
|
135
175
|
elsif BigDecimal == property.type || Float == property.type
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
validates_is_number property.name, options_with_message(
|
176
|
+
options[:precision] = property.precision
|
177
|
+
options[:scale] = property.scale
|
178
|
+
|
179
|
+
validates_is_number property.name, options_with_message(options, property, :is_number)
|
140
180
|
else
|
141
181
|
# We only need this in the case we don't already
|
142
182
|
# have a numeric validator, because otherwise
|
143
183
|
# it will cause duplicate validation errors
|
144
|
-
unless property.custom?
|
145
|
-
# validates_is_primitive property.name, opts
|
146
|
-
validates_is_primitive property.name, options_with_message(opts, property, :is_primitive)
|
147
|
-
end
|
184
|
+
validates_is_primitive property.name, options_with_message(options, property, :is_primitive) unless property.custom?
|
148
185
|
end
|
149
186
|
end
|
150
187
|
|
188
|
+
private
|
189
|
+
|
190
|
+
def allow_nil?(property)
|
191
|
+
property.nullable? || property.serial?
|
192
|
+
end
|
151
193
|
end # module AutoValidate
|
152
194
|
end # module Validate
|
153
195
|
end # module DataMapper
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require "forwardable"
|
2
|
+
|
1
3
|
module DataMapper
|
2
4
|
module Validate
|
3
5
|
|
@@ -6,23 +8,38 @@ module DataMapper
|
|
6
8
|
# @author Guy van den Berg
|
7
9
|
# @since 0.9
|
8
10
|
class ContextualValidators
|
11
|
+
extend Forwardable
|
12
|
+
|
13
|
+
#
|
14
|
+
# Delegators
|
15
|
+
#
|
16
|
+
|
17
|
+
def_delegators :@contexts, :empty?, :each
|
18
|
+
include Enumerable
|
19
|
+
|
20
|
+
attr_reader :contexts
|
21
|
+
|
22
|
+
def initialize
|
23
|
+
@contexts = {}
|
24
|
+
end
|
25
|
+
|
26
|
+
#
|
27
|
+
# API
|
28
|
+
#
|
9
29
|
|
10
30
|
def dump
|
11
31
|
contexts.each_pair do |key, context|
|
12
32
|
puts "Key=#{key} Context: #{context}"
|
13
33
|
end
|
14
34
|
end
|
15
|
-
|
16
|
-
# Get a hash of named context validators for the resource
|
17
|
-
#
|
18
|
-
# @return <Hash> a hash of validators <GenericValidator>
|
19
|
-
def contexts
|
20
|
-
@contexts ||= {}
|
21
|
-
end
|
35
|
+
alias_method :inspect, :dump
|
22
36
|
|
23
37
|
# Return an array of validators for a named context
|
24
38
|
#
|
25
|
-
# @
|
39
|
+
# @param [String]
|
40
|
+
# Context name for which return validators
|
41
|
+
# @return [Array<DataMapper::Validate::GenericValidator>]
|
42
|
+
# An array of validators
|
26
43
|
def context(name)
|
27
44
|
contexts[name] ||= []
|
28
45
|
end
|
@@ -35,18 +52,24 @@ module DataMapper
|
|
35
52
|
|
36
53
|
# Execute all validators in the named context against the target
|
37
54
|
#
|
38
|
-
# @param
|
39
|
-
#
|
40
|
-
# @
|
55
|
+
# @param [Symbol]
|
56
|
+
# named_context the context we are validating against
|
57
|
+
# @param [Object]
|
58
|
+
# target the resource that we are validating
|
59
|
+
# @return [Boolean]
|
60
|
+
# true if all are valid, otherwise false
|
41
61
|
def execute(named_context, target)
|
42
|
-
raise(ArgumentError,
|
62
|
+
raise(ArgumentError, "validation context #{named_context} doesn't seem to be defined. Known contexts are #{contexts.keys.inspect}") if !named_context || (contexts.length > 0 && !contexts[named_context])
|
43
63
|
target.errors.clear!
|
44
64
|
result = true
|
45
|
-
|
46
|
-
|
65
|
+
# note that all? and any? stop iteration on first negative or positive result,
|
66
|
+
# so we really have to use each here to make sure all validators are
|
67
|
+
# executed
|
68
|
+
context(named_context).select { |validator| validator.execute?(target) }.each do |validator|
|
47
69
|
result = false unless validator.call(target)
|
48
70
|
end
|
49
71
|
|
72
|
+
|
50
73
|
result
|
51
74
|
end
|
52
75
|
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
1
3
|
module DataMapper
|
2
4
|
module Validate
|
3
5
|
module Format
|
@@ -11,7 +13,7 @@ module DataMapper
|
|
11
13
|
|
12
14
|
Url = begin
|
13
15
|
# Regex from http://www.igvita.com/2006/09/07/validating-url-in-ruby-on-rails/
|
14
|
-
/(^$)|(^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}(([0-9]{1,5})
|
16
|
+
/(^$)|(^(http|https):\/\/[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,5}((\:[0-9]{1,5})?\/?.*)?$)/ix
|
15
17
|
end
|
16
18
|
|
17
19
|
end # module Url
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Validate
|
3
|
+
|
4
|
+
# Module with validation context functionality.
|
5
|
+
#
|
6
|
+
# Contexts are implemented using a simple array based
|
7
|
+
# stack that is thread local. The default context can be
|
8
|
+
# altered by overwriting default_validation_context or
|
9
|
+
# will default to :default
|
10
|
+
#
|
11
|
+
module Context
|
12
|
+
|
13
|
+
# TODO: document
|
14
|
+
# @api private
|
15
|
+
def default_validation_context
|
16
|
+
current_validation_context || :default
|
17
|
+
end
|
18
|
+
|
19
|
+
protected
|
20
|
+
|
21
|
+
# Pushes given context on top of context stack and yields
|
22
|
+
# given block, then pops the stack. During block execution
|
23
|
+
# contexts previously pushed onto the stack have no effect.
|
24
|
+
#
|
25
|
+
# @api private
|
26
|
+
def validation_context(context = default_validation_context)
|
27
|
+
validation_context_stack << context
|
28
|
+
begin
|
29
|
+
yield
|
30
|
+
ensure
|
31
|
+
validation_context_stack.pop
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# Initializes (if necessary) and returns current scope stack
|
38
|
+
# @api private
|
39
|
+
def validation_context_stack
|
40
|
+
Thread.current[:dm_validations_context_stack] ||= []
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the current validation context or nil if none has been pushed
|
44
|
+
# @api private
|
45
|
+
def current_validation_context
|
46
|
+
validation_context_stack.last
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
include Context
|
52
|
+
|
53
|
+
end
|
54
|
+
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
require 'extlib/dictionary'
|
2
|
+
|
1
3
|
module DataMapper
|
2
4
|
module Validate
|
3
5
|
|
@@ -10,27 +12,28 @@ module DataMapper
|
|
10
12
|
include Enumerable
|
11
13
|
|
12
14
|
@@default_error_messages = {
|
13
|
-
:absent
|
14
|
-
:inclusion
|
15
|
-
:invalid
|
16
|
-
:confirmation
|
17
|
-
:accepted
|
18
|
-
:nil
|
19
|
-
:blank
|
20
|
-
:length_between
|
21
|
-
:too_long
|
22
|
-
:too_short
|
23
|
-
:wrong_length
|
24
|
-
:taken
|
25
|
-
:not_a_number
|
26
|
-
:not_an_integer
|
27
|
-
:greater_than
|
28
|
-
:greater_than_or_equal_to =>
|
29
|
-
:equal_to
|
30
|
-
:
|
31
|
-
:
|
32
|
-
:
|
33
|
-
:
|
15
|
+
:absent => '%s must be absent',
|
16
|
+
:inclusion => '%s must be one of %s',
|
17
|
+
:invalid => '%s has an invalid format',
|
18
|
+
:confirmation => '%s does not match the confirmation',
|
19
|
+
:accepted => '%s is not accepted',
|
20
|
+
:nil => '%s must not be nil',
|
21
|
+
:blank => '%s must not be blank',
|
22
|
+
:length_between => '%s must be between %s and %s characters long',
|
23
|
+
:too_long => '%s must be at most %s characters long',
|
24
|
+
:too_short => '%s must be at least %s characters long',
|
25
|
+
:wrong_length => '%s must be %s characters long',
|
26
|
+
:taken => '%s is already taken',
|
27
|
+
:not_a_number => '%s must be a number',
|
28
|
+
:not_an_integer => '%s must be an integer',
|
29
|
+
:greater_than => '%s must be greater than %s',
|
30
|
+
:greater_than_or_equal_to => '%s must be greater than or equal to %s',
|
31
|
+
:equal_to => '%s must be equal to %s',
|
32
|
+
:not_equal_to => '%s must not be equal to %s',
|
33
|
+
:less_than => '%s must be less than %s',
|
34
|
+
:less_than_or_equal_to => '%s must be less than or equal to %s',
|
35
|
+
:value_between => '%s must be between %s and %s',
|
36
|
+
:primitive => '%s must be of type %s'
|
34
37
|
}
|
35
38
|
|
36
39
|
# Holds a hash with all the default error messages that can be replaced by your own copy or localizations.
|
@@ -41,6 +44,13 @@ module DataMapper
|
|
41
44
|
@@default_error_messages[key] % [field, *values].flatten
|
42
45
|
end
|
43
46
|
|
47
|
+
attr_reader :resource
|
48
|
+
|
49
|
+
def initialize(resource)
|
50
|
+
@resource = resource
|
51
|
+
@errors = Dictionary.new
|
52
|
+
end
|
53
|
+
|
44
54
|
# Clear existing validation errors.
|
45
55
|
def clear!
|
46
56
|
errors.clear
|
@@ -49,9 +59,21 @@ module DataMapper
|
|
49
59
|
# Add a validation error. Use the field_name :general if the errors does
|
50
60
|
# not apply to a specific field of the Resource.
|
51
61
|
#
|
52
|
-
# @param
|
53
|
-
# @param
|
62
|
+
# @param [Symbol] field_name the name of the field that caused the error
|
63
|
+
# @param [String] message the message to add
|
54
64
|
def add(field_name, message)
|
65
|
+
# see 6abe8fff in extlib, but don't enforce
|
66
|
+
# it unless Edge version is installed
|
67
|
+
if message.respond_to?(:try_call)
|
68
|
+
|
69
|
+
# DM resource
|
70
|
+
message = if resource.respond_to?(:model) && resource.model.respond_to?(:properties)
|
71
|
+
message.try_call(resource, resource.model.properties[field_name])
|
72
|
+
else
|
73
|
+
# pure Ruby object
|
74
|
+
message.try_call(resource)
|
75
|
+
end
|
76
|
+
end
|
55
77
|
(errors[field_name] ||= []) << message
|
56
78
|
end
|
57
79
|
|
@@ -64,10 +86,12 @@ module DataMapper
|
|
64
86
|
|
65
87
|
# Return validation errors for a particular field_name.
|
66
88
|
#
|
67
|
-
# @param
|
89
|
+
# @param [Symbol] field_name the name of the field you want an error for
|
90
|
+
# @return [Array<DataMapper::Validate::Error>]
|
91
|
+
# array of validation errors or empty array, if there are no errors on given field
|
68
92
|
def on(field_name)
|
69
93
|
errors_for_field = errors[field_name]
|
70
|
-
errors_for_field.blank? ? nil : errors_for_field
|
94
|
+
errors_for_field.blank? ? nil : errors_for_field.uniq
|
71
95
|
end
|
72
96
|
|
73
97
|
def each
|
@@ -78,7 +102,7 @@ module DataMapper
|
|
78
102
|
end
|
79
103
|
|
80
104
|
def empty?
|
81
|
-
|
105
|
+
@errors.empty?
|
82
106
|
end
|
83
107
|
|
84
108
|
def method_missing(meth, *args, &block)
|
@@ -87,7 +111,7 @@ module DataMapper
|
|
87
111
|
|
88
112
|
private
|
89
113
|
def errors
|
90
|
-
@errors
|
114
|
+
@errors
|
91
115
|
end
|
92
116
|
|
93
117
|
end # class ValidationErrors
|