Skip to content
This repository was archived by the owner on Apr 17, 2018. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions lib/dm-validations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,37 @@ def add_validator_to_context(opts, fields, validator_class)
end
end
end

# Automatically adds a validation to all associations so that validation
# on parent models will return false when children are invalid. This
# matches the behaviour of #save, which returns false if child models
# cannot be saved.
#
# TODO: Validations are only run on dirty models (which includes new models),
# since clean ones are assumed to be valid. This prevents validations
# from cascading down to nested child models when not required.
def has(cardinality, property, *args)
super.tap do
next if @disable_auto_validations

add_validation = lambda do |conditional|
validates_with_block property do
value = send(property)
if conditional[value]
[false, "#{property.to_s.titleize} must be valid"]
else
true
end
end
end

if cardinality == 1 || (cardinality.is_a?(Range) && cardinality.max == 1)
add_validation[lambda {|val| val && val.dirty? && !val.valid? }]
else
add_validation[lambda {|val| val.loaded? && !val.map(&:valid?).all? }]
end
end
end
end # module ClassMethods
end # module Validations

Expand Down
17 changes: 17 additions & 0 deletions spec/fixtures/company.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,23 @@ class ProductCompany < Company

validates_presence_of :title, :message => "Product company must have a name"
validates_presence_of :flagship_product

has n, :products, :child_key => [:company_id]
has 1, :profile
has 0..1, :alternate_profile, :model => "Profile"

without_auto_validations do
has 0..1, :yet_another_profile, :model => "Profile"
end
end

class Profile
include DataMapper::Resource

property :id, Serial
belongs_to :product_company
property :description, Text, :required => false # Allow NULL values, enforce validation in the app
validates_presence_of :description
end

class Product
Expand Down
67 changes: 67 additions & 0 deletions spec/integration/datamapper_models/association_validation_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,3 +24,70 @@
end
end
end

describe 'DataMapper::Validations::Fixtures::ProductCompany' do
before :all do
@model = DataMapper::Validations::Fixtures::ProductCompany.create(:title => "Apple", :flagship_product => "Macintosh")
end

describe 'with no products loaded' do
it 'does not load or validate it' do
@model.reload
@model.valid?
@model.products.should_not be_loaded
end
end

describe 'with no profile loaded' do
it 'does not load or validate it' do
pending "Unsure how to test this"
@model.reload
@model.valid?
@model.profile.should_not be_loaded
end
end

describe 'with not dirty profile' do
before :all do
# Force an invalid, yet clean model. This should not happen in real
# code, but gives us an easy way to check whether the validations
# are getting run on the profile.
profile = DataMapper::Validations::Fixtures::Profile.create!(:product_company => @model)
@model.reload
end

it_should_behave_like "valid model"
end

describe 'with invalid products' do
before :all do
@model.products = [DataMapper::Validations::Fixtures::Product.new]
end

it_should_behave_like "invalid model"

it "has a meaningful error message" do
@model.errors.on(:products).should == [ 'Products must be valid' ]
end
end

describe 'with invalid profile' do
before :all do
@model.profile = DataMapper::Validations::Fixtures::Profile.new
end

it_should_behave_like "invalid model"

it "has a meaningful error message" do
@model.errors.on(:profile).should == [ 'Profile must be valid' ]
end
end

describe 'with invalid yet_another_profile' do
before :all do
@model.yet_another_profile = DataMapper::Validations::Fixtures::Profile.new
end

it_should_behave_like "valid model"
end
end