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
28 changes: 28 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
bundler_args: --without yard guard metrics benchmarks
branches:
only:
- /^release-.*$/
script: "bundle exec rake spec"
rvm:
- ree
- 1.8.7
- 1.9.2
- 1.9.3
- 2.0.0
- ruby-head
- jruby-18mode
- jruby-19mode
- jruby-head
- ree
- rbx-18mode
- rbx-19mode
env:
- "GIT_BRANCH=release-1.2"
notifications:
irc: "irc.freenode.org#datamapper"
email:
- dan.kubb@gmail.com
matrix:
allow_failures:
- rvm: rbx-18mode
- rvm: rbx-19mode
55 changes: 55 additions & 0 deletions lib/dm-validations.rb
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,61 @@ def #{name} # def valid_for_signup?
end
end

# Create a new validator of the given klazz and push it onto the
# requested context for each of the attributes in the fields list
# @param [Hash] opts
# Options supplied to validation macro, example:
# {:context=>:default, :maximum=>50, :allow_nil=>true, :message=>nil}
#
# @param [Array<Symbol>] fields
# Fields given to validation macro, example:
# [:first_name, :last_name] in validates_presence_of :first_name, :last_name
#
# @param [Class] klazz
# Validator class, example: DataMapper::Validations::LengthValidator
def add_validator_to_context(opts, fields, validator_class)
fields.each do |field|
validator = validator_class.new(field, opts.dup)

opts[:context].each do |context|
validator_contexts = validators.context(context)
next if validator_contexts.include?(validator)
validator_contexts << validator
create_context_instance_methods(context)
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.capitalize} 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
2 changes: 1 addition & 1 deletion lib/dm-validations/validators/block_validator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ def validates_with_block(*fields, &block)
method_name = "__validates_with_block_#{@__validates_with_block_count}".to_sym
define_method(method_name, &block)

options = fields.last.is_a?(Hash) ? fields.last.pop.dup : {}
options = fields.last.is_a?(Hash) ? fields.pop.dup : {}
options[:method] = method_name
fields = [method_name] if fields.empty?

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
4 changes: 2 additions & 2 deletions spec/fixtures/g3_concert.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,13 @@ class G3Concert
# Attributes
#

attr_accessor :year, :participants, :city
attr_accessor :year, :participants, :city, :planned

#
# Validations
#

validates_with_block :participants do
validates_with_block :participants, :unless => :planned do
if self.class.known_performances.any? { |perf| perf == self }
true
else
Expand Down
9 changes: 9 additions & 0 deletions spec/integration/block_validator/block_validator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,4 +29,13 @@

it_should_behave_like "valid model"
end

describe "planned concert for non-existing year/participants/city combinations" do
before :all do
@model.planned = true
@model.year = 2021
end

it_should_behave_like "valid model"
end
end
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 @@ -27,3 +27,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