sbf-dm-validations 1.3.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +38 -0
- data/.rspec +1 -0
- data/.rubocop.yml +468 -0
- data/.travis.yml +51 -0
- data/Gemfile +60 -0
- data/LICENSE +21 -0
- data/README.rdoc +122 -0
- data/Rakefile +4 -0
- data/dm-validations.gemspec +20 -0
- data/lib/data_mapper/core.rb +1 -0
- data/lib/data_mapper/support/assertions.rb +1 -0
- data/lib/data_mapper/support/equalizer.rb +1 -0
- data/lib/data_mapper/support/ordered_set.rb +2 -0
- data/lib/data_mapper/validation/backward.rb +205 -0
- data/lib/data_mapper/validation/context.rb +57 -0
- data/lib/data_mapper/validation/contextual_rule_set.rb +210 -0
- data/lib/data_mapper/validation/exceptions.rb +7 -0
- data/lib/data_mapper/validation/inferred.rb +264 -0
- data/lib/data_mapper/validation/macros.rb +449 -0
- data/lib/data_mapper/validation/message_transformer.rb +111 -0
- data/lib/data_mapper/validation/model_extensions.rb +17 -0
- data/lib/data_mapper/validation/resource_extensions.rb +131 -0
- data/lib/data_mapper/validation/rule/absence.rb +31 -0
- data/lib/data_mapper/validation/rule/acceptance.rb +49 -0
- data/lib/data_mapper/validation/rule/block.rb +37 -0
- data/lib/data_mapper/validation/rule/confirmation.rb +47 -0
- data/lib/data_mapper/validation/rule/format/proc.rb +34 -0
- data/lib/data_mapper/validation/rule/format/regexp.rb +51 -0
- data/lib/data_mapper/validation/rule/format.rb +86 -0
- data/lib/data_mapper/validation/rule/formats/email.rb +54 -0
- data/lib/data_mapper/validation/rule/formats/url.rb +13 -0
- data/lib/data_mapper/validation/rule/length/equal.rb +48 -0
- data/lib/data_mapper/validation/rule/length/maximum.rb +50 -0
- data/lib/data_mapper/validation/rule/length/minimum.rb +50 -0
- data/lib/data_mapper/validation/rule/length/range.rb +50 -0
- data/lib/data_mapper/validation/rule/length.rb +96 -0
- data/lib/data_mapper/validation/rule/method.rb +42 -0
- data/lib/data_mapper/validation/rule/numericalness/equal.rb +34 -0
- data/lib/data_mapper/validation/rule/numericalness/greater_than.rb +34 -0
- data/lib/data_mapper/validation/rule/numericalness/greater_than_or_equal.rb +34 -0
- data/lib/data_mapper/validation/rule/numericalness/integer.rb +41 -0
- data/lib/data_mapper/validation/rule/numericalness/less_than.rb +34 -0
- data/lib/data_mapper/validation/rule/numericalness/less_than_or_equal.rb +34 -0
- data/lib/data_mapper/validation/rule/numericalness/not_equal.rb +34 -0
- data/lib/data_mapper/validation/rule/numericalness/numeric.rb +68 -0
- data/lib/data_mapper/validation/rule/numericalness.rb +91 -0
- data/lib/data_mapper/validation/rule/presence.rb +52 -0
- data/lib/data_mapper/validation/rule/primitive_type.rb +32 -0
- data/lib/data_mapper/validation/rule/uniqueness.rb +64 -0
- data/lib/data_mapper/validation/rule/within/range/bounded.rb +29 -0
- data/lib/data_mapper/validation/rule/within/range/unbounded_begin.rb +29 -0
- data/lib/data_mapper/validation/rule/within/range/unbounded_end.rb +29 -0
- data/lib/data_mapper/validation/rule/within/range.rb +55 -0
- data/lib/data_mapper/validation/rule/within/set.rb +45 -0
- data/lib/data_mapper/validation/rule/within.rb +32 -0
- data/lib/data_mapper/validation/rule.rb +232 -0
- data/lib/data_mapper/validation/rule_set.rb +157 -0
- data/lib/data_mapper/validation/support/object.rb +19 -0
- data/lib/data_mapper/validation/support/ordered_hash.rb +434 -0
- data/lib/data_mapper/validation/version.rb +5 -0
- data/lib/data_mapper/validation/violation.rb +136 -0
- data/lib/data_mapper/validation/violation_set.rb +115 -0
- data/lib/data_mapper/validation.rb +105 -0
- data/lib/dm-validations.rb +24 -0
- data/spec/data_mapper/validation/resource_extensions/save_spec.rb +56 -0
- data/spec/data_mapper/validation/resource_extensions/validate_spec.rb +103 -0
- data/spec/fixtures/barcode.rb +40 -0
- data/spec/fixtures/basketball_court.rb +58 -0
- data/spec/fixtures/basketball_player.rb +34 -0
- data/spec/fixtures/beta_tester_account.rb +33 -0
- data/spec/fixtures/bill_of_landing.rb +47 -0
- data/spec/fixtures/boat_dock.rb +26 -0
- data/spec/fixtures/city.rb +24 -0
- data/spec/fixtures/company.rb +93 -0
- data/spec/fixtures/corporate_world.rb +39 -0
- data/spec/fixtures/country.rb +24 -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/integer_dumped_as_string_property.rb +24 -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/llama_spaceship.rb +15 -0
- data/spec/fixtures/mathematical_function.rb +34 -0
- data/spec/fixtures/memory_object.rb +35 -0
- data/spec/fixtures/mittelschnauzer.rb +39 -0
- data/spec/fixtures/motor_launch.rb +21 -0
- data/spec/fixtures/multibyte.rb +16 -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 +56 -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 +90 -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 +57 -0
- data/spec/integration/automatic_validation/disabling_inferred_validation_spec.rb +49 -0
- data/spec/integration/automatic_validation/inferred_boolean_properties_validation_spec.rb +100 -0
- data/spec/integration/automatic_validation/inferred_float_property_validation_spec.rb +45 -0
- data/spec/integration/automatic_validation/inferred_format_validation_spec.rb +35 -0
- data/spec/integration/automatic_validation/inferred_integer_properties_validation_spec.rb +70 -0
- data/spec/integration/automatic_validation/inferred_length_validation_spec.rb +142 -0
- data/spec/integration/automatic_validation/inferred_presence_validation_spec.rb +45 -0
- data/spec/integration/automatic_validation/inferred_primitive_validation_spec.rb +22 -0
- data/spec/integration/automatic_validation/inferred_uniqueness_validation_spec.rb +48 -0
- data/spec/integration/automatic_validation/inferred_within_validation_spec.rb +35 -0
- data/spec/integration/automatic_validation/spec_helper.rb +57 -0
- data/spec/integration/block_validator/spec_helper.rb +5 -0
- data/spec/integration/conditional_validation/if_condition_spec.rb +63 -0
- data/spec/integration/conditional_validation/spec_helper.rb +5 -0
- data/spec/integration/confirmation_validator/confirmation_validator_spec.rb +76 -0
- data/spec/integration/confirmation_validator/spec_helper.rb +5 -0
- data/spec/integration/datamapper_models/association_validation_spec.rb +29 -0
- data/spec/integration/datamapper_models/inheritance_spec.rb +82 -0
- data/spec/integration/dirty_attributes/dirty_attributes_spec.rb +13 -0
- data/spec/integration/duplicated_validations/duplicated_validations_spec.rb +24 -0
- data/spec/integration/duplicated_validations/spec_helper.rb +5 -0
- data/spec/integration/format_validator/email_format_validator_spec.rb +139 -0
- data/spec/integration/format_validator/format_validator_spec.rb +64 -0
- data/spec/integration/format_validator/regexp_validator_spec.rb +33 -0
- data/spec/integration/format_validator/spec_helper.rb +5 -0
- data/spec/integration/format_validator/url_format_validator_spec.rb +91 -0
- data/spec/integration/length_validator/default_value_spec.rb +14 -0
- data/spec/integration/length_validator/equality_spec.rb +83 -0
- data/spec/integration/length_validator/error_message_spec.rb +22 -0
- data/spec/integration/length_validator/maximum_spec.rb +47 -0
- data/spec/integration/length_validator/minimum_spec.rb +54 -0
- data/spec/integration/length_validator/range_spec.rb +87 -0
- data/spec/integration/length_validator/spec_helper.rb +7 -0
- data/spec/integration/method_validator/method_validator_spec.rb +243 -0
- data/spec/integration/method_validator/spec_helper.rb +5 -0
- data/spec/integration/numeric_validator/equality_with_float_type_spec.rb +65 -0
- data/spec/integration/numeric_validator/equality_with_integer_type_spec.rb +41 -0
- data/spec/integration/numeric_validator/float_type_spec.rb +90 -0
- data/spec/integration/numeric_validator/gt_with_float_type_spec.rb +37 -0
- data/spec/integration/numeric_validator/gte_with_float_type_spec.rb +36 -0
- data/spec/integration/numeric_validator/integer_only_true_spec.rb +91 -0
- data/spec/integration/numeric_validator/integer_type_spec.rb +86 -0
- data/spec/integration/numeric_validator/lt_with_float_type_spec.rb +37 -0
- data/spec/integration/numeric_validator/lte_with_float_type_spec.rb +37 -0
- data/spec/integration/numeric_validator/spec_helper.rb +5 -0
- data/spec/integration/primitive_validator/primitive_validator_spec.rb +112 -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 +69 -0
- data/spec/integration/required_field_validator/boolean_type_value_spec.rb +164 -0
- data/spec/integration/required_field_validator/date_type_value_spec.rb +127 -0
- data/spec/integration/required_field_validator/datetime_type_value_spec.rb +127 -0
- data/spec/integration/required_field_validator/float_type_value_spec.rb +131 -0
- data/spec/integration/required_field_validator/integer_type_value_spec.rb +99 -0
- data/spec/integration/required_field_validator/plain_old_ruby_object_spec.rb +35 -0
- data/spec/integration/required_field_validator/shared_examples.rb +27 -0
- data/spec/integration/required_field_validator/spec_helper.rb +7 -0
- data/spec/integration/required_field_validator/string_type_value_spec.rb +167 -0
- data/spec/integration/required_field_validator/text_type_value_spec.rb +49 -0
- data/spec/integration/shared/default_validation_context.rb +13 -0
- data/spec/integration/shared/valid_and_invalid_model.rb +35 -0
- data/spec/integration/uniqueness_validator/spec_helper.rb +5 -0
- data/spec/integration/uniqueness_validator/uniqueness_validator_spec.rb +116 -0
- data/spec/integration/within_validator/spec_helper.rb +5 -0
- data/spec/integration/within_validator/within_validator_spec.rb +168 -0
- data/spec/public/resource_spec.rb +113 -0
- data/spec/spec_helper.rb +28 -0
- data/spec/unit/contextual_validators/emptiness_spec.rb +50 -0
- data/spec/unit/contextual_validators/execution_spec.rb +48 -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/generic_validator/optional_spec.rb +54 -0
- data/spec/unit/validators/within_validator_spec.rb +23 -0
- data/spec/unit/violation_set/adding_spec.rb +54 -0
- data/spec/unit/violation_set/emptiness_spec.rb +38 -0
- data/spec/unit/violation_set/enumerable_spec.rb +32 -0
- data/spec/unit/violation_set/reading_spec.rb +35 -0
- data/spec/unit/violation_set/respond_to_spec.rb +15 -0
- data/tasks/spec.rake +21 -0
- data/tasks/yard.rake +9 -0
- data/tasks/yardstick.rake +19 -0
- metadata +245 -0
@@ -0,0 +1,264 @@
|
|
1
|
+
module DataMapper
|
2
|
+
# for options_with_message
|
3
|
+
# TODO: rename :auto_validation => :infer_validation
|
4
|
+
Property.accept_options :auto_validation, :validates, :set, :format, :message, :messages
|
5
|
+
|
6
|
+
module Validation
|
7
|
+
module Inferred
|
8
|
+
|
9
|
+
# @api private
|
10
|
+
def property(*)
|
11
|
+
property = super
|
12
|
+
|
13
|
+
if property.options.fetch(:auto_validation, true) && !disabled_auto_validations?
|
14
|
+
rule_definitions = Validation::Inferred.rules_for_property(property)
|
15
|
+
rule_definitions.each do |rule_class, attribute_name, options|
|
16
|
+
validation_rules.add(rule_class, [attribute_name], options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# FIXME: explicit return needed for YARD to parse this properly
|
21
|
+
return property
|
22
|
+
end
|
23
|
+
|
24
|
+
# attr_accessor :infer_validations
|
25
|
+
|
26
|
+
# TODO: replace the @disabled_auto_validations reader methods with
|
27
|
+
# a positive statement instead of negative (instead of skip/disable, etc)
|
28
|
+
# eg., @infer_validations, #infer_validations?
|
29
|
+
#
|
30
|
+
# Checks whether auto validations are currently
|
31
|
+
# disabled (see +disable_auto_validations+ method
|
32
|
+
# that takes a block)
|
33
|
+
#
|
34
|
+
# @return [TrueClass, FalseClass]
|
35
|
+
# true if auto validation is currently disabled
|
36
|
+
#
|
37
|
+
# @api public
|
38
|
+
# def infer_validations?
|
39
|
+
# defined?(@infer_validations) ? @infer_validations : true
|
40
|
+
# end
|
41
|
+
|
42
|
+
# TODO: why are there 3 entry points to this ivar?
|
43
|
+
# #disable_auto_validations, #disabled_auto_validations?, #auto_validations_disabled?
|
44
|
+
attr_reader :disable_auto_validations
|
45
|
+
|
46
|
+
# Checks whether auto validations are currently
|
47
|
+
# disabled (see +disable_auto_validations+ method
|
48
|
+
# that takes a block)
|
49
|
+
#
|
50
|
+
# @return [TrueClass, FalseClass]
|
51
|
+
# true if auto validation is currently disabled
|
52
|
+
#
|
53
|
+
# @api semipublic
|
54
|
+
def disabled_auto_validations?
|
55
|
+
@disable_auto_validations || false
|
56
|
+
end
|
57
|
+
|
58
|
+
# TODO: deprecate all but one of these 3 variants
|
59
|
+
alias_method :auto_validations_disabled?, :disabled_auto_validations?
|
60
|
+
|
61
|
+
# Disable generation of validations for the duration of the given block
|
62
|
+
#
|
63
|
+
# @api public
|
64
|
+
def without_auto_validations
|
65
|
+
previous, @disable_auto_validations = @disable_auto_validations, true
|
66
|
+
yield
|
67
|
+
ensure
|
68
|
+
@disable_auto_validations = previous
|
69
|
+
self
|
70
|
+
end
|
71
|
+
|
72
|
+
# Infer validations for a given property. This will only occur
|
73
|
+
# if the option :auto_validation is either true or left undefined.
|
74
|
+
#
|
75
|
+
# Triggers that generate validator creation
|
76
|
+
#
|
77
|
+
# :required => true
|
78
|
+
# Setting the option :required to true causes a Rule::Presence
|
79
|
+
# to be created for the property
|
80
|
+
#
|
81
|
+
# :length => 20
|
82
|
+
# Setting the option :length causes a Rule::Length to be created
|
83
|
+
# for the property.
|
84
|
+
# If the value is a Integer the Rule will have :maximum => value.
|
85
|
+
# If the value is a Range the Rule will have :within => value.
|
86
|
+
#
|
87
|
+
# :format => :predefined / lambda / Proc
|
88
|
+
# Setting the :format option causes a Rule::Format to be created
|
89
|
+
# for the property
|
90
|
+
#
|
91
|
+
# :set => ["foo", "bar", "baz"]
|
92
|
+
# Setting the :set option causes a Rule::Within to be created
|
93
|
+
# for the property
|
94
|
+
#
|
95
|
+
# Integer type
|
96
|
+
# Using a Integer type causes a Rule::Numericalness to be created
|
97
|
+
# for the property. The Rule's :integer_only option is set to true
|
98
|
+
#
|
99
|
+
# BigDecimal or Float type
|
100
|
+
# Using a Integer type causes a Rule::Numericalness to be created
|
101
|
+
# for the property. The Rule's :integer_only option will be set
|
102
|
+
# to false, and precision/scale will be set to match the Property
|
103
|
+
#
|
104
|
+
#
|
105
|
+
# Messages
|
106
|
+
#
|
107
|
+
# :messages => {..}
|
108
|
+
# Setting :messages hash replaces standard error messages
|
109
|
+
# with custom ones. For instance:
|
110
|
+
# :messages => {:presence => "Field is required",
|
111
|
+
# :format => "Field has invalid format"}
|
112
|
+
# Hash keys are: :presence, :format, :length, :is_unique,
|
113
|
+
# :is_number, :is_primitive
|
114
|
+
#
|
115
|
+
# :message => "Some message"
|
116
|
+
# It is just shortcut if only one validation option is set
|
117
|
+
#
|
118
|
+
# @api private
|
119
|
+
def self.rules_for_property(property)
|
120
|
+
rule_definitions = []
|
121
|
+
|
122
|
+
# all inferred rules should not be skipped when the value is nil
|
123
|
+
# (aside from Rule::Presence/Rule::Absence)
|
124
|
+
opts = { :allow_nil => true }
|
125
|
+
|
126
|
+
if property.options.key?(:validates)
|
127
|
+
opts[:context] = property.options[:validates]
|
128
|
+
end
|
129
|
+
|
130
|
+
rule_definitions << infer_presence( property, opts.dup)
|
131
|
+
rule_definitions << infer_length( property, opts.dup)
|
132
|
+
rule_definitions << infer_format( property, opts.dup)
|
133
|
+
rule_definitions << infer_uniqueness(property, opts.dup)
|
134
|
+
rule_definitions << infer_within( property, opts.dup)
|
135
|
+
rule_definitions << infer_type( property, opts.dup)
|
136
|
+
|
137
|
+
rule_definitions.compact
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
# @api private
|
143
|
+
def self.infer_presence(property, options)
|
144
|
+
return if property.allow_blank? || property.serial?
|
145
|
+
|
146
|
+
validation_options = options_with_message(options, property, :presence)
|
147
|
+
|
148
|
+
[Rule::Presence, property.name, validation_options]
|
149
|
+
end
|
150
|
+
|
151
|
+
# @api private
|
152
|
+
def self.infer_length(property, options)
|
153
|
+
# TODO: return unless property.primitive <= String (?)
|
154
|
+
return unless (property.kind_of?(Property::String) ||
|
155
|
+
property.kind_of?(Property::Text))
|
156
|
+
length = property.options.fetch(:length, Property::String.length)
|
157
|
+
|
158
|
+
|
159
|
+
if length.is_a?(Range)
|
160
|
+
if length.last == Infinity
|
161
|
+
raise ArgumentError, "Infinity is not a valid upper bound for a length range"
|
162
|
+
end
|
163
|
+
options[:within] = length
|
164
|
+
else
|
165
|
+
options[:maximum] = length
|
166
|
+
end
|
167
|
+
|
168
|
+
validation_options = options_with_message(options, property, :length)
|
169
|
+
|
170
|
+
[Rule::Length, property.name, validation_options]
|
171
|
+
end
|
172
|
+
|
173
|
+
# @api private
|
174
|
+
def self.infer_format(property, options)
|
175
|
+
return unless property.options.key?(:format)
|
176
|
+
|
177
|
+
options[:with] = property.options[:format]
|
178
|
+
|
179
|
+
validation_options = options_with_message(options, property, :format)
|
180
|
+
|
181
|
+
[Rule::Format, property.name, validation_options]
|
182
|
+
end
|
183
|
+
|
184
|
+
# @api private
|
185
|
+
def self.infer_uniqueness(property, options)
|
186
|
+
return unless property.options.key?(:unique)
|
187
|
+
|
188
|
+
case value = property.options[:unique]
|
189
|
+
when Array, Symbol
|
190
|
+
# TODO: fix this to behave like :unique_index
|
191
|
+
options[:scope] = Array(value)
|
192
|
+
|
193
|
+
validation_options = options_with_message(options, property, :is_unique)
|
194
|
+
[Rule::Uniqueness, property.name, validation_options]
|
195
|
+
when TrueClass
|
196
|
+
validation_options = options_with_message(options, property, :is_unique)
|
197
|
+
[Rule::Uniqueness, property.name, validation_options]
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# @api private
|
202
|
+
def self.infer_within(property, options)
|
203
|
+
return unless property.options.key?(:set)
|
204
|
+
|
205
|
+
options[:set] = property.options[:set]
|
206
|
+
|
207
|
+
validation_options = options_with_message(options, property, :within)
|
208
|
+
[Rule::Within, property.name, validation_options]
|
209
|
+
end
|
210
|
+
|
211
|
+
# @api private
|
212
|
+
def self.infer_type(property, options)
|
213
|
+
return if property.respond_to?(:custom?) && property.custom?
|
214
|
+
|
215
|
+
if property.kind_of?(Property::Numeric)
|
216
|
+
options[:gte] = property.min if property.min
|
217
|
+
options[:lte] = property.max if property.max
|
218
|
+
end
|
219
|
+
|
220
|
+
if Integer == property.load_as
|
221
|
+
options[:integer_only] = true
|
222
|
+
|
223
|
+
validation_options = options_with_message(options, property, :is_number)
|
224
|
+
[Rule::Numericalness, property.name, validation_options]
|
225
|
+
elsif (BigDecimal == property.load_as ||
|
226
|
+
Float == property.load_as)
|
227
|
+
options[:precision] = property.precision
|
228
|
+
options[:scale] = property.scale
|
229
|
+
|
230
|
+
validation_options = options_with_message(options, property, :is_number)
|
231
|
+
[Rule::Numericalness, property.name, validation_options]
|
232
|
+
else
|
233
|
+
# We only need this in the case we don't already
|
234
|
+
# have a numeric validator, because otherwise
|
235
|
+
# it will cause duplicate validation errors
|
236
|
+
validation_options = options_with_message(options, property, :is_primitive)
|
237
|
+
[Rule::PrimitiveType, property.name, validation_options]
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
# TODO: eliminate this;
|
242
|
+
# mutating one arg based on a non-obvious interaction of the other two...
|
243
|
+
# well, it makes my skin crawl.
|
244
|
+
#
|
245
|
+
# @api private
|
246
|
+
def self.options_with_message(base_options, property, validator_name)
|
247
|
+
options = base_options.clone
|
248
|
+
opts = property.options
|
249
|
+
|
250
|
+
if opts.key?(:messages)
|
251
|
+
options[:message] = opts[:messages][validator_name]
|
252
|
+
elsif opts.key?(:message)
|
253
|
+
options[:message] = opts[:message]
|
254
|
+
end
|
255
|
+
|
256
|
+
options
|
257
|
+
end
|
258
|
+
|
259
|
+
end # module Inferred
|
260
|
+
end # module Validation
|
261
|
+
|
262
|
+
Model.append_extensions Validation::Inferred
|
263
|
+
|
264
|
+
end # module DataMapper
|
@@ -0,0 +1,449 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'data_mapper/validation/rule'
|
4
|
+
|
5
|
+
require 'data_mapper/validation/rule/absence'
|
6
|
+
require 'data_mapper/validation/rule/acceptance'
|
7
|
+
require 'data_mapper/validation/rule/block'
|
8
|
+
require 'data_mapper/validation/rule/confirmation'
|
9
|
+
require 'data_mapper/validation/rule/format'
|
10
|
+
require 'data_mapper/validation/rule/length'
|
11
|
+
require 'data_mapper/validation/rule/method'
|
12
|
+
require 'data_mapper/validation/rule/numericalness'
|
13
|
+
require 'data_mapper/validation/rule/presence'
|
14
|
+
require 'data_mapper/validation/rule/primitive_type'
|
15
|
+
require 'data_mapper/validation/rule/uniqueness'
|
16
|
+
require 'data_mapper/validation/rule/within'
|
17
|
+
|
18
|
+
module DataMapper
|
19
|
+
module Validation
|
20
|
+
module Macros
|
21
|
+
def self.extract_options(arguments)
|
22
|
+
arguments.last.kind_of?(Hash) ? arguments.pop : {}
|
23
|
+
end
|
24
|
+
|
25
|
+
# Validates that the specified attribute is "blank" via the
|
26
|
+
# attribute's #blank? method.
|
27
|
+
#
|
28
|
+
# @note
|
29
|
+
# dm-core's support lib adds the #blank? method to many classes,
|
30
|
+
# @see lib/dm-core/support/blank.rb (dm-core) for more information.
|
31
|
+
#
|
32
|
+
# @example [Usage]
|
33
|
+
# require 'dm-validations'
|
34
|
+
#
|
35
|
+
# class Page
|
36
|
+
# include DataMapper::Resource
|
37
|
+
#
|
38
|
+
# property :unwanted_attribute, String
|
39
|
+
# property :another_unwanted, String
|
40
|
+
# property :yet_again, String
|
41
|
+
#
|
42
|
+
# validates_absence_of :unwanted_attribute
|
43
|
+
# validates_absence_of :another_unwanted, :yet_again
|
44
|
+
#
|
45
|
+
# # a call to #validate will return false unless
|
46
|
+
# # all three attributes are blank
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
def validates_absence_of(*attribute_names)
|
50
|
+
options = Macros.extract_options(attribute_names)
|
51
|
+
validation_rules.add(Rule::Absence, attribute_names, options)
|
52
|
+
end
|
53
|
+
|
54
|
+
# Validates that the attributes's value is in the set of accepted
|
55
|
+
# values.
|
56
|
+
#
|
57
|
+
# @option [Boolean] :allow_nil (true)
|
58
|
+
# true if nil is allowed, false if not allowed.
|
59
|
+
#
|
60
|
+
# @option [Array] :accept (["1", 1, "true", true, "t"])
|
61
|
+
# A list of accepted values.
|
62
|
+
#
|
63
|
+
# @example Usage
|
64
|
+
# require 'dm-validations'
|
65
|
+
#
|
66
|
+
# class Page
|
67
|
+
# include DataMapper::Resource
|
68
|
+
#
|
69
|
+
# property :license_agreement_accepted, String
|
70
|
+
# property :terms_accepted, String
|
71
|
+
# validates_acceptance_of :license_agreement, :accept => "1"
|
72
|
+
# validates_acceptance_of :terms_accepted, :allow_nil => false
|
73
|
+
#
|
74
|
+
# # a call to valid? will return false unless:
|
75
|
+
# # license_agreement is nil or "1"
|
76
|
+
# # and
|
77
|
+
# # terms_accepted is one of ["1", 1, "true", true, "t"]
|
78
|
+
#
|
79
|
+
def validates_acceptance_of(*attribute_names)
|
80
|
+
options = Macros.extract_options(attribute_names)
|
81
|
+
validation_rules.add(Rule::Acceptance, attribute_names, options)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Validate using the given block. The block given needs to return:
|
85
|
+
# [result::<Boolean>, Error Message::<String>]
|
86
|
+
#
|
87
|
+
# @example [Usage]
|
88
|
+
# require 'dm-validations'
|
89
|
+
#
|
90
|
+
# class Page
|
91
|
+
# include DataMapper::Resource
|
92
|
+
#
|
93
|
+
# property :zip_code, String
|
94
|
+
#
|
95
|
+
# validates_with_block do
|
96
|
+
# if @zip_code == "94301"
|
97
|
+
# true
|
98
|
+
# else
|
99
|
+
# [false, "You're in the wrong zip code"]
|
100
|
+
# end
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
# # A call to valid? will return false and
|
104
|
+
# # populate the object's errors with "You're in the
|
105
|
+
# # wrong zip code" unless zip_code == "94301"
|
106
|
+
#
|
107
|
+
# # You can also specify field:
|
108
|
+
#
|
109
|
+
# validates_with_block :zip_code do
|
110
|
+
# if @zip_code == "94301"
|
111
|
+
# true
|
112
|
+
# else
|
113
|
+
# [false, "You're in the wrong zip code"]
|
114
|
+
# end
|
115
|
+
# end
|
116
|
+
#
|
117
|
+
# # it will add returned error message to :zip_code field
|
118
|
+
#
|
119
|
+
def validates_with_block(*attribute_names, &block)
|
120
|
+
unless block_given?
|
121
|
+
raise ArgumentError, 'You need to pass a block to validates_with_block'
|
122
|
+
end
|
123
|
+
|
124
|
+
options = Macros.extract_options(attribute_names)
|
125
|
+
validation_rules.add(Rule::Block, attribute_names, options, &block)
|
126
|
+
end
|
127
|
+
|
128
|
+
# Validates that the given attribute is confirmed by another
|
129
|
+
# attribute. A common use case scenario is when you require a user to
|
130
|
+
# confirm their password, for which you use both password and
|
131
|
+
# password_confirmation attributes.
|
132
|
+
#
|
133
|
+
# @option [Boolean] :allow_nil (true)
|
134
|
+
# true or false.
|
135
|
+
#
|
136
|
+
# @option [Boolean] :allow_blank (true)
|
137
|
+
# true or false.
|
138
|
+
#
|
139
|
+
# @option [Symbol] :confirm (firstattr_confirmation)
|
140
|
+
# The attribute that you want to validate against.
|
141
|
+
#
|
142
|
+
# @example Usage
|
143
|
+
# require 'dm-validations'
|
144
|
+
#
|
145
|
+
# class Page
|
146
|
+
# include DataMapper::Resource
|
147
|
+
#
|
148
|
+
# property :password, String
|
149
|
+
# property :email, String
|
150
|
+
# attr_accessor :password_confirmation
|
151
|
+
# attr_accessor :email_repeated
|
152
|
+
#
|
153
|
+
# validates_confirmation_of :password
|
154
|
+
# validates_confirmation_of :email, :confirm => :email_repeated
|
155
|
+
#
|
156
|
+
# # a call to valid? will return false unless:
|
157
|
+
# # password == password_confirmation
|
158
|
+
# # and
|
159
|
+
# # email == email_repeated
|
160
|
+
#
|
161
|
+
def validates_confirmation_of(*attribute_names)
|
162
|
+
options = Macros.extract_options(attribute_names)
|
163
|
+
validation_rules.add(Rule::Confirmation, attribute_names, options)
|
164
|
+
end
|
165
|
+
|
166
|
+
# Validates that the attribute is in the specified format. You may
|
167
|
+
# use the :as (or :with, it's an alias) option to specify the
|
168
|
+
# pre-defined format that you want to validate against. You may also
|
169
|
+
# specify your own format via a Proc or Regexp passed to the the :as
|
170
|
+
# or :with options.
|
171
|
+
#
|
172
|
+
# @option [Boolean] :allow_nil (true)
|
173
|
+
# true or false.
|
174
|
+
#
|
175
|
+
# @option [Boolean] :allow_blank (true)
|
176
|
+
# true or false.
|
177
|
+
#
|
178
|
+
# @option [Format, Proc, Regexp] :as
|
179
|
+
# The pre-defined format, Proc or Regexp to validate against.
|
180
|
+
#
|
181
|
+
# @option [Format, Proc, Regexp] :with
|
182
|
+
# An alias for :as.
|
183
|
+
#
|
184
|
+
# :email_address (format is specified in DataMapper::Validation::Format::Email - note that unicode emails will *not* be matched under MRI1.8.7)
|
185
|
+
# :url (format is specified in DataMapper::Validation::Format::Url)
|
186
|
+
#
|
187
|
+
# @example Usage
|
188
|
+
# require 'dm-validations'
|
189
|
+
#
|
190
|
+
# class Page
|
191
|
+
# include DataMapper::Resource
|
192
|
+
#
|
193
|
+
# property :email, String
|
194
|
+
# property :zip_code, String
|
195
|
+
#
|
196
|
+
# validates_format_of :email, :as => :email_address
|
197
|
+
# validates_format_of :zip_code, :with => /^\d{5}$/
|
198
|
+
#
|
199
|
+
# # a call to valid? will return false unless:
|
200
|
+
# # email is formatted like an email address
|
201
|
+
# # and
|
202
|
+
# # zip_code is a string of 5 digits
|
203
|
+
#
|
204
|
+
def validates_format_of(*attribute_names)
|
205
|
+
options = Macros.extract_options(attribute_names)
|
206
|
+
validation_rules.add(Rule::Format, attribute_names, options)
|
207
|
+
end
|
208
|
+
|
209
|
+
# Validates that the length of the attribute is equal to, less than,
|
210
|
+
# greater than or within a certain range (depending upon the options
|
211
|
+
# you specify).
|
212
|
+
#
|
213
|
+
# @option [Boolean] :allow_nil (true)
|
214
|
+
# true or false.
|
215
|
+
#
|
216
|
+
# @option [Boolean] :allow_blank (true)
|
217
|
+
# true or false.
|
218
|
+
#
|
219
|
+
# @option [Boolean] :minimum
|
220
|
+
# Ensures that the attribute's length is greater than or equal to
|
221
|
+
# the supplied value.
|
222
|
+
#
|
223
|
+
# @option [Boolean] :min
|
224
|
+
# Alias for :minimum.
|
225
|
+
#
|
226
|
+
# @option [Boolean] :maximum
|
227
|
+
# Ensures the attribute's length is less than or equal to the
|
228
|
+
# supplied value.
|
229
|
+
#
|
230
|
+
# @option [Boolean] :max
|
231
|
+
# Alias for :maximum.
|
232
|
+
#
|
233
|
+
# @option [Boolean] :equals
|
234
|
+
# Ensures the attribute's length is equal to the supplied value.
|
235
|
+
#
|
236
|
+
# @option [Boolean] :is
|
237
|
+
# Alias for :equals.
|
238
|
+
#
|
239
|
+
# @option [Range] :in
|
240
|
+
# Given a Range, ensures that the attributes length is include?'ed
|
241
|
+
# in the Range.
|
242
|
+
#
|
243
|
+
# @option [Range] :within
|
244
|
+
# Alias for :in.
|
245
|
+
#
|
246
|
+
# @example Usage
|
247
|
+
# require 'dm-validations'
|
248
|
+
#
|
249
|
+
# class Page
|
250
|
+
# include DataMapper::Resource
|
251
|
+
#
|
252
|
+
# property high, Integer
|
253
|
+
# property low, Integer
|
254
|
+
# property just_right, Integer
|
255
|
+
#
|
256
|
+
# validates_length_of :high, :min => 100000000000
|
257
|
+
# validates_length_of :low, :equals => 0
|
258
|
+
# validates_length_of :just_right, :within => 1..10
|
259
|
+
#
|
260
|
+
# # a call to valid? will return false unless:
|
261
|
+
# # high is greater than or equal to 100000000000
|
262
|
+
# # low is equal to 0
|
263
|
+
# # just_right is between 1 and 10 (inclusive of both 1 and 10)
|
264
|
+
#
|
265
|
+
def validates_length_of(*attribute_names)
|
266
|
+
options = Macros.extract_options(attribute_names)
|
267
|
+
validation_rules.add(Rule::Length, attribute_names, options)
|
268
|
+
end
|
269
|
+
|
270
|
+
# Validate using method called on validated object. The method must
|
271
|
+
# to return either true, or a pair of [false, error message string],
|
272
|
+
# and is specified as a symbol passed with :method option.
|
273
|
+
#
|
274
|
+
# This validator does support multiple attribute_names being specified at a
|
275
|
+
# time, but we encourage you to use it with one property/method at a
|
276
|
+
# time.
|
277
|
+
#
|
278
|
+
# Real world experience shows that method validation is often useful
|
279
|
+
# when attribute needs to be virtual and not a property name.
|
280
|
+
#
|
281
|
+
# @example Usage
|
282
|
+
# require 'dm-validations'
|
283
|
+
#
|
284
|
+
# class Page
|
285
|
+
# include DataMapper::Resource
|
286
|
+
#
|
287
|
+
# property :zip_code, String
|
288
|
+
#
|
289
|
+
# validates_with_method :zip_code,
|
290
|
+
# :method => :in_the_right_location?
|
291
|
+
#
|
292
|
+
# def in_the_right_location?
|
293
|
+
# if @zip_code == "94301"
|
294
|
+
# return true
|
295
|
+
# else
|
296
|
+
# return [false, "You're in the wrong zip code"]
|
297
|
+
# end
|
298
|
+
# end
|
299
|
+
#
|
300
|
+
# # A call to valid? will return false and
|
301
|
+
# # populate the object's errors with "You're in the
|
302
|
+
# # wrong zip code" unless zip_code == "94301"
|
303
|
+
# end
|
304
|
+
def validates_with_method(*attribute_names)
|
305
|
+
options = Macros.extract_options(attribute_names)
|
306
|
+
validation_rules.add(Rule::Method, attribute_names, options)
|
307
|
+
end
|
308
|
+
|
309
|
+
# Validate whether a field is numeric.
|
310
|
+
#
|
311
|
+
# @option [Boolean] :allow_nil
|
312
|
+
# true if number can be nil, false if not.
|
313
|
+
#
|
314
|
+
# @option [Boolean] :allow_blank
|
315
|
+
# true if number can be blank, false if not.
|
316
|
+
#
|
317
|
+
# @option [String] :message
|
318
|
+
# Custom error message, also can be a callable object that takes
|
319
|
+
# an object (for pure Ruby objects) or object and property
|
320
|
+
# (for DM resources).
|
321
|
+
#
|
322
|
+
# @option [Numeric] :precision
|
323
|
+
# Required precision of a value.
|
324
|
+
#
|
325
|
+
# @option [Numeric] :scale
|
326
|
+
# Required scale of a value.
|
327
|
+
#
|
328
|
+
# @option [Numeric] :gte
|
329
|
+
# 'Greater than or equal to' requirement.
|
330
|
+
#
|
331
|
+
# @option [Numeric] :lte
|
332
|
+
# 'Less than or equal to' requirement.
|
333
|
+
#
|
334
|
+
# @option [Numeric] :lt
|
335
|
+
# 'Less than' requirement.
|
336
|
+
#
|
337
|
+
# @option [Numeric] :gt
|
338
|
+
# 'Greater than' requirement.
|
339
|
+
#
|
340
|
+
# @option [Numeric] :eq
|
341
|
+
# 'Equal' requirement.
|
342
|
+
#
|
343
|
+
# @option [Numeric] :ne
|
344
|
+
# 'Not equal' requirement.
|
345
|
+
#
|
346
|
+
# @option [Boolean] :integer_only
|
347
|
+
# Use to restrict allowed values to integers.
|
348
|
+
#
|
349
|
+
def validates_numericalness_of(*attribute_names)
|
350
|
+
options = Macros.extract_options(attribute_names)
|
351
|
+
validation_rules.add(Rule::Numericalness, attribute_names, options)
|
352
|
+
end
|
353
|
+
|
354
|
+
# Validates that the specified attribute is present.
|
355
|
+
#
|
356
|
+
# For most property types "being present" is the same as being "not
|
357
|
+
# blank" as determined by the attribute's #blank? method. However, in
|
358
|
+
# the case of Boolean, "being present" means not nil; i.e. true or
|
359
|
+
# false.
|
360
|
+
#
|
361
|
+
# @note
|
362
|
+
# dm-core's support lib adds the blank? method to many classes,
|
363
|
+
#
|
364
|
+
# @see lib/dm-core/support/blank.rb (dm-core) for more information.
|
365
|
+
#
|
366
|
+
# @example Usage
|
367
|
+
# require 'dm-validations'
|
368
|
+
#
|
369
|
+
# class Page
|
370
|
+
# include DataMapper::Resource
|
371
|
+
#
|
372
|
+
# property :required_attribute, String
|
373
|
+
# property :another_required, String
|
374
|
+
# property :yet_again, String
|
375
|
+
#
|
376
|
+
# validates_presence_of :required_attribute
|
377
|
+
# validates_presence_of :another_required, :yet_again
|
378
|
+
#
|
379
|
+
# # a call to valid? will return false unless
|
380
|
+
# # all three attributes are !blank?
|
381
|
+
# end
|
382
|
+
def validates_presence_of(*attribute_names)
|
383
|
+
options = attribute_names.last.kind_of?(Hash) ? attribute_names.pop : {}
|
384
|
+
validation_rules.add(Rule::Presence, attribute_names, options)
|
385
|
+
end
|
386
|
+
|
387
|
+
# Validates that the specified attribute is of the correct primitive
|
388
|
+
# type.
|
389
|
+
#
|
390
|
+
# @example Usage
|
391
|
+
# require 'dm-validations'
|
392
|
+
#
|
393
|
+
# class Person
|
394
|
+
# include DataMapper::Resource
|
395
|
+
#
|
396
|
+
# property :birth_date, Date
|
397
|
+
#
|
398
|
+
# validates_primitive_type_of :birth_date
|
399
|
+
#
|
400
|
+
# # a call to valid? will return false unless
|
401
|
+
# # the birth_date is something that can be properly
|
402
|
+
# # casted into a Date object.
|
403
|
+
# end
|
404
|
+
def validates_primitive_type_of(*attribute_names)
|
405
|
+
options = Macros.extract_options(attribute_names)
|
406
|
+
validation_rules.add(Rule::PrimitiveType, attribute_names, options)
|
407
|
+
end
|
408
|
+
|
409
|
+
# Validate the uniqueness of a field
|
410
|
+
#
|
411
|
+
# TODO: YARDoc for this method
|
412
|
+
def validates_uniqueness_of(*attribute_names)
|
413
|
+
options = Macros.extract_options(attribute_names)
|
414
|
+
validation_rules.add(Rule::Uniqueness, attribute_names, options)
|
415
|
+
end
|
416
|
+
|
417
|
+
# Validates that the value of a field is within a range/set.
|
418
|
+
#
|
419
|
+
# This validation is defined by passing a field along with a :set
|
420
|
+
# parameter. The :set can be a Range or any object which responds
|
421
|
+
# to the #include? method (an array, for example).
|
422
|
+
#
|
423
|
+
# @example Usage
|
424
|
+
# require 'dm-validations'
|
425
|
+
#
|
426
|
+
# class Review
|
427
|
+
# include DataMapper::Resource
|
428
|
+
#
|
429
|
+
# STATES = ['new', 'in_progress', 'published', 'archived']
|
430
|
+
#
|
431
|
+
# property :title, String
|
432
|
+
# property :body, String
|
433
|
+
# property :review_state, String
|
434
|
+
# property :rating, Integer
|
435
|
+
#
|
436
|
+
# validates_within :review_state, :set => STATES
|
437
|
+
# validates_within :rating, :set => 1..5
|
438
|
+
#
|
439
|
+
# # a call to valid? will return false unless
|
440
|
+
# # the two properties conform to their sets
|
441
|
+
# end
|
442
|
+
def validates_within(*attribute_names)
|
443
|
+
options = Macros.extract_options(attribute_names)
|
444
|
+
validation_rules.add(Rule::Within, attribute_names, options)
|
445
|
+
end
|
446
|
+
|
447
|
+
end # module Macros
|
448
|
+
end # module Validation
|
449
|
+
end # module DataMapper
|