dm-validations 0.9.2
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/LICENSE +20 -0
- data/README +72 -0
- data/Rakefile +74 -0
- data/TODO +16 -0
- data/lib/dm-validations.rb +208 -0
- data/lib/dm-validations/absent_field_validator.rb +60 -0
- data/lib/dm-validations/acceptance_validator.rb +76 -0
- data/lib/dm-validations/auto_validate.rb +98 -0
- data/lib/dm-validations/confirmation_validator.rb +76 -0
- data/lib/dm-validations/contextual_validators.rb +56 -0
- data/lib/dm-validations/custom_validator.rb +72 -0
- data/lib/dm-validations/format_validator.rb +94 -0
- data/lib/dm-validations/formats/email.rb +40 -0
- data/lib/dm-validations/generic_validator.rb +92 -0
- data/lib/dm-validations/length_validator.rb +113 -0
- data/lib/dm-validations/method_validator.rb +58 -0
- data/lib/dm-validations/numeric_validator.rb +66 -0
- data/lib/dm-validations/primitive_validator.rb +60 -0
- data/lib/dm-validations/required_field_validator.rb +88 -0
- data/lib/dm-validations/support/object.rb +5 -0
- data/lib/dm-validations/uniqueness_validator.rb +61 -0
- data/lib/dm-validations/validation_errors.rb +63 -0
- data/lib/dm-validations/within_validator.rb +41 -0
- data/spec/integration/absent_field_validator_spec.rb +34 -0
- data/spec/integration/acceptance_validator_spec.rb +87 -0
- data/spec/integration/auto_validate_spec.rb +262 -0
- data/spec/integration/confirmation_validator_spec.rb +66 -0
- data/spec/integration/contextual_validators_spec.rb +28 -0
- data/spec/integration/custom_validator_spec.rb +9 -0
- data/spec/integration/format_validator_spec.rb +118 -0
- data/spec/integration/generic_validator_spec.rb +9 -0
- data/spec/integration/length_validator_spec.rb +113 -0
- data/spec/integration/method_validator_spec.rb +31 -0
- data/spec/integration/numeric_validator_spec.rb +192 -0
- data/spec/integration/primitive_validator_spec.rb +25 -0
- data/spec/integration/required_field_validator_spec.rb +93 -0
- data/spec/integration/uniqueness_validator_spec.rb +81 -0
- data/spec/integration/validation_errors_spec.rb +18 -0
- data/spec/integration/validation_spec.rb +339 -0
- data/spec/integration/within_validator_spec.rb +35 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +26 -0
- metadata +104 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2007 Guy van den Berg
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
This is a DataMapper plugin that provides validations for DataMapper model
|
2
|
+
classes.
|
3
|
+
|
4
|
+
== Setup
|
5
|
+
DataMapper validation capabilities are automatically available when you
|
6
|
+
'require dm-validations' into your application.
|
7
|
+
|
8
|
+
More specifically, DataMapper::Validate is automatically included into
|
9
|
+
DataMapper::Resource when you require dm-validations.
|
10
|
+
|
11
|
+
== Specifying Model Validations
|
12
|
+
There are two primary ways to implement validations for your models:
|
13
|
+
|
14
|
+
1) Placing validation methods with properties as params in your class
|
15
|
+
definitions like:
|
16
|
+
- validates_length :name
|
17
|
+
- validates_length [:name, :description]
|
18
|
+
|
19
|
+
2) Using auto-validations, please see DataMapper::Validate::AutoValidate
|
20
|
+
|
21
|
+
An example class with validations declared:
|
22
|
+
|
23
|
+
require 'dm-validations'
|
24
|
+
|
25
|
+
class Account
|
26
|
+
include DataMapper::Resource
|
27
|
+
|
28
|
+
property :name, String
|
29
|
+
validates_length :name
|
30
|
+
end
|
31
|
+
|
32
|
+
See all of the DataMapper::Validate module's XYZValidator(s) to learn about the
|
33
|
+
complete collections of validators available to you.
|
34
|
+
|
35
|
+
== Validating
|
36
|
+
DataMapper validations, when included, alter the default save/create/update
|
37
|
+
process for a model. Unless you specify a context the resource must be
|
38
|
+
valid in the :default context before saving.
|
39
|
+
|
40
|
+
You may manually validate a resource using the valid? method, which will
|
41
|
+
return true if the resource is valid, and false if it is invalid.
|
42
|
+
|
43
|
+
In addition to the valid? method, there is also an all_valid? method that
|
44
|
+
recursively walks both the current object and its associated objects and returns
|
45
|
+
its true/false result for the entire walk.
|
46
|
+
|
47
|
+
== Working with Validation Errors
|
48
|
+
If your validators find errors in your model, they will populate the
|
49
|
+
DataMapper::Validate::ValidationErrors object that is available through each of
|
50
|
+
your models via calls to your model's errors method.
|
51
|
+
|
52
|
+
For example:
|
53
|
+
|
54
|
+
my_account = Account.new(:name => "Jose")
|
55
|
+
if my_account.save
|
56
|
+
# my_account is valid and has been saved
|
57
|
+
else
|
58
|
+
my_account.errors.each do |e|
|
59
|
+
puts e
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
See DataMapper::Validate::ValidationErrors for all you can do with your model's
|
64
|
+
errors method.
|
65
|
+
|
66
|
+
== Contextual Validations
|
67
|
+
|
68
|
+
DataMapper Validations also provide a means of grouping your validations into
|
69
|
+
contexts. This enables you to run different sets of validations under ...
|
70
|
+
different contexts.
|
71
|
+
|
72
|
+
TO BE ADDED... For now, see
|
data/Rakefile
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'spec'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
require 'spec/rake/spectask'
|
7
|
+
require 'pathname'
|
8
|
+
|
9
|
+
CLEAN.include '{log,pkg}/'
|
10
|
+
|
11
|
+
desc "Generate Documentation"
|
12
|
+
rd = Rake::RDocTask.new do |rdoc|
|
13
|
+
rdoc.rdoc_dir = 'doc'
|
14
|
+
rdoc.title = "DataMapper Validations"
|
15
|
+
rdoc.options << '--line-numbers' << '--inline-source' << '--main' << 'README'
|
16
|
+
rdoc.rdoc_files.include(FileList[ 'lib/**/*.rb', 'README', 'LICENSE'])
|
17
|
+
end
|
18
|
+
|
19
|
+
spec = Gem::Specification.new do |s|
|
20
|
+
s.name = 'dm-validations'
|
21
|
+
s.version = '0.9.2'
|
22
|
+
s.platform = Gem::Platform::RUBY
|
23
|
+
s.has_rdoc = true
|
24
|
+
s.extra_rdoc_files = %w[ README LICENSE TODO ]
|
25
|
+
s.summary = 'DataMapper plugin for performing validations on data models'
|
26
|
+
s.description = s.summary
|
27
|
+
s.author = 'Guy van den Berg'
|
28
|
+
s.email = 'vandenberg.guy@gmail.com'
|
29
|
+
s.homepage = 'http://github.com/sam/dm-more/tree/master/dm-validations'
|
30
|
+
s.require_path = 'lib'
|
31
|
+
s.files = FileList[ '{lib,spec}/**/*.rb', 'spec/spec.opts', 'Rakefile', *s.extra_rdoc_files ]
|
32
|
+
s.add_dependency('dm-core', "=#{s.version}")
|
33
|
+
end
|
34
|
+
|
35
|
+
task :default => [ :spec ]
|
36
|
+
|
37
|
+
WIN32 = (RUBY_PLATFORM =~ /win32|mingw|cygwin/) rescue nil
|
38
|
+
SUDO = WIN32 ? '' : ('sudo' unless ENV['SUDOLESS'])
|
39
|
+
|
40
|
+
Rake::GemPackageTask.new(spec) do |pkg|
|
41
|
+
pkg.gem_spec = spec
|
42
|
+
end
|
43
|
+
|
44
|
+
desc "Install #{spec.name} #{spec.version} (default ruby)"
|
45
|
+
task :install => [ :package ] do
|
46
|
+
sh "#{SUDO} gem install --local pkg/#{spec.name}-#{spec.version} --no-update-sources", :verbose => false
|
47
|
+
end
|
48
|
+
|
49
|
+
desc "Uninstall #{spec.name} #{spec.version} (default ruby)"
|
50
|
+
task :uninstall => [ :clobber ] do
|
51
|
+
sh "#{SUDO} gem uninstall #{spec.name} -v#{spec.version} -I -x", :verbose => false
|
52
|
+
end
|
53
|
+
|
54
|
+
namespace :jruby do
|
55
|
+
desc "Install #{spec.name} #{spec.version} with JRuby"
|
56
|
+
task :install => [ :package ] do
|
57
|
+
sh %{#{SUDO} jruby -S gem install --local pkg/#{spec.name}-#{spec.version} --no-update-sources}, :verbose => false
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
desc 'Run specifications'
|
62
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
63
|
+
t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
|
64
|
+
t.spec_files = Pathname.glob(Pathname.new(__FILE__).dirname + 'spec/**/*_spec.rb')
|
65
|
+
|
66
|
+
begin
|
67
|
+
t.rcov = ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true
|
68
|
+
t.rcov_opts << '--exclude' << 'spec'
|
69
|
+
t.rcov_opts << '--text-summary'
|
70
|
+
t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
|
71
|
+
rescue Exception
|
72
|
+
# rcov not installed
|
73
|
+
end
|
74
|
+
end
|
data/TODO
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
Short TODO list
|
2
|
+
===============
|
3
|
+
|
4
|
+
* Add logic_validator -- validates(“You need more money!”) { |instance| instance.money < 1_000_000 }
|
5
|
+
* Allow for the translation of strings in messages
|
6
|
+
* Add the canned RFC2822 email format to format_validator
|
7
|
+
* Add a validates_acceptance_of validator
|
8
|
+
* Add a validates_each validator
|
9
|
+
* Add a validates_true validator
|
10
|
+
|
11
|
+
* Validators do not accept associations except validates_uniqueness_of - fix this
|
12
|
+
* Add auto validation on cardinality of associations
|
13
|
+
|
14
|
+
---
|
15
|
+
TODO tickets may also be found in the DataMapper Issue Tracker:
|
16
|
+
http://wm.lighthouseapp.com/projects/4819-datamapper/overview
|
@@ -0,0 +1,208 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'pathname'
|
3
|
+
|
4
|
+
gem 'dm-core', '=0.9.2'
|
5
|
+
require 'dm-core'
|
6
|
+
|
7
|
+
dir = Pathname(__FILE__).dirname.expand_path / 'dm-validations'
|
8
|
+
|
9
|
+
require dir / 'validation_errors'
|
10
|
+
require dir / 'contextual_validators'
|
11
|
+
require dir / 'auto_validate'
|
12
|
+
|
13
|
+
require dir / 'generic_validator'
|
14
|
+
require dir / 'required_field_validator'
|
15
|
+
require dir / 'primitive_validator'
|
16
|
+
require dir / 'absent_field_validator'
|
17
|
+
require dir / 'confirmation_validator'
|
18
|
+
require dir / 'format_validator'
|
19
|
+
require dir / 'length_validator'
|
20
|
+
require dir / 'within_validator'
|
21
|
+
require dir / 'numeric_validator'
|
22
|
+
require dir / 'method_validator'
|
23
|
+
require dir / 'uniqueness_validator'
|
24
|
+
require dir / 'acceptance_validator'
|
25
|
+
require dir / 'custom_validator'
|
26
|
+
|
27
|
+
require dir / 'support' / 'object'
|
28
|
+
|
29
|
+
module DataMapper
|
30
|
+
module Validate
|
31
|
+
|
32
|
+
def self.included(model)
|
33
|
+
if model.method_defined?(:save) && !model.method_defined?(:save!)
|
34
|
+
model.send(:alias_method, :save!, :save)
|
35
|
+
model.send(:alias_method, :save, :save_with_validations)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
# Validate the resource before saving. Use #save! to save
|
40
|
+
# the record without validations.
|
41
|
+
#
|
42
|
+
def save_with_validations(context = :default)
|
43
|
+
return false unless valid?(context)
|
44
|
+
save!
|
45
|
+
end
|
46
|
+
|
47
|
+
# Return the ValidationErrors
|
48
|
+
#
|
49
|
+
def errors
|
50
|
+
@errors ||= ValidationErrors.new
|
51
|
+
end
|
52
|
+
|
53
|
+
# Mark this resource as validatable. When we validate associations of a
|
54
|
+
# resource we can check if they respond to validatable? before trying to
|
55
|
+
# recursivly validate them
|
56
|
+
#
|
57
|
+
def validatable?()
|
58
|
+
true
|
59
|
+
end
|
60
|
+
|
61
|
+
# Alias for valid?(:default)
|
62
|
+
#
|
63
|
+
def valid_for_default?
|
64
|
+
valid?(:default)
|
65
|
+
end
|
66
|
+
|
67
|
+
# Check if a resource is valid in a given context
|
68
|
+
#
|
69
|
+
def valid?(context = :default)
|
70
|
+
self.class.validators.execute(context,self)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Begin a recursive walk of the model checking validity
|
74
|
+
#
|
75
|
+
def all_valid?(context = :default)
|
76
|
+
recursive_valid?(self,context,true)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Do recursive validity checking
|
80
|
+
#
|
81
|
+
def recursive_valid?(target, context, state)
|
82
|
+
valid = state
|
83
|
+
target.instance_variables.each do |ivar|
|
84
|
+
ivar_value = target.instance_variable_get(ivar)
|
85
|
+
if ivar_value.validatable?
|
86
|
+
valid = valid && recursive_valid?(ivar_value,context,valid)
|
87
|
+
elsif ivar_value.respond_to?(:each)
|
88
|
+
ivar_value.each do |item|
|
89
|
+
if item.validatable?
|
90
|
+
valid = valid && recursive_valid?(item,context,valid)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
return valid && target.valid?
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
def validation_property_value(name)
|
100
|
+
return self.instance_variable_get("@#{name}") if self.instance_variables.include?(name)
|
101
|
+
return self.send(name) if self.respond_to?(name)
|
102
|
+
nil
|
103
|
+
end
|
104
|
+
|
105
|
+
# Get the corresponding Resource property, if it exists.
|
106
|
+
#
|
107
|
+
# Note: DataMapper validations can be used on non-DataMapper resources.
|
108
|
+
# In such cases, the return value will be nil.
|
109
|
+
def validation_property(field_name)
|
110
|
+
if DataMapper::Resource > self.class
|
111
|
+
self.class.properties(self.repository.name)[field_name]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
def validation_association_keys(name)
|
116
|
+
if self.class.relationships.has_key?(name)
|
117
|
+
result = []
|
118
|
+
relation = self.class.relationships[name]
|
119
|
+
relation.child_key.each do |key|
|
120
|
+
result << key.name
|
121
|
+
end
|
122
|
+
return result
|
123
|
+
end
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
|
127
|
+
module ClassMethods
|
128
|
+
include DataMapper::Validate::ValidatesPresent
|
129
|
+
include DataMapper::Validate::ValidatesAbsent
|
130
|
+
include DataMapper::Validate::ValidatesIsConfirmed
|
131
|
+
include DataMapper::Validate::ValidatesIsPrimitive
|
132
|
+
include DataMapper::Validate::ValidatesIsAccepted
|
133
|
+
include DataMapper::Validate::ValidatesFormat
|
134
|
+
include DataMapper::Validate::ValidatesLength
|
135
|
+
include DataMapper::Validate::ValidatesWithin
|
136
|
+
include DataMapper::Validate::ValidatesIsNumber
|
137
|
+
include DataMapper::Validate::ValidatesWithMethod
|
138
|
+
include DataMapper::Validate::ValidatesIsUnique
|
139
|
+
include DataMapper::Validate::AutoValidate
|
140
|
+
|
141
|
+
# Return the set of contextual validators or create a new one
|
142
|
+
#
|
143
|
+
def validators
|
144
|
+
@validations ||= ContextualValidators.new
|
145
|
+
end
|
146
|
+
|
147
|
+
# Clean up the argument list and return a opts hash, including the
|
148
|
+
# merging of any default opts. Set the context to default if none is
|
149
|
+
# provided. Also allow :context to be aliased to :on, :when & group
|
150
|
+
#
|
151
|
+
def opts_from_validator_args(args, defaults = nil)
|
152
|
+
opts = args.last.kind_of?(Hash) ? args.pop : {}
|
153
|
+
context = :default
|
154
|
+
context = opts[:context] if opts.has_key?(:context)
|
155
|
+
context = opts.delete(:on) if opts.has_key?(:on)
|
156
|
+
context = opts.delete(:when) if opts.has_key?(:when)
|
157
|
+
context = opts.delete(:group) if opts.has_key?(:group)
|
158
|
+
opts[:context] = context
|
159
|
+
opts.mergs!(defaults) unless defaults.nil?
|
160
|
+
opts
|
161
|
+
end
|
162
|
+
|
163
|
+
# Given a new context create an instance method of
|
164
|
+
# valid_for_<context>? which simply calls valid?(context)
|
165
|
+
# if it does not already exist
|
166
|
+
#
|
167
|
+
def create_context_instance_methods(context)
|
168
|
+
name = "valid_for_#{context.to_s}?"
|
169
|
+
if !self.instance_methods.include?(name)
|
170
|
+
class_eval <<-EOS
|
171
|
+
def #{name}
|
172
|
+
valid?('#{context.to_s}'.to_sym)
|
173
|
+
end
|
174
|
+
EOS
|
175
|
+
end
|
176
|
+
|
177
|
+
all = "all_valid_for_#{context.to_s}?"
|
178
|
+
if !self.instance_methods.include?(all)
|
179
|
+
class_eval <<-EOS
|
180
|
+
def #{all}
|
181
|
+
all_valid?('#{context.to_s}'.to_sym)
|
182
|
+
end
|
183
|
+
EOS
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Create a new validator of the given klazz and push it onto the
|
188
|
+
# requested context for each of the attributes in the fields list
|
189
|
+
#
|
190
|
+
def add_validator_to_context(opts, fields, klazz)
|
191
|
+
fields.each do |field|
|
192
|
+
if opts[:context].is_a?(Symbol)
|
193
|
+
validators.context(opts[:context]) << klazz.new(field, opts)
|
194
|
+
create_context_instance_methods(opts[:context])
|
195
|
+
elsif opts[:context].is_a?(Array)
|
196
|
+
opts[:context].each do |c|
|
197
|
+
validators.context(c) << klazz.new(field, opts)
|
198
|
+
create_context_instance_methods(c)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end # module ClassMethods
|
204
|
+
end # module Validate
|
205
|
+
|
206
|
+
Resource.append_inclusions Validate
|
207
|
+
Model.append_extensions Validate::ClassMethods
|
208
|
+
end # module DataMapper
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Validate
|
3
|
+
|
4
|
+
##
|
5
|
+
#
|
6
|
+
# @author Guy van den Berg
|
7
|
+
# @since 0.9
|
8
|
+
class AbsentFieldValidator < GenericValidator
|
9
|
+
|
10
|
+
def initialize(field_name, options={})
|
11
|
+
super
|
12
|
+
@field_name, @options = field_name, options
|
13
|
+
end
|
14
|
+
|
15
|
+
def call(target)
|
16
|
+
field_value = target.send(field_name).blank?
|
17
|
+
return true if field_value
|
18
|
+
|
19
|
+
error_message = @options[:message] || "%s must be absent".t(Extlib::Inflection.humanize(@field_name))
|
20
|
+
add_error(target, error_message , @field_name)
|
21
|
+
|
22
|
+
return false
|
23
|
+
end
|
24
|
+
end # class AbsentFieldValidator
|
25
|
+
|
26
|
+
module ValidatesAbsent
|
27
|
+
|
28
|
+
##
|
29
|
+
# Validates that the specified attribute is "blank" via the attribute's
|
30
|
+
# #blank? method.
|
31
|
+
#
|
32
|
+
# @note
|
33
|
+
# dm-core's support lib adds the #blank? method to many classes,
|
34
|
+
# @see lib/dm-core/support/blank.rb (dm-core) for more information.
|
35
|
+
#
|
36
|
+
# @example [Usage]
|
37
|
+
# require 'dm-validations'
|
38
|
+
#
|
39
|
+
# class Page
|
40
|
+
# include DataMapper::Resource
|
41
|
+
#
|
42
|
+
# property :unwanted_attribute, String
|
43
|
+
# property :another_unwanted, String
|
44
|
+
# property :yet_again, String
|
45
|
+
#
|
46
|
+
# validates_absent :unwanted_attribute
|
47
|
+
# validates_absent :another_unwanted, :yet_again
|
48
|
+
#
|
49
|
+
# # a call to valid? will return false unless
|
50
|
+
# # all three attributes are blank
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
def validates_absent(*fields)
|
54
|
+
opts = opts_from_validator_args(fields)
|
55
|
+
add_validator_to_context(opts, fields, DataMapper::Validate::AbsentFieldValidator)
|
56
|
+
end
|
57
|
+
|
58
|
+
end # module ValidatesAbsent
|
59
|
+
end # module Validate
|
60
|
+
end # module DataMapper
|