validatable-1.6.7/0000755000175000017500000000000011755015216013356 5ustar boutilboutilvalidatable-1.6.7/rakefile.rb0000644000175000017500000000270311755015216015467 0ustar boutilboutilrequire 'rubygems' require 'rake/gempackagetask' require 'rake/rdoctask' require 'rake/contrib/sshpublisher' task :default => :test task :test do require File.dirname(__FILE__) + '/test/all_tests.rb' end desc 'Generate RDoc' Rake::RDocTask.new do |task| task.main = 'README' task.title = 'Validatable' task.rdoc_dir = 'doc' task.options << "--line-numbers" << "--inline-source" task.rdoc_files.include('README', 'lib/**/*.rb') %x[erb README_TEMPLATE > README] if File.exists?('README_TEMPLATE') end desc "Upload RDoc to RubyForge" task :publish_rdoc => [:rdoc] do Rake::SshDirPublisher.new("jaycfields@rubyforge.org", "/var/www/gforge-projects/validatable", "doc").upload end Gem::manage_gems specification = Gem::Specification.new do |s| s.name = "validatable" s.summary = "Validatable is a library for adding validations." s.version = "1.6.7" s.author = 'Jay Fields' s.description = "Validatable is a library for adding validations." s.email = 'validatable-developer@rubyforge.org' s.homepage = 'http://validatable.rubyforge.org' s.rubyforge_project = 'validatable' s.has_rdoc = true s.extra_rdoc_files = ['README'] s.rdoc_options << '--title' << 'Validatable' << '--main' << 'README' << '--line-numbers' s.files = FileList['{lib,test}/**/*.rb', '[A-Z]*$', 'rakefile.rb'].to_a s.test_file = "test/all_tests.rb" end Rake::GemPackageTask.new(specification) do |package| package.need_zip = false package.need_tar = false end validatable-1.6.7/metadata.yml0000644000175000017500000000513111755015216015661 0ustar boutilboutil--- !ruby/object:Gem::Specification name: validatable version: !ruby/object:Gem::Version version: 1.6.7 platform: ruby authors: - Jay Fields autorequire: bindir: bin cert_chain: [] date: 2008-03-20 00:00:00 -04:00 default_executable: dependencies: [] description: Validatable is a library for adding validations. email: validatable-developer@rubyforge.org executables: [] extensions: [] extra_rdoc_files: - README files: - lib/child_validation.rb - lib/errors.rb - lib/included_validation.rb - lib/macros.rb - lib/object_extension.rb - lib/requireable.rb - lib/understandable.rb - lib/validatable.rb - lib/validatable_class_methods.rb - lib/validatable_instance_methods.rb - lib/validations/validates_acceptance_of.rb - lib/validations/validates_confirmation_of.rb - lib/validations/validates_each.rb - lib/validations/validates_format_of.rb - lib/validations/validates_length_of.rb - lib/validations/validates_numericality_of.rb - lib/validations/validates_presence_of.rb - lib/validations/validates_true_for.rb - lib/validations/validation_base.rb - test/all_tests.rb - test/functional/validatable_test.rb - test/functional/validates_acceptance_of_test.rb - test/functional/validates_confirmation_of_test.rb - test/functional/validates_each_test.rb - test/functional/validates_format_of_test.rb - test/functional/validates_length_of_test.rb - test/functional/validates_numericality_of_test.rb - test/functional/validates_presence_of_test.rb - test/functional/validates_true_for_test.rb - test/test_helper.rb - test/unit/errors_test.rb - test/unit/understandable_test.rb - test/unit/validatable_test.rb - test/unit/validates_acceptance_of_test.rb - test/unit/validates_confirmation_of_test.rb - test/unit/validates_format_of_test.rb - test/unit/validates_length_of_test.rb - test/unit/validates_numericality_of_test.rb - test/unit/validates_presence_of_test.rb - test/unit/validates_true_for_test.rb - test/unit/validation_base_test.rb - rakefile.rb - README has_rdoc: true homepage: http://validatable.rubyforge.org post_install_message: rdoc_options: - --title - Validatable - --main - README - --line-numbers require_paths: - lib required_ruby_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: "0" version: required_rubygems_version: !ruby/object:Gem::Requirement requirements: - - ">=" - !ruby/object:Gem::Version version: "0" version: requirements: [] rubyforge_project: validatable rubygems_version: 1.0.1 signing_key: specification_version: 2 summary: Validatable is a library for adding validations. test_files: - test/all_tests.rb validatable-1.6.7/test/0000755000175000017500000000000011755015216014335 5ustar boutilboutilvalidatable-1.6.7/test/unit/0000755000175000017500000000000011755015216015314 5ustar boutilboutilvalidatable-1.6.7/test/unit/validates_acceptance_of_test.rb0000644000175000017500000000123311755015216023505 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') Expectations do expect true do validation = Validatable::ValidatesAcceptanceOf.new stub_everything, :acceptance instance = stub(:acceptance=>'true') validation.valid?(instance) end expect false do validation = Validatable::ValidatesAcceptanceOf.new stub_everything, :acceptance instance = stub(:acceptance=>'false') validation.valid?(instance) end expect true do options = {:message => nil, :if => nil, :times => nil, :level => nil, :groups => nil} Validatable::ValidatesAcceptanceOf.new(stub_everything, :test).must_understand(options) end endvalidatable-1.6.7/test/unit/validates_true_for_test.rb0000644000175000017500000000166711755015216022573 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') Expectations do expect false do validation = Validatable::ValidatesTrueFor.new stub_everything, :name, :logic => lambda { false } validation.valid?(stub_everything) end expect true do validation = Validatable::ValidatesTrueFor.new stub_everything, :name, :logic => lambda { true } validation.valid?(stub_everything) end expect ArgumentError do validation = Validatable::ValidatesTrueFor.new stub_everything, :age end expect true do options = [:message, :if, :times, :level, :groups, :logic, :key] Validatable::ValidatesTrueFor.new(stub_everything, :name, options.to_blank_options_hash).must_understand(options.to_blank_options_hash) end expect true do options = [:logic] Validatable::ValidatesTrueFor.new(stub_everything, :name, options.to_blank_options_hash).requires(options.to_blank_options_hash) end endvalidatable-1.6.7/test/unit/validatable_test.rb0000644000175000017500000000156511755015216021157 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') Expectations do expect false do validation = stub_everything(:should_validate? => true, :attribute => "attribute", :level => 1, :groups => []) klass = Class.new do include Validatable validations << validation end klass.new.valid? end expect true do klass = Class.new do include Validatable end instance = klass.new instance.errors.add(:attribute, "message") instance.valid? instance.errors.empty? end expect false do klass = Class.new do include Validatable end klass.validation_keys_include?("anything") end expect true do validation = stub_everything(:key => "key") klass = Class.new do include Validatable validations << validation end klass.validation_keys_include?("key") end end validatable-1.6.7/test/unit/validates_format_of_test.rb0000644000175000017500000000210211755015216022703 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') Expectations do expect false do validation = Validatable::ValidatesFormatOf.new stub_everything, :name, :with => /book/ validation.valid?(stub_everything) end expect true do validation = Validatable::ValidatesFormatOf.new stub_everything, :name, :with => /book/ validation.valid?(stub(:name=>"book")) end expect true do validation = Validatable::ValidatesFormatOf.new stub_everything, :age, :with => /14/ validation.valid?(stub(:age=>14)) end expect ArgumentError do validation = Validatable::ValidatesFormatOf.new stub_everything, :age end expect true do options = [:message, :if, :times, :level, :groups, :with, :key] Validatable::ValidatesFormatOf.new(stub_everything, :test, options.to_blank_options_hash).must_understand(options.to_blank_options_hash) end expect true do options = [:with] Validatable::ValidatesFormatOf.new(stub_everything, :name, options.to_blank_options_hash).requires(options.to_blank_options_hash) end endvalidatable-1.6.7/test/unit/validates_numericality_of_test.rb0000644000175000017500000000323011755015216024123 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') Expectations do expect false do validation = Validatable::ValidatesNumericalityOf.new stub_everything, :nothing instance = stub(:nothing => nil) validation.valid?(instance) end expect true do validation = Validatable::ValidatesNumericalityOf.new stub_everything, :some_int instance = stub(:some_int => 50) validation.valid?(instance) end expect true do validation = Validatable::ValidatesNumericalityOf.new stub_everything, :some_decimal instance = stub(:some_decimal => 1.23) validation.valid?(instance) end expect false do validation = Validatable::ValidatesNumericalityOf.new stub_everything, :some_decimal, :only_integer => true instance = stub(:some_decimal => 1.23) validation.valid?(instance) end expect true do validation = Validatable::ValidatesNumericalityOf.new stub_everything, :some_negative_number, :only_integer => true instance = stub(:some_negative_number => "-1") validation.valid?(instance) end expect false do validation = Validatable::ValidatesNumericalityOf.new stub_everything, :some_non_numeric instance = stub(:some_non_numeric => "50F") validation.valid?(instance) end expect false do validation = Validatable::ValidatesNumericalityOf.new stub_everything, :multiple_dots instance = stub(:multiple_dots => "50.0.0") validation.valid?(instance) end expect true do options = [:message, :if, :times, :level, :groups, :only_integer] Validatable::ValidatesNumericalityOf.new(stub_everything, :test).must_understand(options.to_blank_options_hash) end endvalidatable-1.6.7/test/unit/understandable_test.rb0000644000175000017500000000065411755015216021700 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') Expectations do expect [:c, :b, :a] do a = Class.new do include Validatable::Understandable understands :a end b = Class.new(a) do include Validatable::Understandable understands :b end c = Class.new(b) do include Validatable::Understandable understands :c end c.all_understandings end end validatable-1.6.7/test/unit/validates_confirmation_of_test.rb0000644000175000017500000000502411755015216024111 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') Expectations do expect true do validation = Validatable::ValidatesConfirmationOf.new stub_everything, :username instance = stub(:username=>"username", :username_confirmation=>"username") validation.valid?(instance) end expect false do validation = Validatable::ValidatesConfirmationOf.new stub_everything, :username instance = stub(:username=>"username", :username_confirmation=>"usessrname") validation.valid?(instance) end expect true do validation = Validatable::ValidatesConfirmationOf.new stub_everything, :username, :case_sensitive => false instance = stub(:username=>"username", :username_confirmation=>"USERNAME") validation.valid?(instance) end expect false do validation = Validatable::ValidatesConfirmationOf.new stub_everything, :username, :case_sensitive => true instance = stub(:username=>"username", :username_confirmation=>"USERNAME") validation.valid?(instance) end expect false do validation = Validatable::ValidatesConfirmationOf.new stub_everything, :username, :case_sensitive => true validation.valid?(stub(:username => nil, :username_confirmation => 'something')) end expect false do validation = Validatable::ValidatesConfirmationOf.new stub_everything, :username, :case_sensitive => true validation.valid?(stub(:username => 'something', :username_confirmation => nil)) end expect true do validation = Validatable::ValidatesConfirmationOf.new stub_everything, :username, :case_sensitive => true validation.valid?(stub(:username => nil, :username_confirmation => nil)) end expect false do validation = Validatable::ValidatesConfirmationOf.new stub_everything, :username, :case_sensitive => false validation.valid?(stub(:username => nil, :username_confirmation => 'something')) end expect false do validation = Validatable::ValidatesConfirmationOf.new stub_everything, :username, :case_sensitive => false validation.valid?(stub(:username => 'something', :username_confirmation => nil)) end expect true do validation = Validatable::ValidatesConfirmationOf.new stub_everything, :username, :case_sensitive => false validation.valid?(stub(:username => nil, :username_confirmation => nil)) end expect true do options = { :message => nil, :if => nil, :times => nil, :level => nil, :groups => nil, :case_sensitive => nil } Validatable::ValidatesConfirmationOf.new(stub_everything, :test).must_understand(options) end endvalidatable-1.6.7/test/unit/validates_presence_of_test.rb0000644000175000017500000000136511755015216023231 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') Expectations do expect false do validation = Validatable::ValidatesPresenceOf.new stub_everything, :name validation.valid?(stub_everything) end expect true do validation = Validatable::ValidatesPresenceOf.new stub_everything, :name validation.valid?(stub(:name=>"book")) end expect true do validation = Validatable::ValidatesPresenceOf.new stub_everything, :employee validation.valid?(stub(:employee => stub(:nil? => false))) end expect true do options = {:message => nil, :if => nil, :times => nil, :level => nil, :groups => nil} Validatable::ValidatesPresenceOf.new(stub_everything, :test).must_understand(options) end endvalidatable-1.6.7/test/unit/errors_test.rb0000644000175000017500000000310111755015216020207 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') Expectations do expect "message" do errors = Validatable::Errors.new errors.add(:attribute, "message") errors.on(:attribute) end expect ["message"] do errors = Validatable::Errors.new errors.add(:attribute, "message") errors.raw(:attribute) end expect "something new" do errors = Validatable::Errors.new errors.add(:attribute, "something old") errors.replace(:attribute, ["something new"]) errors.on(:attribute) end expect "Capitalized word" do errors = Validatable::Errors.new errors.humanize("capitalized_word") end expect "Capitalized word without" do errors = Validatable::Errors.new errors.humanize("capitalized_word_without_id") end expect ["A humanized message", "a base message"] do errors = Validatable::Errors.new errors.add(:base, "a base message") errors.add(:a_humanized, "message") errors.full_messages.sort end expect true do Validatable::Errors.included_modules.include?(Enumerable) end expect ["message1", "message2"] do errors = Validatable::Errors.new errors.add(:attribute, "message1") errors.add(:attribute, "message2") errors.on(:attribute) end expect 2 do errors = Validatable::Errors.new errors.add(:attribute, "message1") errors.add(:attribute, "message2") errors.count end expect 2 do errors = Validatable::Errors.new errors.add(:attribute1, "message1") errors.add(:attribute2, "message2") errors.count end endvalidatable-1.6.7/test/unit/validates_length_of_test.rb0000644000175000017500000000460711755015216022710 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') Expectations do expect false do validation = Validatable::ValidatesLengthOf.new stub_everything, :username, :maximum => 8 validation.valid?(stub(:username=>"usernamefdfd")) end expect false do validation = Validatable::ValidatesLengthOf.new stub_everything, :username, :minimum => 2 instance = stub(:username=>"u") validation.valid?(instance) end expect true do validation = Validatable::ValidatesLengthOf.new stub_everything, :username, :minimum => 2, :maximum => 8 instance = stub(:username=>"udfgdf") validation.valid?(instance) end expect false do validation = Validatable::ValidatesLengthOf.new stub_everything, :username, :is => 2 instance = stub(:username=>"u") validation.valid?(instance) end expect true do validation = Validatable::ValidatesLengthOf.new stub_everything, :username, :is => 2 instance = stub(:username=>"uu") validation.valid?(instance) end expect true do validation = Validatable::ValidatesLengthOf.new stub_everything, :username, :within => 2..4 instance = stub(:username => "aa") validation.valid?(instance) end expect false do validation = Validatable::ValidatesLengthOf.new stub_everything, :username, :within => 2..4 instance = stub(:username => "a") validation.valid?(instance) end expect true do validation = Validatable::ValidatesLengthOf.new stub_everything, :username, :within => 2..4 instance = stub(:username => "aaaa") validation.valid?(instance) end expect false do validation = Validatable::ValidatesLengthOf.new stub_everything, :username, :within => 2..4 instance = stub(:username => "aaaaa") validation.valid?(instance) end expect false do validation = Validatable::ValidatesLengthOf.new stub_everything, :username, :within => 2..4 instance = stub(:username => nil) validation.valid?(instance) end expect true do validation = Validatable::ValidatesLengthOf.new stub_everything, :username, :within => 2..4, :allow_nil => true instance = stub(:username => nil) validation.valid?(instance) end expect true do options = [:message, :if, :times, :level, :groups, :maximum, :minimum, :is, :within, :allow_nil] Validatable::ValidatesLengthOf.new(stub_everything, :test).must_understand(options.to_blank_options_hash) end endvalidatable-1.6.7/test/unit/validation_base_test.rb0000644000175000017500000000325111755015216022025 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') Expectations do expect true do validation = Validatable::ValidationBase.new stub_everything, :base validation.should_validate? Object.new end expect true do validation = Validatable::ValidationBase.new stub_everything, :base, :times => 1 validation.validate_this_time?(stub(:times_validated => 0)) end expect true do validation = Validatable::ValidationBase.new stub_everything, :base validation.validate_this_time?(nil) end expect true do validation = Validatable::ValidationBase.new stub_everything, :base, :times => 2 validation.validate_this_time?(stub(:times_validated => 1)) end expect false do validation = Validatable::ValidationBase.new stub_everything, :base, :times => 1 validation.validate_this_time?(stub(:times_validated => 1)) end expect 1 do validation = Validatable::ValidationBase.new stub_everything, :base validation.level end expect ArgumentError do Validatable::ValidationBase.new stub_everything(:validation_keys_include? => true), :base, :times => 1 end expect "some message 100" do validation = Validatable::ValidationBase.new stub_everything, :base, :message => lambda { "some message #{a_method}" } validation.message(stub(:a_method=>'100')) end expect ArgumentError do Validatable::ValidationBase.new(stub_everything, :base).must_understand(:foo => 1, :bar => 2) end expect true do options = {:message => nil, :if => nil, :times => nil, :level => nil, :groups => nil, :key => nil} Validatable::ValidationBase.new(stub_everything, :base).must_understand(options) end endvalidatable-1.6.7/test/all_tests.rb0000644000175000017500000000007711755015216016660 0ustar boutilboutilDir['test/**/*_test.rb'].each { |test_case| require test_case }validatable-1.6.7/test/test_helper.rb0000644000175000017500000000137611755015216017207 0ustar boutilboutilrequire 'test/unit' require 'rubygems' require 'mocha' require 'dust' require 'set' require 'expectations' require File.dirname(__FILE__) + '/../lib/validatable' class << Test::Unit::TestCase def expect(expected_value, &block) define_method :"test_#{caller.first.split("/").last}" do begin assert_equal expected_value, instance_eval(&block) rescue Exception => ex raise ex unless expected_value.is_a?(Class) && ex.is_a?(expected_value) assert_equal expected_value, ex.class end end end end class Test::Unit::TestCase def assert_array_equal a, b assert_equal Set.new(a), Set.new(b) end end class Array def to_blank_options_hash self.inject({}) {|hash, value| hash[value] = nil; hash } end endvalidatable-1.6.7/test/functional/0000755000175000017500000000000011755015216016477 5ustar boutilboutilvalidatable-1.6.7/test/functional/validates_acceptance_of_test.rb0000644000175000017500000000077011755015216024675 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') module Functional class ValidatesAcceptanceOfTest < Test::Unit::TestCase test "given no acceptance, when validated, then error is in the objects error collection" do klass = Class.new do include Validatable attr_accessor :name validates_acceptance_of :name end instance = klass.new instance.valid? assert_equal "must be accepted", instance.errors.on(:name) end end endvalidatable-1.6.7/test/functional/validates_true_for_test.rb0000644000175000017500000000160111755015216023742 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') module Functional class ValidatesFormatOfTest < Test::Unit::TestCase test "given invalid name, when validated, then error is in the objects error collection" do klass = Class.new do include Validatable attr_accessor :name validates_true_for :name, :logic => lambda { name == "nombre" } end instance = klass.new instance.valid? assert_equal "is invalid", instance.errors.on(:name) end test "given valid name, when validated, then no error is in the objects error collection" do klass = Class.new do include Validatable attr_accessor :name validates_true_for :name, :logic => lambda { name == "nombre" } end instance = klass.new instance.name = "nombre" assert_equal true, instance.valid? end end endvalidatable-1.6.7/test/functional/validatable_test.rb0000644000175000017500000002526211755015216022342 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') functional_tests do expect "can't be empty" do child_class = Module.new do include Validatable validates_presence_of :name end klass = Class.new do include Validatable include_validations_from :child define_method :child do child_class end attr_accessor :name end instance = klass.new instance.valid? instance.errors.on(:name) end expect "can't be empty" do child_class = Module.new do include Validatable validates_presence_of :name end klass = Class.new do include Validatable validates_presence_of :address include_validations_from :child define_method :child do child_class end attr_accessor :name, :address end instance = klass.new instance.valid? instance.errors.on(:address) end expect :is_set do klass = Class.new do include Validatable attr_accessor :result before_validation do self.result = :is_set end end instance = klass.new instance.valid? instance.result end expect :is_set do klass = Class.new do include Validatable attr_accessor :name, :result validates_presence_of :name, :after_validate => lambda { |result, attribute| self.result = :is_set } end instance = klass.new instance.valid? instance.result end expect false do klass = Class.new do include Validatable attr_accessor :name validates_presence_of :name end subclass = Class.new klass do end subsubclass = Class.new subclass do end subsubclass.new.valid? end expect false do klass = Class.new do include Validatable attr_accessor :name validates_presence_of :name end subclass = Class.new klass do end subclass.new.valid? end expect true do klass = Class.new do include Validatable attr_accessor :name validates_presence_of :name end subclass = Class.new klass do end instance = subclass.new instance.name = 'a name' instance.valid? end expect ArgumentError do Class.new do include Validatable attr_accessor :name validates_presence_of :name, :times => 1 validates_presence_of :name, :times => 1 end end expect ArgumentError do Class.new do include Validatable attr_accessor :name validates_presence_of :name, :times => 1, :key => 'anything' validates_presence_of :name, :times => 1, :key => 'anything' end end expect "is invalid" do child_class = Class.new do include Validatable attr_accessor :name, :address validates_presence_of :name validates_format_of :address, :with => /.+/ end klass = Class.new do include Validatable include_errors_from :child define_method :child do child_class.new end end instance = klass.new instance.valid? instance.errors.on(:address) end expect "can't be empty" do child_class = Class.new do include Validatable attr_accessor :name, :address validates_presence_of :name validates_format_of :address, :with => /.+/ end klass = Class.new do include Validatable include_errors_from :child define_method :child do child_class.new end end instance = klass.new instance.valid? instance.errors.on(:name) end test "when child validations have errors, level 2 and higher parent validations are not performed" do child_class = Class.new do include Validatable attr_accessor :name validates_presence_of :name end klass = Class.new do include Validatable extend Forwardable def_delegator :child, :name validates_true_for :name, :logic => lambda { false }, :level => 2, :message => "invalid message" include_errors_from :child define_method :child do @child ||= child_class.new end end instance = klass.new instance.valid? assert_equal "can't be empty", instance.errors.on(:name) end test "when child validations have errors, level 1 parent validations are still performed" do child_class = Class.new do include Validatable attr_accessor :name validates_presence_of :name end klass = Class.new do include Validatable validates_true_for :address, :logic => lambda { false }, :level => 1, :message => "invalid message" include_errors_from :child define_method :child do @child ||= child_class.new end end instance = klass.new instance.valid? assert_equal "can't be empty", instance.errors.on(:name) assert_equal "invalid message", instance.errors.on(:address) end expect "can't be empty" do child_class = Class.new do include Validatable attr_accessor :name, :address validates_presence_of :name end klass = Class.new do include Validatable include_errors_from :child, :map => {:name => :namen} define_method :child do child_class.new end end instance = klass.new instance.valid? instance.errors.on(:namen) end expect "can't be empty" do child_class = Class.new do include Validatable attr_accessor :name, :address validates_presence_of :name end klass = Class.new do include Validatable include_errors_from :child, :if => lambda { true } define_method :child do child_class.new end end instance = klass.new instance.valid? instance.errors.on(:name) end expect true do child_class = Class.new do include Validatable attr_accessor :name, :address validates_presence_of :name end klass = Class.new do include Validatable include_errors_from :child, :if => lambda { false } define_method :child do child_class.new end end instance = klass.new instance.valid? end test "classes only have valid_for_* methods for groups that appear in their validations" do class_with_group_one = Class.new do include Validatable validates_presence_of :name, :groups => :group_one attr_accessor :name end class_with_group_two = Class.new do include Validatable validates_presence_of :name, :groups => :group_two attr_accessor :name end assert_equal false, class_with_group_one.public_instance_methods.include?(:valid_for_group_two?) assert_equal false, class_with_group_two.public_instance_methods.include?(:valid_for_group_one?) end test "nonmatching groups are not used as validations" do klass = Class.new do include Validatable validates_presence_of :name, :groups => :group_one validates_presence_of :address, :groups => :group_two attr_accessor :name, :address end instance = klass.new assert_equal nil, instance.errors.on(:name) end expect "can't be empty twice changed message" do klass = Class.new do include Validatable validates_presence_of :name attr_accessor :name end Validatable::ValidationBase.class_eval do after_validate do |result, instance, attribute| instance.errors.add(attribute, " changed message") end end Validatable::ValidatesPresenceOf.class_eval do after_validate do |result, instance, attribute| instance.errors.add(attribute, " twice") end end instance = klass.new instance.valid? Validatable::ValidatesPresenceOf.after_validations.clear Validatable::ValidationBase.after_validations.clear instance.errors.on(:name).join end expect false do klass = Class.new do include Validatable validates_presence_of :name, :groups => :group_one attr_accessor :name end instance = klass.new instance.valid_for_group_one? end expect false do klass = Class.new do include Validatable validates_presence_of :name, :groups => [:group_one, :group_two] attr_accessor :name end instance = klass.new instance.valid_for_group_one? end expect true do klass = Class.new do include Validatable validates_presence_of :name, :groups => :group_one validates_presence_of :address attr_accessor :name, :address end instance = klass.new instance.address = 'anything' instance.valid? end expect true do klass = Class.new do include Validatable validates_presence_of :name, :groups => :group_one attr_accessor :name end instance = klass.new instance.valid? end expect true do klass = Class.new do include Validatable validates_presence_of :name, :times => 1 attr_accessor :name end instance = klass.new instance.valid? instance.valid? end expect false do klass = Class.new do include Validatable validates_presence_of :name, :times => 1 attr_accessor :name end instance1 = klass.new instance1.valid? instance2 = klass.new instance2.valid? end expect "name message" do klass = Class.new do include Validatable validates_presence_of :name, :level => 1, :message => "name message" validates_presence_of :address, :level => 2 attr_accessor :name, :address end instance = klass.new instance.valid? instance.errors.on(:name) end expect nil do klass = Class.new do include Validatable validates_presence_of :name, :level => 1, :message => "name message" validates_presence_of :address, :level => 2 attr_accessor :name, :address end instance = klass.new instance.valid? instance.errors.on(:address) end expect "Mod::Klass/Validatable::ValidatesPresenceOf/name" do module Mod class Klass include Validatable validates_presence_of :name end end Mod::Klass.validations.first.key end expect "/Validatable::ValidatesPresenceOf/custom key" do klass = Class.new do include Validatable validates_presence_of :name, :key => "custom key" end klass.validations.first.key end expect "can't be empty" do klass = Class.new do include Validatable validates_presence_of :name, :address attr_accessor :name, :address end instance = klass.new instance.validate_only("presence_of/name") instance.errors.on(:name) end expect nil do klass = Class.new do include Validatable validates_presence_of :name, :address attr_accessor :name, :address end instance = klass.new instance.validate_only("presence_of/name") instance.errors.on(:address) end end validatable-1.6.7/test/functional/validates_each_test.rb0000644000175000017500000000053111755015216023016 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') functional_tests do expect :is_set do klass = Class.new do include Validatable attr_accessor :name, :result validates_each :name, :logic => lambda { @result = :is_set } end instance = klass.new instance.valid? instance.result end endvalidatable-1.6.7/test/functional/validates_format_of_test.rb0000644000175000017500000000225311755015216024075 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') module Functional class ValidatesFormatOfTest < Test::Unit::TestCase test "given invalid name format, when validated, then error is in the objects error collection" do klass = Class.new do include Validatable attr_accessor :name validates_format_of :name, :with => /.+/ end instance = klass.new instance.valid? assert_equal "is invalid", instance.errors.on(:name) end test "given invalid name format and nil name, when validated, then error is in the objects error collection" do klass = Class.new do include Validatable attr_accessor :name validates_format_of :name, :with => /.+/, :if => Proc.new { !name.nil? } end assert_equal true, klass.new.valid? end test "given invalid name format and a name, when validated, then error is in the objects error collection" do klass = Class.new do include Validatable attr_accessor :name validates_format_of :name, :with => /.+/, :if => Proc.new { name.nil? } end assert_equal false, klass.new.valid? end end endvalidatable-1.6.7/test/functional/validates_numericality_of_test.rb0000644000175000017500000000336111755015216025313 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') module Functional class ValidatesNumericalityOfTest < Test::Unit::TestCase test "when validating numericality and the value is nil an error should exist on the instance" do klass = Class.new do include Validatable attr_accessor :nothing validates_numericality_of :nothing end instance = klass.new instance.valid? assert_equal "must be a number", instance.errors.on(:nothing) end test "when validating numericality and the value has a non numeric character an error should exist on the instance" do klass = Class.new do include Validatable validates_numericality_of :some_string def some_string "some_string" end end instance = klass.new instance.valid? assert_equal "must be a number", instance.errors.on(:some_string) end test "when validating a number no error will be in the instance" do klass = Class.new do include Validatable validates_numericality_of :valid_number def valid_number 1.23 end end instance = klass.new instance.valid? assert_equal nil, instance.errors.on(:valid_number) end test "when validating an integer and the value is a decimal an error should exist on the instance" do klass = Class.new do include Validatable validates_numericality_of :valid_number, :only_integer => true attr_accessor :valid_number end instance = klass.new instance.valid_number = 1.23 instance.valid? assert_equal "must be a number", instance.errors.on(:valid_number) end end endvalidatable-1.6.7/test/functional/validates_confirmation_of_test.rb0000644000175000017500000000375111755015216025301 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') module Functional class ValidatesConfirmationOfTest < Test::Unit::TestCase test "given non matching attributes, when validated, then error is in the objects error collection" do klass = Class.new do include Validatable attr_accessor :name, :name_confirmation validates_confirmation_of :name end instance = klass.new instance.name = "foo" instance.name_confirmation = "bar" instance.valid? assert_equal "doesn't match confirmation", instance.errors.on(:name) end test "given matching attributes, when validated, then no error is in the objects error collection" do klass = Class.new do include Validatable attr_accessor :name, :name_confirmation validates_confirmation_of :name end instance = klass.new instance.name = "foo" instance.name_confirmation = "foo" assert_equal true, instance.valid? end test "given matching attributes of different case, when validated with case sensitive false, then no error is in the objects error collection" do klass = Class.new do include Validatable attr_accessor :name, :name_confirmation validates_confirmation_of :name, :case_sensitive => false end instance = klass.new instance.name = "foo" instance.name_confirmation = "FOO" assert_equal true, instance.valid? end test "given matching attributes of different case, when validated with case sensitive true, then error is in the objects error collection" do klass = Class.new do include Validatable attr_accessor :name, :name_confirmation validates_confirmation_of :name end instance = klass.new instance.name = "foo" instance.name_confirmation = "FOO" assert_equal false, instance.valid? assert_equal "doesn't match confirmation", instance.errors.on(:name) end end endvalidatable-1.6.7/test/functional/validates_presence_of_test.rb0000644000175000017500000000075411755015216024415 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') module Functional class ValidatesPresenceOfTest < Test::Unit::TestCase test "given no name, when validated, then error is in the objects error collection" do klass = Class.new do include Validatable attr_accessor :name validates_presence_of :name end instance = klass.new instance.valid? assert_equal "can't be empty", instance.errors.on(:name) end end endvalidatable-1.6.7/test/functional/validates_length_of_test.rb0000644000175000017500000000351411755015216024067 0ustar boutilboutilrequire File.expand_path(File.dirname(__FILE__) + '/../test_helper') module Functional class ValidatesLengthOfTest < Test::Unit::TestCase test "given short value, when validated, then error is in the objects error collection" do klass = Class.new do include Validatable attr_accessor :name validates_length_of :name, :minimum => 2 end instance = klass.new instance.valid? assert_equal "is invalid", instance.errors.on(:name) end test "given is constraint, when validated, then error is in the objects error collection" do klass = Class.new do include Validatable attr_accessor :name validates_length_of :name, :is => 2 end instance = klass.new instance.valid? assert_equal "is invalid", instance.errors.on(:name) end test "given is constraint is met, when validated, then valid is true" do klass = Class.new do include Validatable attr_accessor :name validates_length_of :name, :is => 2 end instance = klass.new instance.name = "bk" assert_equal true, instance.valid? end test "given within constraint, when validated, then error is in the objects error collection" do klass = Class.new do include Validatable attr_accessor :name validates_length_of :name, :within => 2..4 end instance = klass.new instance.valid? assert_equal "is invalid", instance.errors.on(:name) end test "given within constraint, when validated, then valid is true" do klass = Class.new do include Validatable attr_accessor :name validates_length_of :name, :within => 2..4 end instance = klass.new instance.name = "bk" assert_equal true, instance.valid? end end endvalidatable-1.6.7/README0000644000175000017500000000723111755015216014241 0ustar boutilboutil= Validatable Validatable is a library for adding validations. by Jay[http://jayfields.blogspot.com] Fields[http://jayfields.blogspot.com] == Download and Installation You can download Validatable from here[http://rubyforge.org/projects/validatable] or install it with the following command. $ gem install validatable == License You may use, copy and redistribute this library under the same terms as Ruby itself (see http://www.ruby-lang.org/en/LICENSE.txt). == Examples Validation of an entire hierarchy of objects with errors aggregated at the root object. class Person include Validatable validates_presence_of :name attr_accessor :name end class PersonPresenter include Validatable include_validations_for :person attr_accessor :person def initialize(person) @person = person end end presenter = PersonPresenter.new(Person.new) presenter.valid? #=> false presenter.errors.on(:name) #=> "can't be blank" Validations that turn off after X times of failed attempts. class Person include Validatable validates_presence_of :name, :times => 1 attr_accessor :name end person = Person.new person.valid? #=> false person.valid? #=> true Validations can be given levels. If a validation fails on a level the validations for subsequent levels will not be executed. class Person include Validatable validates_presence_of :name, :level => 1, :message => "name message" validates_presence_of :address, :level => 2 attr_accessor :name, :address end person = Person.new person.valid? #=> false person.errors.on(:name) #=> "name message" person.errors.on(:address) #=> nil Validations can also be given groups. Groups can be used to validate an object when it can be valid in various states. For example a mortgage application may be valid for saving (saving a partial application), but that same mortgage application would not be valid for underwriting. In our example a application can be saved as long as a Social Security Number is present; however, an application can not be underwritten unless the name attribute contains a value. class MortgageApplication include Validatable validates_presence_of :ssn, :groups => [:saving, :underwriting] validates_presence_of :name, :groups => :underwriting attr_accessor :name, :ssn end application = MortgageApplication.new application.ssn = 377990118 application.valid_for_saving? #=> true application.valid_for_underwriting? #=> false As you can see, you can use an array if the validation needs to be part of various groups. However, if the validation only applies to one group you can simply use a symbol for the group name. Similar to Rails, Validatable also supports conditional validation. class Person include Validatable attr_accessor :name validates_format_of :name, :with => /.+/, :if => Proc.new { !name.nil? } end Person.new.valid? #=> true Validatable also exposes an after_validate hook method. class Person include Validatable validates_presence_of :name attr_accessor :name end class ValidatesPresenceOf after_validate do |result, instance, attribute| instance.errors.add("#{attribute} can't be blank") unless result end end person = Person.new person.valid? #=> false person.errors.on(:name) #=> "name can't be blank" The after_validate hook yields the result of the validation being run, the instance the validation was run on, and the attribute that was validated In the above example the attribute "name" is appended to the message. See the tests for more examples == Contributors Rick Bradley, Anonymous Z, Jason Miller, Ali Aghareza, Xavier Shay, Dan Manges, Karthik Krishnan and Venkat, Clint Bishopvalidatable-1.6.7/lib/0000755000175000017500000000000011755015216014124 5ustar boutilboutilvalidatable-1.6.7/lib/validatable.rb0000644000175000017500000000263711755015216016731 0ustar boutilboutilrequire 'forwardable' require File.expand_path(File.dirname(__FILE__) + '/object_extension') require File.expand_path(File.dirname(__FILE__) + '/errors') require File.expand_path(File.dirname(__FILE__) + '/validatable_class_methods') require File.expand_path(File.dirname(__FILE__) + '/macros') require File.expand_path(File.dirname(__FILE__) + '/validatable_instance_methods') require File.expand_path(File.dirname(__FILE__) + '/included_validation') require File.expand_path(File.dirname(__FILE__) + '/child_validation') require File.expand_path(File.dirname(__FILE__) + '/understandable') require File.expand_path(File.dirname(__FILE__) + '/requireable') require File.expand_path(File.dirname(__FILE__) + '/validations/validation_base') require File.expand_path(File.dirname(__FILE__) + '/validations/validates_format_of') require File.expand_path(File.dirname(__FILE__) + '/validations/validates_presence_of') require File.expand_path(File.dirname(__FILE__) + '/validations/validates_acceptance_of') require File.expand_path(File.dirname(__FILE__) + '/validations/validates_confirmation_of') require File.expand_path(File.dirname(__FILE__) + '/validations/validates_length_of') require File.expand_path(File.dirname(__FILE__) + '/validations/validates_true_for') require File.expand_path(File.dirname(__FILE__) + '/validations/validates_numericality_of') require File.expand_path(File.dirname(__FILE__) + '/validations/validates_each')validatable-1.6.7/lib/validatable_class_methods.rb0000644000175000017500000000443211755015216021634 0ustar boutilboutilmodule Validatable module ClassMethods #:nodoc: def validate_children(instance, group) self.children_to_validate.each do |child_validation| next unless child_validation.should_validate?(instance) child = instance.send child_validation.attribute if (child.respond_to?(:valid_for_group?)) child.valid_for_group?(group) else child.valid? end child.errors.each do |attribute, messages| if messages.is_a?(String) add_error(instance, child_validation.map[attribute.to_sym] || attribute, messages) else messages.each do |message| add_error(instance, child_validation.map[attribute.to_sym] || attribute, message) end end end end end def all_before_validations if self.superclass.respond_to? :all_before_validations return before_validations + self.superclass.all_before_validations end before_validations end def before_validations @before_validations ||= [] end def all_validations if self.respond_to?(:superclass) && self.superclass.respond_to?(:all_validations) return validations + self.superclass.all_validations end validations end def validations @validations ||= [] end def add_error(instance, attribute, msg) instance.errors.add(attribute, msg) end def validation_keys_include?(key) validations.map { |validation| validation.key }.include?(key) end def validations_to_include @validations_to_include ||= [] end protected def add_validations(args, klass) options = args.last.is_a?(Hash) ? args.pop : {} args.each do |attribute| new_validation = klass.new self, attribute, options self.validations << new_validation self.create_valid_method_for_groups new_validation.groups end end def create_valid_method_for_groups(groups) groups.each do |group| self.class_eval do define_method "valid_for_#{group}?".to_sym do valid_for_group?(group) end end end end def children_to_validate @children_to_validate ||= [] end end endvalidatable-1.6.7/lib/errors.rb0000644000175000017500000000403711755015216015771 0ustar boutilboutilmodule Validatable class Errors extend Forwardable include Enumerable def_delegators :errors, :clear, :each, :each_pair, :empty?, :length, :size # call-seq: on(attribute) # # * Returns nil, if no errors are associated with the specified +attribute+. # * Returns the error message, if one error is associated with the specified +attribute+. # * Returns an array of error messages, if more than one error is associated with the specified +attribute+. def on(attribute) return nil if errors[attribute.to_sym].nil? errors[attribute.to_sym].size == 1 ? errors[attribute.to_sym].first : errors[attribute.to_sym] end def add(attribute, message) #:nodoc: errors[attribute.to_sym] = [] if errors[attribute.to_sym].nil? errors[attribute.to_sym] << message end def merge!(errors) #:nodoc: errors.each_pair{|k, v| add(k,v)} self end # call-seq: replace(attribute) # # * Replaces the errors value for the given +attribute+ def replace(attribute, value) errors[attribute.to_sym] = value end # call-seq: raw(attribute) # # * Returns an array of error messages associated with the specified +attribute+. def raw(attribute) errors[attribute.to_sym] end def errors #:nodoc: @errors ||= {} end def count #:nodoc: errors.values.flatten.size end # call-seq: full_messages -> an_array_of_messages # # Returns an array containing the full list of error messages. def full_messages full_messages = [] errors.each_key do |attribute| errors[attribute].each do |msg| next if msg.nil? if attribute.to_s == "base" full_messages << msg else full_messages << humanize(attribute.to_s) + " " + msg end end end full_messages end def humanize(lower_case_and_underscored_word) #:nodoc: lower_case_and_underscored_word.to_s.gsub(/_id$/, "").gsub(/_/, " ").capitalize end end endvalidatable-1.6.7/lib/understandable.rb0000644000175000017500000000146111755015216017446 0ustar boutilboutilmodule Validatable module Understandable #:nodoc: module ClassMethods #:nodoc: def understands(*args) understandings.concat args end def understandings @understandings ||= [] end def all_understandings return understandings + self.superclass.all_understandings if self.superclass.respond_to? :all_understandings understandings end end def self.included(klass) klass.extend ClassMethods end def must_understand(hash) invalid_options = hash.inject([]) do |errors, (key, value)| errors << key.to_s unless self.class.all_understandings.include?(key) errors end raise ArgumentError.new("invalid options: #{invalid_options.join(', ')}") if invalid_options.any? true end end endvalidatable-1.6.7/lib/object_extension.rb0000644000175000017500000000105511755015216020014 0ustar boutilboutilclass Object #:nodoc: module InstanceExecHelper #:nodoc: end include InstanceExecHelper def instance_eval_with_params(*args, &block) begin old_critical, Thread.critical = Thread.critical, true n = 0 n += 1 while respond_to?(mname="__instance_exec#{n}") InstanceExecHelper.module_eval{ define_method(mname, &block) } ensure Thread.critical = old_critical end begin ret = send(mname, *args) ensure InstanceExecHelper.module_eval{ remove_method(mname) } rescue nil end ret end endvalidatable-1.6.7/lib/macros.rb0000644000175000017500000002650211755015216015742 0ustar boutilboutilmodule Validatable module Macros # call-seq: validates_each(*args) # # Validates that the logic evaluates to true # # class Address # include Validatable # validates_each :zip_code, :logic => lambda { errors.add(:zip_code, "is not valid") if ZipCodeService.allows(zip_code) } # end # # The logic option is required. # # Configuration options: # # * after_validate - A block that executes following the run of a validation # * group - The group that this validation belongs to. A validation can belong to multiple groups # * if - A block that when executed must return true of the validation will not occur # * level - The level at which the validation should occur # * logic - A block that executes to perform the validation # * message - The message to add to the errors collection when the validation fails # * times - The number of times the validation applies def validates_each(*args) add_validations(args, ValidatesEach) end # call-seq: validates_format_of(*args) # # Validates whether the value of the specified attribute is of the # correct form by matching it against the regular expression provided. # # class Person # include Validatable # validates_format_of :first_name, :with => /[ A-Za-z]/ # end # # A regular expression must be provided or else an exception will be raised. # # Configuration options: # # * after_validate - A block that executes following the run of a validation # * message - The message to add to the errors collection when the validation fails # * times - The number of times the validation applies # * level - The level at which the validation should occur # * if - A block that when executed must return true of the validation will not occur # * with - The regular expression used to validate the format # * group - The group that this validation belongs to. A validation can belong to multiple groups def validates_format_of(*args) add_validations(args, ValidatesFormatOf) end # call-seq: validates_length_of(*args) # # Validates that the specified attribute matches the length restrictions supplied. # # class Person # include Validatable # validates_length_of :first_name, :maximum=>30 # validates_length_of :last_name, :minimum=>30 # end # # Configuration options: # # * after_validate - A block that executes following the run of a validation # * message - The message to add to the errors collection when the validation fails # * times - The number of times the validation applies # * level - The level at which the validation should occur # * if - A block that when executed must return true of the validation will not occur # * minimum - The minimum size of the attribute # * maximum - The maximum size of the attribute # * is - The size the attribute must be # * within - A range that the size of the attribute must fall within # * group - The group that this validation belongs to. A validation can belong to multiple groups def validates_length_of(*args) add_validations(args, ValidatesLengthOf) end # call-seq: validates_numericality_of(*args) # # Validates that the specified attribute is numeric. # # class Person # include Validatable # validates_numericality_of :age # end # # Configuration options: # # * after_validate - A block that executes following the run of a validation # * message - The message to add to the errors collection when the validation fails # * times - The number of times the validation applies # * level - The level at which the validation should occur # * if - A block that when executed must return true of the validation will not occur # * group - The group that this validation belongs to. A validation can belong to multiple groups # * only_integer - Whether the attribute must be an integer (default is false) def validates_numericality_of(*args) add_validations(args, ValidatesNumericalityOf) end # call-seq: validates_acceptance_of(*args) # # Encapsulates the pattern of wanting to validate the acceptance of a terms of service check box (or similar agreement). Example: # # class Person # include Validatable # validates_acceptance_of :terms_of_service # validates_acceptance_of :eula, :message => "must be abided" # end # # Configuration options: # # * after_validate - A block that executes following the run of a validation # * message - The message to add to the errors collection when the validation fails # * times - The number of times the validation applies # * level - The level at which the validation should occur # * if - A block that when executed must return true of the validation will not occur # * group - The group that this validation belongs to. A validation can belong to multiple groups def validates_acceptance_of(*args) add_validations(args, ValidatesAcceptanceOf) end # call-seq: validates_confirmation_of(*args) # # Encapsulates the pattern of wanting to validate a password or email address field with a confirmation. Example: # # Class: # class PersonPresenter # include Validatable # validates_confirmation_of :user_name, :password # validates_confirmation_of :email_address, :message => "should match confirmation" # end # # View: # <%= password_field "person", "password" %> # <%= password_field "person", "password_confirmation" %> # # Configuration options: # # * after_validate - A block that executes following the run of a validation # * case_sensitive - Whether or not to apply case-sensitivity on the comparison. Defaults to true. # * message - The message to add to the errors collection when the validation fails # * times - The number of times the validation applies # * level - The level at which the validation should occur # * if - A block that when executed must return true of the validation will not occur # * group - The group that this validation belongs to. A validation can belong to multiple groups def validates_confirmation_of(*args) add_validations(args, ValidatesConfirmationOf) end # call-seq: validates_presence_of(*args) # # Validates that the specified attributes are not nil or an empty string # # class Person # include Validatable # validates_presence_of :first_name # end # # The first_name attribute must be in the object and it cannot be nil or empty. # # Configuration options: # # * after_validate - A block that executes following the run of a validation # * message - The message to add to the errors collection when the validation fails # * times - The number of times the validation applies # * level - The level at which the validation should occur # * if - A block that when executed must return true of the validation will not occur # * group - The group that this validation belongs to. A validation can belong to multiple groups def validates_presence_of(*args) add_validations(args, ValidatesPresenceOf) end # call-seq: validates_true_for(*args) # # Validates that the logic evaluates to true # # class Person # include Validatable # validates_true_for :first_name, :logic => lambda { first_name == 'Jamie' } # end # # The logic option is required. # # Configuration options: # # * after_validate - A block that executes following the run of a validation # * message - The message to add to the errors collection when the validation fails # * times - The number of times the validation applies # * level - The level at which the validation should occur # * if - A block that when executed must return true of the validation will not occur # * group - The group that this validation belongs to. A validation can belong to multiple groups # * logic - A block that executes to perform the validation def validates_true_for(*args) add_validations(args, ValidatesTrueFor) end # call-seq: include_validations_from(attribute) # # Includes all the validations that are defined on the attribute. # class Person # include Validatable # validates_presence_of :name # end # # class PersonPresenter # include Validatable # include_validataions_from :person # attr_accessor :person # def name # person.name # end # # def initialize(person) # @person = person # end # end # # presenter = PersonPresenter.new(Person.new) # presenter.valid? #=> false # presenter.errors.on(:name) #=> "can't be blank" # # The name attribute whose validations should be added. def include_validations_from(attribute_to_validate, options = {}) validations_to_include << IncludedValidation.new(attribute_to_validate) end # call-seq: include_errors_from(attribute_to_validate, options = {}) # # Validates the specified attributes. # class Person # include Validatable # validates_presence_of :name # attr_accessor :name # end # # class PersonPresenter # include Validatable # include_errors_from :person, :map => { :name => :namen }, :if => lambda { not person.nil? } # attr_accessor :person # # def initialize(person) # @person = person # end # end # # presenter = PersonPresenter.new(Person.new) # presenter.valid? #=> false # presenter.errors.on(:namen) #=> "can't be blank" # # The person attribute will be validated. # If person is invalid the errors will be added to the PersonPresenter errors collection. # # Configuration options: # # * map - A hash that maps attributes of the child to attributes of the parent. # * if - A block that when executed must return true of the validation will not occur. def include_errors_from(attribute_to_validate, options = {}) children_to_validate << ChildValidation.new(attribute_to_validate, options[:map] || {}, options[:if] || lambda { true }) end def include_validations_for(attribute_to_validate, options = {}) #:nodoc: puts "include_validations_for is deprecated; use include_errors_from instead" children_to_validate << ChildValidation.new(attribute_to_validate, options[:map] || {}, options[:if] || lambda { true }) end # call-seq: before_validation(&block) # # Is called before valid? or valid_for_*? # # class Person # include Validatable # before_validation do # self.name = "default name" # end # # attr_accessor :name # end # def before_validation(&block) before_validations << block end end endvalidatable-1.6.7/lib/validations/0000755000175000017500000000000011755015216016441 5ustar boutilboutilvalidatable-1.6.7/lib/validations/validates_presence_of.rb0000644000175000017500000000054711755015216023320 0ustar boutilboutilmodule Validatable class ValidatesPresenceOf < ValidationBase #:nodoc: def valid?(instance) return false if instance.send(self.attribute).nil? instance.send(self.attribute).respond_to?(:strip) ? instance.send(self.attribute).strip.length != 0 : true end def message(instance) super || "can't be empty" end end end validatable-1.6.7/lib/validations/validates_length_of.rb0000644000175000017500000000121011755015216022761 0ustar boutilboutilmodule Validatable class ValidatesLengthOf < ValidationBase #:nodoc: option :minimum, :maximum, :is, :within, :allow_nil def message(instance) super || "is invalid" end def valid?(instance) valid = true value = instance.send(self.attribute) if value.nil? return true if allow_nil value = "" end valid &&= value.length <= maximum unless maximum.nil? valid &&= value.length >= minimum unless minimum.nil? valid &&= value.length == is unless is.nil? valid &&= within.include?(value.length) unless within.nil? valid end end endvalidatable-1.6.7/lib/validations/validates_confirmation_of.rb0000644000175000017500000000077011755015216024202 0ustar boutilboutilmodule Validatable class ValidatesConfirmationOf < ValidationBase #:nodoc: option :case_sensitive default :case_sensitive => true def valid?(instance) return instance.send(self.attribute) == instance.send("#{self.attribute}_confirmation".to_sym) if case_sensitive instance.send(self.attribute).to_s.casecmp(instance.send("#{self.attribute}_confirmation".to_sym).to_s) == 0 end def message(instance) super || "doesn't match confirmation" end end endvalidatable-1.6.7/lib/validations/validation_base.rb0000644000175000017500000000517611755015216022123 0ustar boutilboutilmodule Validatable class ValidationBase #:nodoc: class << self def required_option(*args) option(*args) requires(*args) end def option(*args) attr_accessor(*args) understands(*args) end def default(hash) defaults.merge! hash end def defaults @defaults ||= {} end def all_defaults return defaults.merge(self.superclass.all_defaults) if self.superclass.respond_to? :all_defaults defaults end def after_validate(&block) after_validations << block end def after_validations @after_validations ||= [] end def all_after_validations return after_validations + self.superclass.all_after_validations if self.superclass.respond_to? :all_after_validations after_validations end end include Understandable include Requireable option :message, :if, :times, :level, :groups, :key, :after_validate default :level => 1, :groups => [] attr_accessor :attribute def initialize(klass, attribute, options={}) must_understand options requires options self.class.all_understandings.each do |understanding| options[understanding] = self.class.all_defaults[understanding] unless options.has_key? understanding self.instance_variable_set("@#{understanding}", options[understanding]) end self.attribute = attribute self.groups = [self.groups] unless self.groups.is_a?(Array) self.key = "#{klass.name}/#{self.class.name}/#{self.key || self.attribute}" raise_error_if_key_is_dup(klass) end def raise_error_if_key_is_dup(klass) message = "key #{self.key} must be unique, provide the :key option to specify a unique key" raise ArgumentError.new(message) if klass.validation_keys_include? self.key end def should_validate?(instance) result = validate_this_time?(instance) result &&= instance.instance_eval(&self.if) unless self.if.nil? result end def message(instance) @message.respond_to?(:call) ? instance.instance_eval(&@message) : @message end def validate_this_time?(instance) return true if @times.nil? self.times > instance.times_validated(self.key) end def run_after_validate(result, instance, attribute) self.class.all_after_validations.each do |block| block.call result, instance, attribute end instance.instance_eval_with_params result, attribute, &self.after_validate unless self.after_validate.nil? end end endvalidatable-1.6.7/lib/validations/validates_acceptance_of.rb0000644000175000017500000000035411755015216023576 0ustar boutilboutilmodule Validatable class ValidatesAcceptanceOf < ValidationBase #:nodoc: def valid?(instance) instance.send(self.attribute) == "true" end def message(instance) super || "must be accepted" end end endvalidatable-1.6.7/lib/validations/validates_each.rb0000644000175000017500000000052511755015216021724 0ustar boutilboutilmodule Validatable class ValidatesEach < ValidationBase #:nodoc: required_option :logic def valid?(instance) instance.instance_eval(&logic) true # return true so no error is added. should look in the future at doing this different. end def message(instance) super || "is invalid" end end endvalidatable-1.6.7/lib/validations/validates_numericality_of.rb0000644000175000017500000000056711755015216024223 0ustar boutilboutilmodule Validatable class ValidatesNumericalityOf < ValidationBase #:nodoc: option :only_integer def valid?(instance) value = instance.send(self.attribute).to_s regex = self.only_integer ? /\A[+-]?\d+\Z/ : /^\d*\.{0,1}\d+$/ not (value =~ regex).nil? end def message(instance) super || "must be a number" end end end validatable-1.6.7/lib/validations/validates_true_for.rb0000644000175000017500000000037611755015216022655 0ustar boutilboutilmodule Validatable class ValidatesTrueFor < ValidationBase #:nodoc: required_option :logic def valid?(instance) instance.instance_eval(&logic) == true end def message(instance) super || "is invalid" end end endvalidatable-1.6.7/lib/validations/validates_format_of.rb0000644000175000017500000000042211755015216022774 0ustar boutilboutilmodule Validatable class ValidatesFormatOf < ValidationBase #:nodoc: required_option :with def valid?(instance) not (instance.send(self.attribute).to_s =~ self.with).nil? end def message(instance) super || "is invalid" end end endvalidatable-1.6.7/lib/requireable.rb0000644000175000017500000000125211755015216016751 0ustar boutilboutilmodule Validatable module Requireable #:nodoc: module ClassMethods #:nodoc: def requires(*args) required_options.concat args end def required_options @required_options ||= [] end end def self.included(klass) klass.extend ClassMethods end def requires(options) required_options = self.class.required_options.inject([]) do |errors, attribute| errors << attribute.to_s unless options.has_key?(attribute) errors end raise ArgumentError.new("#{self.class} requires options: #{required_options.join(', ')}") if required_options.any? true end end endvalidatable-1.6.7/lib/child_validation.rb0000644000175000017500000000060111755015216017743 0ustar boutilboutilmodule Validatable class ChildValidation #:nodoc: attr_accessor :attribute, :map, :should_validate_proc def initialize(attribute, map, should_validate_proc) @attribute = attribute @map = map @should_validate_proc = should_validate_proc end def should_validate?(instance) instance.instance_eval &should_validate_proc end end endvalidatable-1.6.7/lib/included_validation.rb0000644000175000017500000000024511755015216020453 0ustar boutilboutilmodule Validatable class IncludedValidation #:nodoc: attr_accessor :attribute def initialize(attribute) @attribute = attribute end end endvalidatable-1.6.7/lib/validatable_instance_methods.rb0000644000175000017500000000704611755015216022337 0ustar boutilboutilmodule Validatable def self.included(klass) #:nodoc: klass.extend Validatable::ClassMethods klass.extend Validatable::Macros end # call-seq: valid? # # Returns true if no errors were added otherwise false. Only executes validations that have no :groups option specified def valid? valid_for_group?(nil) end # call-seq: errors # # Returns the Errors object that holds all information about attribute error messages. def errors @errors ||= Validatable::Errors.new end def valid_for_group?(group) #:nodoc: run_before_validations errors.clear self.class.validate_children(self, group) self.validate(group) errors.empty? end def times_validated(key) #:nodoc: times_validated_hash[key] || 0 end def increment_times_validated_for(validation) #:nodoc: if validation.key != nil if times_validated_hash[validation.key].nil? times_validated_hash[validation.key] = 1 else times_validated_hash[validation.key] += 1 end end end # call-seq: validate_only(key) # # Only executes a specified validation. The argument should follow a pattern based on the key of the validation. # Examples: # * validates_presence_of :name can be run with obj.validate_only("presence_of/name") # * validates_presence_of :birthday, :key => "a key" can be run with obj.validate_only("presence_of/a key") def validate_only(key) validation_name, attribute_name = key.split("/") validation_name = validation_name.split("_").collect{|word| word.capitalize}.join validation_key = "#{self.class.name}/Validatable::Validates#{validation_name}/#{attribute_name}" validation = self.class.all_validations.find { |validation| validation.key == validation_key } raise ArgumentError.new("validation with key #{validation_key} could not be found") if validation.nil? errors.clear run_validation(validation) end protected def times_validated_hash #:nodoc: @times_validated_hash ||= {} end def validate(group) #:nodoc: validation_levels.each do |level| validations_for_level_and_group(level, group).each do |validation| run_validation(validation) if validation.should_validate?(self) end return unless self.errors.empty? end end def run_validation(validation) #:nodoc: validation_result = validation.valid?(self) add_error(validation.attribute, validation.message(self)) unless validation_result increment_times_validated_for(validation) validation.run_after_validate(validation_result, self, validation.attribute) end def run_before_validations #:nodoc: self.class.all_before_validations.each do |block| instance_eval &block end end def add_error(attribute, message) #:nodoc: self.class.add_error(self, attribute, message) end def validations_for_level_and_group(level, group) #:nodoc: validations_for_level = self.all_validations.select { |validation| validation.level == level } return validations_for_level.select { |validation| validation.groups.empty? } if group.nil? validations_for_level.select { |validation| validation.groups.include?(group) } end def all_validations #:nodoc: res = self.class.validations_to_include.inject(self.class.all_validations) do |result, included_validation_class| result += self.send(included_validation_class.attribute).all_validations result end end def validation_levels #:nodoc: self.class.all_validations.inject([1]) { |result, validation| result << validation.level }.uniq.sort end end