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
data/README.rdoc
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
This is a DataMapper plugin that provides validations for DataMapper model classes.
|
2
|
+
|
3
|
+
== Setup
|
4
|
+
DataMapper validation capabilities are automatically available for DataMapper resources when you require dm-validations' in your application. There is no need to manually include anything, every DataMapper::Resource will be able to handle validations once this gem got required.
|
5
|
+
|
6
|
+
== Specifying Model Validations
|
7
|
+
|
8
|
+
There are two primary ways to implement validations for your models
|
9
|
+
|
10
|
+
1) Placing validation methods with properties as params in your class
|
11
|
+
|
12
|
+
require 'dm-core'
|
13
|
+
require 'dm-validations'
|
14
|
+
|
15
|
+
class ProgrammingLanguage
|
16
|
+
include DataMapper::Resource
|
17
|
+
property :name, String
|
18
|
+
validates_presence_of :name
|
19
|
+
end
|
20
|
+
|
21
|
+
2) Using auto-validations, please see DataMapper::Validation::AutoValidations. Note that not all validations that are provided via validation methods, are also available as autovalidation options. If they are available, they're functionally equivalent though.
|
22
|
+
|
23
|
+
class ProgrammingLanguage
|
24
|
+
include DataMapper::Resource
|
25
|
+
property :name, String, :required => true
|
26
|
+
end
|
27
|
+
|
28
|
+
See data_mapper/validation/macros.rb for to learn about the complete collection of validation rules available.
|
29
|
+
|
30
|
+
== Validating
|
31
|
+
|
32
|
+
DataMapper validations, when included, alter the default save/create/update process for a model. Unless you specify a context the resource must be valid in the :default context before saving.
|
33
|
+
|
34
|
+
You may manually validate a resource using the valid? method, which will return true if the resource is valid, and false if it is invalid.
|
35
|
+
|
36
|
+
== Working with Validation Errors
|
37
|
+
|
38
|
+
If your validators find errors in your model, they will populate the DataMapper::Validation::ViolationSet object that is available through each of your models via calls to your model's errors method.
|
39
|
+
|
40
|
+
For example:
|
41
|
+
|
42
|
+
my_account = Account.new(:name => "Jose")
|
43
|
+
if my_account.save
|
44
|
+
# my_account is valid and has been saved
|
45
|
+
else
|
46
|
+
my_account.errors.each do |e|
|
47
|
+
puts e
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
See DataMapper::Validation::ViolationSet for all you can do with your model's
|
52
|
+
errors method.
|
53
|
+
|
54
|
+
== Contextual Validation
|
55
|
+
|
56
|
+
DataMapper Validation also provide a means of grouping your validations into
|
57
|
+
contexts. This enables you to run different sets of validations when you
|
58
|
+
need it. For instance, the same model may not only behave differently
|
59
|
+
when initially saved or saved on update, but also require special validation sets
|
60
|
+
for publishing, exporting, importing and so on.
|
61
|
+
|
62
|
+
Again, using our example for pure Ruby class validations:
|
63
|
+
|
64
|
+
class ProgrammingLanguage
|
65
|
+
|
66
|
+
include DataMapper::Resource
|
67
|
+
|
68
|
+
property :name, String
|
69
|
+
|
70
|
+
def ensure_allows_manual_memory_management
|
71
|
+
# ...
|
72
|
+
end
|
73
|
+
|
74
|
+
def ensure_allows_optional_parentheses
|
75
|
+
# ...
|
76
|
+
end
|
77
|
+
|
78
|
+
validates_presence_of :name
|
79
|
+
validates_with_method :ensure_allows_optional_parentheses, :when => [:implementing_a_dsl]
|
80
|
+
validates_with_method :ensure_allows_manual_memory_management, :when => [:doing_system_programming]
|
81
|
+
end
|
82
|
+
|
83
|
+
ProgrammingLanguage instance now use #valid? method with one of two context symbols:
|
84
|
+
|
85
|
+
@ruby.valid?(:implementing_a_dsl) # => true
|
86
|
+
@ruby.valid?(:doing_system_programming) # => false
|
87
|
+
|
88
|
+
@c.valid?(:implementing_a_dsl) # => false
|
89
|
+
@c.valid?(:doing_system_programming) # => true
|
90
|
+
|
91
|
+
Each context causes different set of validations to be triggered. If you don't
|
92
|
+
specify a context using :when, :on or :group options (they are all aliases and do
|
93
|
+
the same thing), default context name is :default. When you do model.valid? (without
|
94
|
+
specifying context explicitly), again, :default context is used. One validation
|
95
|
+
can be used in two, three or five contexts if you like:
|
96
|
+
|
97
|
+
class Book
|
98
|
+
|
99
|
+
include ::DataMapper::Resource
|
100
|
+
|
101
|
+
property :id, Serial
|
102
|
+
property :name, String
|
103
|
+
|
104
|
+
property :agreed_title, String
|
105
|
+
property :finished_toc, Boolean
|
106
|
+
|
107
|
+
# used in all contexts, including default
|
108
|
+
validates_presence_of :name, :when => [:default, :sending_to_print]
|
109
|
+
validates_presence_of :agreed_title, :when => [:sending_to_print]
|
110
|
+
|
111
|
+
validates_with_block :toc, :when => [:sending_to_print] do
|
112
|
+
if self.finished_toc
|
113
|
+
[true]
|
114
|
+
else
|
115
|
+
[false, "TOC must be finalized before you send a book to print"]
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
In the example above, name is validated for presence in both :default context and
|
121
|
+
:sending_to_print context, while TOC related block validation and title presence validation
|
122
|
+
only take place in :sending_to_print context.
|
data/Rakefile
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require File.expand_path('../lib/data_mapper/validation/version', __FILE__)
|
2
|
+
|
3
|
+
Gem::Specification.new do |gem|
|
4
|
+
gem.authors = ['Guy van den Berg', 'Emmanuel Gomez']
|
5
|
+
gem.email = ['emmanuel.gomez@gmail.com']
|
6
|
+
gem.summary = 'Library for performing validations on DataMapper resources and plain Ruby objects'
|
7
|
+
gem.description = 'This is a DataMapper plugin that provides validations for DataMapper model classes.'
|
8
|
+
gem.homepage = 'https://datamapper.org'
|
9
|
+
gem.license = 'Nonstandard'
|
10
|
+
|
11
|
+
gem.files = `git ls-files`.split("\n")
|
12
|
+
gem.extra_rdoc_files = %w(LICENSE README.rdoc)
|
13
|
+
|
14
|
+
gem.name = 'sbf-dm-validations'
|
15
|
+
gem.require_paths = ['lib']
|
16
|
+
gem.version = DataMapper::Validation::VERSION
|
17
|
+
gem.required_ruby_version = '>= 2.7.8'
|
18
|
+
|
19
|
+
gem.add_runtime_dependency('sbf-dm-core', '~> 1.3.0.beta')
|
20
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'dm-core'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'dm-core/support/assertions'
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'dm-core/support/equalizer'
|
@@ -0,0 +1,205 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Validation
|
3
|
+
|
4
|
+
# Alias for validate(:default)
|
5
|
+
#
|
6
|
+
# @api public
|
7
|
+
def valid_for_default?
|
8
|
+
# warn "#{self.class}#valid_for_default? is deprecated and will be removed in a future version (#{caller[0]})"
|
9
|
+
valid?(:default)
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
extend Deprecate
|
14
|
+
|
15
|
+
# deprecate :validators, :validation_rules
|
16
|
+
|
17
|
+
# This is a widely used API, wait a little before issuing warnings
|
18
|
+
def validators
|
19
|
+
validation_rules
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
class ViolationSet
|
25
|
+
extend Deprecate
|
26
|
+
|
27
|
+
deprecate :clear!, :clear
|
28
|
+
deprecate :errors, :violations
|
29
|
+
|
30
|
+
def self.default_error_messages=(error_messages)
|
31
|
+
MessageTransformer::Default.error_messages = error_messages
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.default_error_message(violation_type, attribute_name, *violation_data)
|
35
|
+
MessageTransformer::Default.error_message(violation_type, attribute_name, *violation_data)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class ContextualRuleSet
|
40
|
+
extend Deprecate
|
41
|
+
|
42
|
+
deprecate :contexts, :rule_sets
|
43
|
+
deprecate :clear!, :clear
|
44
|
+
|
45
|
+
def execute(context_name, resource)
|
46
|
+
# warn "#{self.class}#execute is deprecated. Use #{self.class}#validate instead."
|
47
|
+
context(context_name).execute(resource)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Given a new context create an instance method of
|
51
|
+
# valid_for_<context>? which simply calls validate(context)
|
52
|
+
# if it does not already exist
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
def self.create_context_instance_methods(model, context)
|
56
|
+
name = "valid_for_#{context}?"
|
57
|
+
present = model.respond_to?(:resource_method_defined) ? model.resource_method_defined?(name) : model.instance_methods.include?(name)
|
58
|
+
unless present
|
59
|
+
model.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
60
|
+
def #{name} # def valid_for_signup?
|
61
|
+
# warn "\#{self.class}##{name} is deprecated. Use #valid?(context_name) instead (\#{caller[0]})"
|
62
|
+
valid?(:#{context}) # valid?(:signup)
|
63
|
+
end # end
|
64
|
+
RUBY
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
class Rule
|
71
|
+
extend Deprecate
|
72
|
+
|
73
|
+
deprecate :field_name, :attribute_name
|
74
|
+
|
75
|
+
def humanized_field_name
|
76
|
+
# warn "#{self.class}#humanized_field_name is deprecated and will be removed in a future version (#{caller[0]})"
|
77
|
+
DataMapper::Inflector.humanize(attribute_name)
|
78
|
+
end
|
79
|
+
|
80
|
+
# Call the validator. "call" is used so the operation is BoundMethod
|
81
|
+
# and Block compatible. This must be implemented in all concrete
|
82
|
+
# classes.
|
83
|
+
#
|
84
|
+
# @param [Object] resource
|
85
|
+
# The resource that the validator must be called against.
|
86
|
+
#
|
87
|
+
# @return [Boolean]
|
88
|
+
# true if valid, otherwise false.
|
89
|
+
#
|
90
|
+
def call(resource)
|
91
|
+
# warn "#{self.class}#call is deprecated and will be removed in a future version (#{caller[0]})"
|
92
|
+
return true if valid?(resource)
|
93
|
+
|
94
|
+
error_message = self.custom_message ||
|
95
|
+
MessageTransformer::Default.error_message(
|
96
|
+
violation_type(resource),
|
97
|
+
attribute_name,
|
98
|
+
*violation_values(resource))
|
99
|
+
|
100
|
+
add_error(resource, error_message, attribute_name)
|
101
|
+
|
102
|
+
false
|
103
|
+
end
|
104
|
+
|
105
|
+
class Block
|
106
|
+
def call(resource)
|
107
|
+
# warn "#{self.class}#call is deprecated and will be removed in a future version (#{caller[0]})"
|
108
|
+
result, error_message = resource.instance_eval(&self.block)
|
109
|
+
add_error(resource, error_message, attribute_name) unless result
|
110
|
+
result
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class Method
|
115
|
+
def call(resource)
|
116
|
+
# warn "#{self.class}#call is deprecated and will be removed in a future version (#{caller[0]})"
|
117
|
+
result, error_message = resource.__send__(method)
|
118
|
+
add_error(resource, error_message, attribute_name) unless result
|
119
|
+
result
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
124
|
+
|
125
|
+
class RuleSet
|
126
|
+
extend Deprecate
|
127
|
+
|
128
|
+
# This is present to provide a backwards-compatible codepath to
|
129
|
+
# ContextualRuleSet#execute
|
130
|
+
def execute(resource)
|
131
|
+
rules = rules_for_resource(resource)
|
132
|
+
rules.map { |rule| rule.call(resource) }.all?
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
class Violation
|
137
|
+
# TODO: Extract the correct custom message for a Rule's context
|
138
|
+
# (in ContextualRuleSet#add). That change will break this interface.
|
139
|
+
def [](context_name)
|
140
|
+
# warn "Accessing custom messages by context name will be removed in a future version (#{caller[0]})"
|
141
|
+
@custom_message[context_name]
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
module Macros
|
146
|
+
extend Deprecate
|
147
|
+
|
148
|
+
deprecate :validates_absent, :validates_absence_of
|
149
|
+
deprecate :validates_format, :validates_format_of
|
150
|
+
deprecate :validates_present, :validates_presence_of
|
151
|
+
deprecate :validates_length, :validates_length_of
|
152
|
+
deprecate :validates_is_accepted, :validates_acceptance_of
|
153
|
+
deprecate :validates_is_confirmed, :validates_confirmation_of
|
154
|
+
deprecate :validates_is_number, :validates_numericality_of
|
155
|
+
deprecate :validates_is_primitive, :validates_primitive_type_of
|
156
|
+
deprecate :validates_is_unique, :validates_uniqueness_of
|
157
|
+
|
158
|
+
def validates_numericality_of(*attribute_names)
|
159
|
+
# warn "'Numericality' is not a word in the English language, please use validates_numericalness_of (#{caller[0]})"
|
160
|
+
options = attribute_names.last.kind_of?(Hash) ? attribute_names.pop : {}
|
161
|
+
validation_rules.add(Rule::Numericalness, attribute_names, options)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
module Inferred
|
166
|
+
extend Deprecate
|
167
|
+
|
168
|
+
# TODO: why are there 3 entry points to this ivar?
|
169
|
+
# #disable_auto_validations, #disabled_auto_validations?, #auto_validations_disabled?
|
170
|
+
# def disable_auto_validations
|
171
|
+
# !infer_validations?
|
172
|
+
# end
|
173
|
+
|
174
|
+
# Checks whether auto validations are currently
|
175
|
+
# disabled (see +disable_auto_validations+ method
|
176
|
+
# that takes a block)
|
177
|
+
#
|
178
|
+
# @return [TrueClass, FalseClass]
|
179
|
+
# true if auto validation is currently disabled
|
180
|
+
#
|
181
|
+
# @api semipublic
|
182
|
+
# def disabled_auto_validations?
|
183
|
+
# !infer_validations?
|
184
|
+
# end
|
185
|
+
|
186
|
+
# deprecate :auto_validations_disabled?, :infer_validations?
|
187
|
+
# deprecate :without_auto_validations, :without_inferred_validations
|
188
|
+
|
189
|
+
end # module Inferred
|
190
|
+
|
191
|
+
AutoValidations = Inferred
|
192
|
+
ValidationErrors = ViolationSet
|
193
|
+
ContextualValidators = ContextualRuleSet
|
194
|
+
|
195
|
+
end # module Validation
|
196
|
+
|
197
|
+
# Previously used similarly to Violation (I believe)
|
198
|
+
class ValidationError < StandardError; end
|
199
|
+
|
200
|
+
# Previous top-level namespace (1.0-1.1)
|
201
|
+
Validations = Validation
|
202
|
+
# Very old constant name (0.9?)
|
203
|
+
Validate = Validation
|
204
|
+
|
205
|
+
end # module DataMapper
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
module DataMapper
|
4
|
+
module Validation
|
5
|
+
|
6
|
+
module Context
|
7
|
+
# Module with validation context functionality.
|
8
|
+
#
|
9
|
+
# Contexts are implemented using a thread-local array-based stack.
|
10
|
+
|
11
|
+
|
12
|
+
# Execute a block of code within a specific validation context
|
13
|
+
#
|
14
|
+
# @param [Symbol] context
|
15
|
+
# the context to execute the block of code within
|
16
|
+
#
|
17
|
+
# @api semipublic
|
18
|
+
def self.in_context(context)
|
19
|
+
stack << context
|
20
|
+
return_value = yield
|
21
|
+
ensure
|
22
|
+
stack.pop
|
23
|
+
return_value
|
24
|
+
end
|
25
|
+
|
26
|
+
# Get the current validation context or nil (if no context is on the stack).
|
27
|
+
#
|
28
|
+
# @return [Symbol, NilClass]
|
29
|
+
# The current validation context (for the current thread),
|
30
|
+
# or nil if no current context is on the stack
|
31
|
+
def self.current
|
32
|
+
stack.last
|
33
|
+
end
|
34
|
+
|
35
|
+
# Are there any contexts on the stack?
|
36
|
+
#
|
37
|
+
# @return [Boolean]
|
38
|
+
# true/false whether there are any contexts on the context stack
|
39
|
+
#
|
40
|
+
# @api semipublic
|
41
|
+
def self.any?(&block)
|
42
|
+
stack.any?(&block)
|
43
|
+
end
|
44
|
+
|
45
|
+
# The (thread-local) validation context stack
|
46
|
+
# This allows object graphs to be saved within potentially nested contexts
|
47
|
+
# without having to pass the validation context throughout
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
def self.stack
|
51
|
+
Thread.current[:dm_validations_context_stack] ||= []
|
52
|
+
end
|
53
|
+
|
54
|
+
end # module Context
|
55
|
+
|
56
|
+
end # module Validation
|
57
|
+
end # module DataMapper
|
@@ -0,0 +1,210 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
require 'data_mapper/validation/exceptions'
|
5
|
+
require 'data_mapper/validation/context'
|
6
|
+
require 'data_mapper/validation/rule_set'
|
7
|
+
|
8
|
+
module DataMapper
|
9
|
+
module Validation
|
10
|
+
|
11
|
+
class ContextualRuleSet
|
12
|
+
extend Forwardable
|
13
|
+
include Enumerable
|
14
|
+
|
15
|
+
# MessageTransformer to use for transforming Violations on Resources
|
16
|
+
# instantiated from the model to which this ContextualRuleSet is bound
|
17
|
+
#
|
18
|
+
# @api public
|
19
|
+
attr_accessor :transformer
|
20
|
+
|
21
|
+
# @api private
|
22
|
+
attr_reader :rule_sets
|
23
|
+
|
24
|
+
# Whether to optimize the execution of validators for this model's resources
|
25
|
+
#
|
26
|
+
# @api public
|
27
|
+
attr_reader :optimize
|
28
|
+
|
29
|
+
def_delegators :rule_sets, :each, :empty?
|
30
|
+
|
31
|
+
# Clear all named context rule sets
|
32
|
+
#
|
33
|
+
# @api public
|
34
|
+
def_delegators :rule_sets, :clear
|
35
|
+
|
36
|
+
def initialize(model = nil)
|
37
|
+
@model = model
|
38
|
+
@rule_sets = Hash.new { |h, context_name| h[context_name] = RuleSet.new }
|
39
|
+
end
|
40
|
+
|
41
|
+
# Delegate #validate to RuleSet
|
42
|
+
#
|
43
|
+
# @api public
|
44
|
+
def validate(resource, context_name)
|
45
|
+
context(context_name).validate(resource)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Return the RuleSet for a given context name
|
49
|
+
#
|
50
|
+
# @param [String] name
|
51
|
+
# Context name for which to return a RuleSet
|
52
|
+
# @return [RuleSet]
|
53
|
+
# RuleSet for the given context
|
54
|
+
#
|
55
|
+
# @api public
|
56
|
+
def context(context_name)
|
57
|
+
rule_sets[context_name]
|
58
|
+
end
|
59
|
+
|
60
|
+
# Retrieve Rules applicable to a given attribute name
|
61
|
+
#
|
62
|
+
# @param [Symbol] attribute_name
|
63
|
+
# name of the attribute for which to retrieve applicable Rules
|
64
|
+
#
|
65
|
+
# @return [Array]
|
66
|
+
# list of Rules applicable to +attribute_name+
|
67
|
+
def [](attribute_name)
|
68
|
+
context(:default).fetch(attribute_name, [])
|
69
|
+
end
|
70
|
+
|
71
|
+
# Create a new rule of the given class for each name in +attribute_names+
|
72
|
+
# and add the rules to the RuleSet(s) indicated
|
73
|
+
#
|
74
|
+
# @param [DataMapper::Validation::Rule] rule_class
|
75
|
+
# Rule class, example: DataMapper::Validation::Rule::Presence
|
76
|
+
#
|
77
|
+
# @param [Array<Symbol>] attribute_names
|
78
|
+
# Attribute names given to validation macro, example:
|
79
|
+
# [:first_name, :last_name] in validates_presence_of :first_name, :last_name
|
80
|
+
#
|
81
|
+
# @param [Hash] options
|
82
|
+
# Options supplied to validation macro, example:
|
83
|
+
# {:context=>:default, :maximum=>50, :allow_nil=>true, :message=>nil}
|
84
|
+
#
|
85
|
+
# @option [Symbol] :context
|
86
|
+
# the context in which the new rule should be run
|
87
|
+
# @option [Boolean] :allow_nil
|
88
|
+
# whether or not the new rule should allow nil values
|
89
|
+
# @option [Boolean] :message
|
90
|
+
# the error message the new rule will provide on validation failure
|
91
|
+
#
|
92
|
+
# @return [ContextualRuleSet]
|
93
|
+
# This method is a command, thus returns the receiver
|
94
|
+
def add(rule_class, attribute_names, options = {}, &block)
|
95
|
+
context_names = extract_context_names(options)
|
96
|
+
|
97
|
+
attribute_names.each do |attribute_name|
|
98
|
+
rules = rule_class.rules_for(attribute_name, options, &block)
|
99
|
+
|
100
|
+
context_names.each { |context| context(context).concat(rules) }
|
101
|
+
end
|
102
|
+
|
103
|
+
# TODO: remove this shortcut, then eliminate the @model ivar entirely
|
104
|
+
context_names.each do |context|
|
105
|
+
ContextualRuleSet.create_context_instance_methods(@model, context) if @model
|
106
|
+
end
|
107
|
+
|
108
|
+
self
|
109
|
+
end
|
110
|
+
|
111
|
+
# Assimilate all rules contained in +other+ into the receiver
|
112
|
+
#
|
113
|
+
# @param [ContextualRuleSet] other
|
114
|
+
# the ContextualRuleSet whose rules are to be assimilated
|
115
|
+
#
|
116
|
+
# @return [ContextualRuleSet]
|
117
|
+
# +self+, the receiver
|
118
|
+
def concat(other)
|
119
|
+
other.rule_sets.each do |context_name, rule_set|
|
120
|
+
context(context_name).concat(rule_set)
|
121
|
+
end
|
122
|
+
self
|
123
|
+
end
|
124
|
+
|
125
|
+
def optimize=(new_value)
|
126
|
+
@optimize = new_value
|
127
|
+
rule_sets.each { |rule_set| rule_set.optimize = self.optimize }
|
128
|
+
new_value
|
129
|
+
end
|
130
|
+
|
131
|
+
# Returns the current validation context on the stack if valid for this model,
|
132
|
+
# nil if no RuleSets are defined for the model (and no context names are on
|
133
|
+
# the validation stack), or :default if the current context is invalid for
|
134
|
+
# this model or no contexts have been defined for this model and
|
135
|
+
# no context name is on the stack.
|
136
|
+
#
|
137
|
+
# @return [Symbol]
|
138
|
+
# the current validation context from the stack (if valid for this model),
|
139
|
+
# nil if no context name is on the stack and no contexts are defined for
|
140
|
+
# this model, or :default if the context on the stack is invalid for
|
141
|
+
# this model or no context is on the stack and this model has at least
|
142
|
+
# one validation context
|
143
|
+
#
|
144
|
+
# @api private
|
145
|
+
#
|
146
|
+
# TODO: simplify the semantics of #current_context, #validate
|
147
|
+
def current_context
|
148
|
+
context = Validation::Context.current
|
149
|
+
valid_context?(context) ? context : :default
|
150
|
+
end
|
151
|
+
|
152
|
+
# Test if the context is valid for the model
|
153
|
+
#
|
154
|
+
# @param [Symbol] context
|
155
|
+
# the context to test
|
156
|
+
#
|
157
|
+
# @return [Boolean]
|
158
|
+
# true if the context is valid for the model
|
159
|
+
#
|
160
|
+
# @api private
|
161
|
+
def valid_context?(context_name)
|
162
|
+
!context_name.nil? &&
|
163
|
+
(rule_sets.empty? || rule_sets.include?(context_name))
|
164
|
+
end
|
165
|
+
|
166
|
+
# Assert that the given context is valid for this model
|
167
|
+
#
|
168
|
+
# @param [Symbol] context
|
169
|
+
# the context to test
|
170
|
+
#
|
171
|
+
# @raise [InvalidContextError]
|
172
|
+
# raised if the context is not valid for this model
|
173
|
+
#
|
174
|
+
# @api private
|
175
|
+
#
|
176
|
+
# TODO: is this method actually needed?
|
177
|
+
def assert_valid_context(context_name)
|
178
|
+
unless valid_context?(context_name)
|
179
|
+
actual = context_name.inspect
|
180
|
+
expected = rule_sets.keys.inspect
|
181
|
+
raise InvalidContextError, "#{actual} is an invalid context, known contexts are #{expected}"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
private
|
186
|
+
|
187
|
+
# Allow :context to be aliased to :group, :when & :on
|
188
|
+
#
|
189
|
+
# @param [Hash] options
|
190
|
+
# the options from which +context_names+ is to be extracted
|
191
|
+
#
|
192
|
+
# @return [Array(Symbol)]
|
193
|
+
# the context name(s) from +options+
|
194
|
+
#
|
195
|
+
# @api private
|
196
|
+
def extract_context_names(options)
|
197
|
+
context_names = [
|
198
|
+
options.delete(:context),
|
199
|
+
options.delete(:group),
|
200
|
+
options.delete(:when),
|
201
|
+
options.delete(:on)
|
202
|
+
].compact.first
|
203
|
+
|
204
|
+
Array(context_names || :default)
|
205
|
+
end
|
206
|
+
|
207
|
+
end # class ContextualRuleSet
|
208
|
+
|
209
|
+
end # module Validation
|
210
|
+
end # module DataMapper
|