dm-validations 1.1.0 → 1.2.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +8 -8
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/dm-validations.gemspec +29 -139
- data/lib/dm-validations.rb +51 -103
- data/lib/dm-validations/auto_validate.rb +104 -61
- data/lib/dm-validations/context.rb +66 -0
- data/lib/dm-validations/contextual_validators.rb +164 -53
- data/lib/dm-validations/formats/email.rb +41 -23
- data/lib/dm-validations/formats/url.rb +6 -1
- data/lib/dm-validations/support/object.rb +13 -0
- data/lib/dm-validations/validation_errors.rb +27 -19
- data/lib/dm-validations/validators/absent_field_validator.rb +11 -11
- data/lib/dm-validations/validators/acceptance_validator.rb +15 -13
- data/lib/dm-validations/validators/block_validator.rb +12 -11
- data/lib/dm-validations/validators/confirmation_validator.rb +23 -16
- data/lib/dm-validations/validators/format_validator.rb +45 -23
- data/lib/dm-validations/validators/generic_validator.rb +85 -38
- data/lib/dm-validations/validators/length_validator.rb +61 -26
- data/lib/dm-validations/validators/method_validator.rb +13 -17
- data/lib/dm-validations/validators/numeric_validator.rb +35 -35
- data/lib/dm-validations/validators/primitive_validator.rb +11 -12
- data/lib/dm-validations/validators/required_field_validator.rb +11 -13
- data/lib/dm-validations/validators/uniqueness_validator.rb +15 -14
- data/lib/dm-validations/validators/within_validator.rb +30 -12
- data/spec/fixtures/bill_of_landing.rb +5 -0
- data/spec/fixtures/llama_spaceship.rb +15 -0
- data/spec/integration/automatic_validation/custom_messages_for_inferred_validation_spec.rb +10 -0
- data/spec/integration/automatic_validation/disabling_inferred_validation_spec.rb +8 -0
- data/spec/integration/automatic_validation/inferred_length_validation_spec.rb +8 -0
- data/spec/integration/automatic_validation/inferred_uniqueness_validation_spec.rb +6 -2
- data/spec/integration/automatic_validation/inferred_within_validation_spec.rb +6 -0
- data/spec/integration/datamapper_models/association_validation_spec.rb +5 -2
- data/spec/integration/dirty_attributes/dirty_attributes_spec.rb +13 -0
- data/spec/integration/format_validator/email_format_validator_spec.rb +13 -5
- data/spec/integration/format_validator/url_format_validator_spec.rb +28 -2
- data/spec/integration/required_field_validator/association_spec.rb +5 -1
- data/spec/public/resource_spec.rb +2 -2
- data/spec/spec_helper.rb +1 -1
- data/spec/unit/contextual_validators/emptiness_spec.rb +10 -10
- data/spec/unit/contextual_validators/execution_spec.rb +4 -4
- data/spec/unit/validators/within_validator_spec.rb +23 -0
- metadata +81 -146
- data/lib/dm-validations/support/context.rb +0 -93
@@ -1,14 +1,12 @@
|
|
1
1
|
module DataMapper
|
2
2
|
module Validations
|
3
|
-
|
4
|
-
##
|
5
|
-
#
|
6
3
|
# @author Dirkjan Bussink
|
7
4
|
# @since 0.9
|
8
5
|
class PrimitiveTypeValidator < GenericValidator
|
6
|
+
|
9
7
|
def call(target)
|
10
8
|
value = target.validation_property_value(field_name)
|
11
|
-
property = target
|
9
|
+
property = get_resource_property(target, field_name)
|
12
10
|
|
13
11
|
return true if value.nil? || property.primitive?(value)
|
14
12
|
|
@@ -21,19 +19,22 @@ module DataMapper
|
|
21
19
|
protected
|
22
20
|
|
23
21
|
def default_error(property)
|
24
|
-
ValidationErrors.default_error_message(
|
22
|
+
ValidationErrors.default_error_message(
|
23
|
+
:primitive,
|
24
|
+
field_name,
|
25
|
+
property.primitive
|
26
|
+
)
|
25
27
|
end
|
26
28
|
|
27
29
|
end # class PrimitiveTypeValidator
|
28
30
|
|
29
31
|
module ValidatesPrimitiveType
|
30
|
-
|
31
32
|
extend Deprecate
|
32
33
|
|
33
|
-
|
34
|
-
#
|
34
|
+
# Validates that the specified attribute is of the correct primitive
|
35
|
+
# type.
|
35
36
|
#
|
36
|
-
# @example
|
37
|
+
# @example Usage
|
37
38
|
# require 'dm-validations'
|
38
39
|
#
|
39
40
|
# class Person
|
@@ -48,12 +49,10 @@ module DataMapper
|
|
48
49
|
# # casted into a Date object.
|
49
50
|
# end
|
50
51
|
def validates_primitive_type_of(*fields)
|
51
|
-
|
52
|
-
add_validator_to_context(opts, fields, DataMapper::Validations::PrimitiveTypeValidator)
|
52
|
+
validators.add(PrimitiveTypeValidator, *fields)
|
53
53
|
end
|
54
54
|
|
55
55
|
deprecate :validates_is_primitive, :validates_primitive_type_of
|
56
|
-
|
57
56
|
end # module ValidatesPresent
|
58
57
|
end # module Validations
|
59
58
|
end # module DataMapper
|
@@ -1,14 +1,12 @@
|
|
1
1
|
module DataMapper
|
2
2
|
module Validations
|
3
|
-
|
4
|
-
##
|
5
|
-
#
|
6
3
|
# @author Guy van den Berg
|
7
4
|
# @since 0.9
|
8
5
|
class PresenceValidator < GenericValidator
|
6
|
+
|
9
7
|
def call(target)
|
10
|
-
value
|
11
|
-
property = target
|
8
|
+
value = target.validation_property_value(field_name)
|
9
|
+
property = get_resource_property(target, field_name)
|
12
10
|
return true if present?(value, property)
|
13
11
|
|
14
12
|
error_message = @options[:message] || default_error(property)
|
@@ -31,11 +29,12 @@ module DataMapper
|
|
31
29
|
ValidationErrors.default_error_message(actual, field_name)
|
32
30
|
end
|
33
31
|
|
34
|
-
# Is
|
32
|
+
# Is the property a boolean property?
|
35
33
|
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
# Returns false for
|
34
|
+
# @return [Boolean]
|
35
|
+
# Returns true for Boolean, ParanoidBoolean, TrueClass and other
|
36
|
+
# properties. Returns false for other property types or for
|
37
|
+
# non-properties.
|
39
38
|
def boolean_type?(property)
|
40
39
|
property ? property.primitive == TrueClass : false
|
41
40
|
end
|
@@ -55,9 +54,10 @@ module DataMapper
|
|
55
54
|
#
|
56
55
|
# @note
|
57
56
|
# dm-core's support lib adds the blank? method to many classes,
|
57
|
+
#
|
58
58
|
# @see lib/dm-core/support/blank.rb (dm-core) for more information.
|
59
59
|
#
|
60
|
-
# @example
|
60
|
+
# @example Usage
|
61
61
|
# require 'dm-validations'
|
62
62
|
#
|
63
63
|
# class Page
|
@@ -74,12 +74,10 @@ module DataMapper
|
|
74
74
|
# # all three attributes are !blank?
|
75
75
|
# end
|
76
76
|
def validates_presence_of(*fields)
|
77
|
-
|
78
|
-
add_validator_to_context(opts, fields, DataMapper::Validations::PresenceValidator)
|
77
|
+
validators.add(PresenceValidator, *fields)
|
79
78
|
end
|
80
79
|
|
81
80
|
deprecate :validates_present, :validates_presence_of
|
82
|
-
|
83
81
|
end # module ValidatesPresent
|
84
82
|
end # module Validations
|
85
83
|
end # module DataMapper
|
@@ -1,15 +1,16 @@
|
|
1
1
|
module DataMapper
|
2
2
|
module Validations
|
3
|
-
|
4
|
-
##
|
5
|
-
#
|
6
3
|
# @author Guy van den Berg
|
7
4
|
# @since 0.9
|
8
5
|
class UniquenessValidator < GenericValidator
|
6
|
+
|
9
7
|
include DataMapper::Assertions
|
10
8
|
|
11
9
|
def initialize(field_name, options = {})
|
12
|
-
|
10
|
+
if options.has_key?(:scope)
|
11
|
+
assert_kind_of('scope', options[:scope], Array, Symbol)
|
12
|
+
end
|
13
|
+
|
13
14
|
super
|
14
15
|
|
15
16
|
set_optional_by_default
|
@@ -33,20 +34,22 @@ module DataMapper
|
|
33
34
|
field_name => value,
|
34
35
|
}
|
35
36
|
|
36
|
-
Array(@options[:scope]).each {|subject|
|
37
|
-
|
38
|
-
|
39
|
-
else
|
40
|
-
raise ArgumentError, "Could not find property to scope by: #{subject}. Note that :unique does not currently support arbitrarily named groups, for that you should use :unique_index with an explicit validates_uniqueness_of."
|
37
|
+
Array(@options[:scope]).each { |subject|
|
38
|
+
unless target.respond_to?(subject)
|
39
|
+
raise(ArgumentError,"Could not find property to scope by: #{subject}. Note that :unique does not currently support arbitrarily named groups, for that you should use :unique_index with an explicit validates_uniqueness_of.")
|
41
40
|
end
|
41
|
+
|
42
|
+
opts[subject] = target.__send__(subject)
|
42
43
|
}
|
43
44
|
|
44
|
-
resource = DataMapper.repository(target.repository.name)
|
45
|
+
resource = DataMapper.repository(target.repository.name) do
|
46
|
+
target.model.first(opts)
|
47
|
+
end
|
45
48
|
|
46
49
|
return true if resource.nil?
|
47
|
-
|
48
50
|
target.saved? && resource.key == target.key
|
49
51
|
end
|
52
|
+
|
50
53
|
end # class UniquenessValidator
|
51
54
|
|
52
55
|
module ValidatesUniqueness
|
@@ -55,12 +58,10 @@ module DataMapper
|
|
55
58
|
# Validate the uniqueness of a field
|
56
59
|
#
|
57
60
|
def validates_uniqueness_of(*fields)
|
58
|
-
|
59
|
-
add_validator_to_context(opts, fields, DataMapper::Validations::UniquenessValidator)
|
61
|
+
validators.add(UniquenessValidator, *fields)
|
60
62
|
end
|
61
63
|
|
62
64
|
deprecate :validates_is_unique, :validates_uniqueness_of
|
63
|
-
|
64
65
|
end # module ValidatesIsUnique
|
65
66
|
end # module Validations
|
66
67
|
end # module DataMapper
|
@@ -1,8 +1,5 @@
|
|
1
1
|
module DataMapper
|
2
2
|
module Validations
|
3
|
-
|
4
|
-
##
|
5
|
-
#
|
6
3
|
# @author Guy van den Berg
|
7
4
|
# @since 0.9
|
8
5
|
class WithinValidator < GenericValidator
|
@@ -18,8 +15,10 @@ module DataMapper
|
|
18
15
|
return true if optional?(value)
|
19
16
|
return true if @options[:set].include?(value)
|
20
17
|
|
18
|
+
n = 1.0/0
|
21
19
|
set = @options[:set]
|
22
20
|
msg = @options[:message]
|
21
|
+
|
23
22
|
if set.is_a?(Range)
|
24
23
|
if set.first != -n && set.last != n
|
25
24
|
error_message = msg || ValidationErrors.default_error_message(:value_between, field_name, set.first, set.last)
|
@@ -29,7 +28,7 @@ module DataMapper
|
|
29
28
|
error_message = msg || ValidationErrors.default_error_message(:greater_than_or_equal_to, field_name, set.first)
|
30
29
|
end
|
31
30
|
else
|
32
|
-
error_message = msg || ValidationErrors.default_error_message(:inclusion, field_name, set.join(', '))
|
31
|
+
error_message = msg || ValidationErrors.default_error_message(:inclusion, field_name, set.to_a.join(', '))
|
33
32
|
end
|
34
33
|
|
35
34
|
add_error(target, error_message, field_name)
|
@@ -37,20 +36,39 @@ module DataMapper
|
|
37
36
|
false
|
38
37
|
end
|
39
38
|
|
40
|
-
|
41
|
-
1.0/0
|
42
|
-
end
|
39
|
+
|
43
40
|
end # class WithinValidator
|
44
41
|
|
45
42
|
module ValidatesWithin
|
46
|
-
|
47
|
-
#
|
43
|
+
##
|
44
|
+
# Validates that the value of a field is within a range/set.
|
45
|
+
#
|
46
|
+
# This validation is defined by passing a field along with a :set
|
47
|
+
# parameter. The :set can be a Range or any object which responds
|
48
|
+
# to the #include? method (an array, for example).
|
48
49
|
#
|
50
|
+
# @example Usage
|
51
|
+
# require 'dm-validations'
|
52
|
+
#
|
53
|
+
# class Review
|
54
|
+
# include DataMapper::Resource
|
55
|
+
#
|
56
|
+
# STATES = ['new', 'in_progress', 'published', 'archived']
|
57
|
+
#
|
58
|
+
# property :title, String
|
59
|
+
# property :body, String
|
60
|
+
# property :review_state, String
|
61
|
+
# property :rating, Integer
|
62
|
+
#
|
63
|
+
# validates_within :review_state, :set => STATES
|
64
|
+
# validates_within :rating, :set => 1..5
|
65
|
+
#
|
66
|
+
# # a call to valid? will return false unless
|
67
|
+
# # the two properties conform to their sets
|
68
|
+
# end
|
49
69
|
def validates_within(*fields)
|
50
|
-
|
51
|
-
add_validator_to_context(opts, fields, DataMapper::Validations::WithinValidator)
|
70
|
+
validators.add(WithinValidator, *fields)
|
52
71
|
end
|
53
|
-
|
54
72
|
end # module ValidatesWithin
|
55
73
|
end # module Validations
|
56
74
|
end # module DataMapper
|
@@ -20,6 +20,7 @@ module DataMapper
|
|
20
20
|
property :email, String, :auto_validation => false
|
21
21
|
property :username, String, :auto_validation => false
|
22
22
|
property :url, String, :auto_validation => false
|
23
|
+
property :bank_url, URI, :auto_validation => false
|
23
24
|
property :code, String, :auto_validation => false, :default => "123456"
|
24
25
|
|
25
26
|
#
|
@@ -37,6 +38,10 @@ module DataMapper
|
|
37
38
|
validates_format_of :username, :with => /[a-z]/, :message => 'Username must have at least one letter', :allow_nil => true
|
38
39
|
validates_format_of :code, :with => /\d{5,6}/, :message => 'Code format is invalid'
|
39
40
|
end
|
41
|
+
|
42
|
+
class SurrenderBillOfLading < BillOfLading
|
43
|
+
validates_format_of :bank_url, :as => :url, :allow_nil => false, :allow_blank => false
|
44
|
+
end
|
40
45
|
end # Fixtures
|
41
46
|
end # Validations
|
42
47
|
end # DataMapper
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module DataMapper
|
2
|
+
module Validations
|
3
|
+
module Fixtures
|
4
|
+
class LlamaSpaceship
|
5
|
+
include DataMapper::Resource
|
6
|
+
|
7
|
+
property :id, Serial
|
8
|
+
property :type, String
|
9
|
+
property :color, String
|
10
|
+
|
11
|
+
validates_format_of :color, :with => /^red|black$/, :if => Proc.new { |spaceship| spaceship.type == "standard" }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -5,6 +5,11 @@ describe 'Inferred validations' do
|
|
5
5
|
it "allow overriding a single error message" do
|
6
6
|
custom_boat = Class.new do
|
7
7
|
include DataMapper::Resource
|
8
|
+
|
9
|
+
def self.name
|
10
|
+
'Boat'
|
11
|
+
end
|
12
|
+
|
8
13
|
property :id, DataMapper::Property::Serial
|
9
14
|
property :name, String, :required => true, :message => "This boat must have name"
|
10
15
|
end
|
@@ -16,6 +21,11 @@ describe 'Inferred validations' do
|
|
16
21
|
it "should have correct error messages" do
|
17
22
|
custom_boat = Class.new do
|
18
23
|
include DataMapper::Resource
|
24
|
+
|
25
|
+
def self.name
|
26
|
+
'Boat'
|
27
|
+
end
|
28
|
+
|
19
29
|
property :id, DataMapper::Property::Serial
|
20
30
|
property :name, String, :required => true, :length => 5..20, :format => /^[a-z]+$/,
|
21
31
|
:messages => {
|
@@ -6,6 +6,10 @@ describe "A class with inferred validations disabled for all properties with an
|
|
6
6
|
@klass = Class.new do
|
7
7
|
include DataMapper::Resource
|
8
8
|
|
9
|
+
def self.name
|
10
|
+
'InferredValidation'
|
11
|
+
end
|
12
|
+
|
9
13
|
property :id, DataMapper::Property::Serial, :auto_validation => false
|
10
14
|
property :name, String, :required => true, :auto_validation => false
|
11
15
|
property :bool, DataMapper::Property::Boolean, :required => true, :auto_validation => false
|
@@ -25,6 +29,10 @@ describe "A class with inferred validations disabled for all properties with a b
|
|
25
29
|
@klass = Class.new do
|
26
30
|
include DataMapper::Resource
|
27
31
|
|
32
|
+
def self.name
|
33
|
+
'InferredValidation'
|
34
|
+
end
|
35
|
+
|
28
36
|
without_auto_validations do
|
29
37
|
property :id, DataMapper::Property::Serial
|
30
38
|
property :name, String, :required => true
|
@@ -124,6 +124,14 @@ describe 'DataMapper::Validations::Fixtures::SmsMessage' do
|
|
124
124
|
end
|
125
125
|
|
126
126
|
describe 'with an infinitely long note' do
|
127
|
+
before do
|
128
|
+
@original = @model.class.properties[:body]
|
129
|
+
end
|
130
|
+
|
131
|
+
after do
|
132
|
+
@model.class.property(@original.name, @original.class, @original.options)
|
133
|
+
end
|
134
|
+
|
127
135
|
it "should raise when trying to set the upper bound of a property length range to Infinity" do
|
128
136
|
expected_msg = "Infinity is no valid upper bound for a length range"
|
129
137
|
lambda {
|
@@ -7,7 +7,9 @@ describe 'uniqueness' do
|
|
7
7
|
@klass = Class.new do
|
8
8
|
include DataMapper::Resource
|
9
9
|
|
10
|
-
|
10
|
+
def self.name
|
11
|
+
'UniqueEventsSingle'
|
12
|
+
end
|
11
13
|
|
12
14
|
property :id, Integer, :key => true
|
13
15
|
property :start_year, Integer, :unique => true
|
@@ -28,7 +30,9 @@ describe 'uniqueness' do
|
|
28
30
|
@klass = Class.new do
|
29
31
|
include DataMapper::Resource
|
30
32
|
|
31
|
-
|
33
|
+
def self.name
|
34
|
+
'UniqueEventsMultiple'
|
35
|
+
end
|
32
36
|
|
33
37
|
property :id, Integer, :key => true
|
34
38
|
property :start_year, Integer, :unique => :years
|
@@ -8,6 +8,12 @@ describe 'A model with a :set & :default options on a property' do
|
|
8
8
|
property :id, DataMapper::Property::Serial
|
9
9
|
property :limited, String, :set => %w[ foo bar bang ], :default => 'foo'
|
10
10
|
end
|
11
|
+
|
12
|
+
DataMapper.finalize
|
13
|
+
|
14
|
+
if DataMapper.respond_to?(:auto_migrate!)
|
15
|
+
DataMapper.auto_migrate!
|
16
|
+
end
|
11
17
|
end
|
12
18
|
|
13
19
|
describe "without value on that property" do
|
@@ -5,10 +5,13 @@ describe 'DataMapper::Validations::Fixtures::Product' do
|
|
5
5
|
DataMapper::Validations::Fixtures::ProductCompany.auto_migrate!
|
6
6
|
DataMapper::Validations::Fixtures::Product.auto_migrate!
|
7
7
|
|
8
|
-
|
8
|
+
parent_model = DataMapper::Validations::Fixtures::ProductCompany
|
9
|
+
@parent = parent_model.new(:title => "Apple", :flagship_product => "Macintosh")
|
9
10
|
@parent.should be_valid
|
11
|
+
@parent.save.should be_true
|
10
12
|
|
11
|
-
|
13
|
+
model_model = DataMapper::Validations::Fixtures::Product
|
14
|
+
@model = model_model.new(:name => "MacBook Pro", :company => @parent)
|
12
15
|
@model.should be_valid
|
13
16
|
end
|
14
17
|
|
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe 'DataMapper::Validations::Fixtures::LlamaSpaceship' do
|
4
|
+
before :all do
|
5
|
+
DataMapper::Validations::Fixtures::LlamaSpaceship.auto_migrate!
|
6
|
+
end
|
7
|
+
|
8
|
+
it "validates even non dirty attributes" do
|
9
|
+
spaceship = DataMapper::Validations::Fixtures::LlamaSpaceship.create(:type => "custom", :color => "pink")
|
10
|
+
spaceship.type = "standard"
|
11
|
+
spaceship.should_not be_valid
|
12
|
+
end
|
13
|
+
end
|
@@ -41,7 +41,8 @@ describe 'DataMapper::Validations::Fixtures::BillOfLading' do
|
|
41
41
|
'Max@Job 3:14',
|
42
42
|
'Job@Book of Job',
|
43
43
|
'test@localhost',
|
44
|
-
'J. P. \'s-Gravezande, a.k.a. The Hacker!@example.com'
|
44
|
+
'J. P. \'s-Gravezande, a.k.a. The Hacker!@example.com',
|
45
|
+
"test@example.com\nsomething after the newline"].each do |email|
|
45
46
|
describe "with email value of #{email} (non RFC2822 compliant)" do
|
46
47
|
before :all do
|
47
48
|
@model = DataMapper::Validations::Fixtures::BillOfLading.new(valid_attributes.merge(:email => email))
|
@@ -56,10 +57,17 @@ describe 'DataMapper::Validations::Fixtures::BillOfLading' do
|
|
56
57
|
@model = DataMapper::Validations::Fixtures::BillOfLading.new(valid_attributes.merge(:email => 'pelé@gmail.com'))
|
57
58
|
end
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
it 'should
|
62
|
-
@model.
|
60
|
+
if (RUBY_VERSION == '1.9.2' && RUBY_ENGINE == 'jruby') || RUBY_VERSION == '1.9.3'
|
61
|
+
# Not supported on jruby 1.9 or 1.9.3 yet - see formats/email.rb
|
62
|
+
it 'should not raise an error' do
|
63
|
+
lambda { @model.valid? }.should_not raise_error
|
64
|
+
end
|
65
|
+
else
|
66
|
+
# Unicode emails not supported on MRI18
|
67
|
+
unless !defined?(RUBY_ENGINE) && RUBY_VERSION == '1.8.7'
|
68
|
+
it 'should behave like valid model' do
|
69
|
+
@model.should be_valid
|
70
|
+
end
|
63
71
|
end
|
64
72
|
end
|
65
73
|
end
|