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.
Files changed (44) hide show
  1. data/Gemfile +8 -8
  2. data/Rakefile +2 -2
  3. data/VERSION +1 -1
  4. data/dm-validations.gemspec +29 -139
  5. data/lib/dm-validations.rb +51 -103
  6. data/lib/dm-validations/auto_validate.rb +104 -61
  7. data/lib/dm-validations/context.rb +66 -0
  8. data/lib/dm-validations/contextual_validators.rb +164 -53
  9. data/lib/dm-validations/formats/email.rb +41 -23
  10. data/lib/dm-validations/formats/url.rb +6 -1
  11. data/lib/dm-validations/support/object.rb +13 -0
  12. data/lib/dm-validations/validation_errors.rb +27 -19
  13. data/lib/dm-validations/validators/absent_field_validator.rb +11 -11
  14. data/lib/dm-validations/validators/acceptance_validator.rb +15 -13
  15. data/lib/dm-validations/validators/block_validator.rb +12 -11
  16. data/lib/dm-validations/validators/confirmation_validator.rb +23 -16
  17. data/lib/dm-validations/validators/format_validator.rb +45 -23
  18. data/lib/dm-validations/validators/generic_validator.rb +85 -38
  19. data/lib/dm-validations/validators/length_validator.rb +61 -26
  20. data/lib/dm-validations/validators/method_validator.rb +13 -17
  21. data/lib/dm-validations/validators/numeric_validator.rb +35 -35
  22. data/lib/dm-validations/validators/primitive_validator.rb +11 -12
  23. data/lib/dm-validations/validators/required_field_validator.rb +11 -13
  24. data/lib/dm-validations/validators/uniqueness_validator.rb +15 -14
  25. data/lib/dm-validations/validators/within_validator.rb +30 -12
  26. data/spec/fixtures/bill_of_landing.rb +5 -0
  27. data/spec/fixtures/llama_spaceship.rb +15 -0
  28. data/spec/integration/automatic_validation/custom_messages_for_inferred_validation_spec.rb +10 -0
  29. data/spec/integration/automatic_validation/disabling_inferred_validation_spec.rb +8 -0
  30. data/spec/integration/automatic_validation/inferred_length_validation_spec.rb +8 -0
  31. data/spec/integration/automatic_validation/inferred_uniqueness_validation_spec.rb +6 -2
  32. data/spec/integration/automatic_validation/inferred_within_validation_spec.rb +6 -0
  33. data/spec/integration/datamapper_models/association_validation_spec.rb +5 -2
  34. data/spec/integration/dirty_attributes/dirty_attributes_spec.rb +13 -0
  35. data/spec/integration/format_validator/email_format_validator_spec.rb +13 -5
  36. data/spec/integration/format_validator/url_format_validator_spec.rb +28 -2
  37. data/spec/integration/required_field_validator/association_spec.rb +5 -1
  38. data/spec/public/resource_spec.rb +2 -2
  39. data/spec/spec_helper.rb +1 -1
  40. data/spec/unit/contextual_validators/emptiness_spec.rb +10 -10
  41. data/spec/unit/contextual_validators/execution_spec.rb +4 -4
  42. data/spec/unit/validators/within_validator_spec.rb +23 -0
  43. metadata +81 -146
  44. 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.validation_property(field_name)
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(:primitive, field_name, property.primitive)
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
- # Validates that the specified attribute is of the correct primitive type.
34
+ # Validates that the specified attribute is of the correct primitive
35
+ # type.
35
36
  #
36
- # @example [Usage]
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
- opts = opts_from_validator_args(fields)
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 = target.validation_property_value(field_name)
11
- property = target.validation_property(field_name)
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 +property+ a boolean property?
32
+ # Is the property a boolean property?
35
33
  #
36
- # Returns true for Boolean, ParanoidBoolean, TrueClass, etc. properties.
37
- # Returns false for other property types.
38
- # Returns false for non-properties.
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 [Usage]
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
- opts = opts_from_validator_args(fields)
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
- assert_kind_of 'scope', options[:scope], Array, Symbol if options.has_key?(:scope)
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
- if target.respond_to?(subject)
38
- opts[subject] = target.__send__(subject)
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) { target.model.first(opts) }
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
- opts = opts_from_validator_args(fields)
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
- def n
41
- 1.0/0
42
- end
39
+
43
40
  end # class WithinValidator
44
41
 
45
42
  module ValidatesWithin
46
-
47
- # Validate that value of a field if within a range/set
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
- opts = opts_from_validator_args(fields)
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
- storage_names[:default] = 'unique_events_single'
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
- storage_names[:default] = 'unique_events_multiple'
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
- @parent = DataMapper::Validations::Fixtures::ProductCompany.create(:title => "Apple", :flagship_product => "Macintosh")
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
- @model = DataMapper::Validations::Fixtures::Product.new(:name => "MacBook Pro", :company => @parent)
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'].each do |email|
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
- # Unicode emails not supported on MRI18
60
- unless !defined?(RUBY_ENGINE) && RUBY_VERSION == '1.8.7'
61
- it 'should behave like valid model' do
62
- @model.should be_valid
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