dm-validations 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/LICENSE +20 -0
  2. data/README +72 -0
  3. data/Rakefile +74 -0
  4. data/TODO +16 -0
  5. data/lib/dm-validations.rb +208 -0
  6. data/lib/dm-validations/absent_field_validator.rb +60 -0
  7. data/lib/dm-validations/acceptance_validator.rb +76 -0
  8. data/lib/dm-validations/auto_validate.rb +98 -0
  9. data/lib/dm-validations/confirmation_validator.rb +76 -0
  10. data/lib/dm-validations/contextual_validators.rb +56 -0
  11. data/lib/dm-validations/custom_validator.rb +72 -0
  12. data/lib/dm-validations/format_validator.rb +94 -0
  13. data/lib/dm-validations/formats/email.rb +40 -0
  14. data/lib/dm-validations/generic_validator.rb +92 -0
  15. data/lib/dm-validations/length_validator.rb +113 -0
  16. data/lib/dm-validations/method_validator.rb +58 -0
  17. data/lib/dm-validations/numeric_validator.rb +66 -0
  18. data/lib/dm-validations/primitive_validator.rb +60 -0
  19. data/lib/dm-validations/required_field_validator.rb +88 -0
  20. data/lib/dm-validations/support/object.rb +5 -0
  21. data/lib/dm-validations/uniqueness_validator.rb +61 -0
  22. data/lib/dm-validations/validation_errors.rb +63 -0
  23. data/lib/dm-validations/within_validator.rb +41 -0
  24. data/spec/integration/absent_field_validator_spec.rb +34 -0
  25. data/spec/integration/acceptance_validator_spec.rb +87 -0
  26. data/spec/integration/auto_validate_spec.rb +262 -0
  27. data/spec/integration/confirmation_validator_spec.rb +66 -0
  28. data/spec/integration/contextual_validators_spec.rb +28 -0
  29. data/spec/integration/custom_validator_spec.rb +9 -0
  30. data/spec/integration/format_validator_spec.rb +118 -0
  31. data/spec/integration/generic_validator_spec.rb +9 -0
  32. data/spec/integration/length_validator_spec.rb +113 -0
  33. data/spec/integration/method_validator_spec.rb +31 -0
  34. data/spec/integration/numeric_validator_spec.rb +192 -0
  35. data/spec/integration/primitive_validator_spec.rb +25 -0
  36. data/spec/integration/required_field_validator_spec.rb +93 -0
  37. data/spec/integration/uniqueness_validator_spec.rb +81 -0
  38. data/spec/integration/validation_errors_spec.rb +18 -0
  39. data/spec/integration/validation_spec.rb +339 -0
  40. data/spec/integration/within_validator_spec.rb +35 -0
  41. data/spec/spec.opts +2 -0
  42. data/spec/spec_helper.rb +26 -0
  43. 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