dm-validations 1.1.0 → 1.2.0.rc1

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.
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