diff options
Diffstat (limited to 'activemodel')
29 files changed, 348 insertions, 133 deletions
diff --git a/activemodel/MIT-LICENSE b/activemodel/MIT-LICENSE new file mode 100644 index 0000000000..e7accc5ea1 --- /dev/null +++ b/activemodel/MIT-LICENSE @@ -0,0 +1,21 @@ +Copyright (c) 2004-2009 David Heinemeier Hansson + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/activemodel/Rakefile b/activemodel/Rakefile index ae2fbdb002..fd69a557aa 100755 --- a/activemodel/Rakefile +++ b/activemodel/Rakefile @@ -1,7 +1,13 @@ -#!/usr/bin/env ruby -require 'rake' +require File.join(File.dirname(__FILE__), 'lib', 'active_model', 'version') + +PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : '' +PKG_NAME = 'activemodel' +PKG_VERSION = ActiveModel::VERSION::STRING + PKG_BUILD +PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}" +RELEASE_NAME = "REL #{PKG_VERSION}" + + require 'rake/testtask' -require 'rake/rdoctask' task :default => :test @@ -9,14 +15,19 @@ Rake::TestTask.new do |t| t.libs << "test" t.test_files = Dir.glob("test/cases/**/*_test.rb").sort t.verbose = true + t.warning = true end + task :isolated_test do ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) Dir.glob("test/**/*_test.rb").all? do |file| - system(ruby, '-Ilib:test', file) + system(ruby, '-w', '-Ilib:test', file) end or raise "Failures" end + +require 'rake/rdoctask' + # Generate the RDoc documentation Rake::RDocTask.new do |rdoc| rdoc.rdoc_dir = 'doc' @@ -27,3 +38,15 @@ Rake::RDocTask.new do |rdoc| rdoc.rdoc_files.include('README', 'CHANGES') rdoc.rdoc_files.include('lib/**/*.rb') end + + +require 'rake/packagetask' +require 'rake/gempackagetask' + +spec = eval(File.read('activemodel.gemspec')) + +Rake::GemPackageTask.new(spec) do |p| + p.gem_spec = spec + p.need_tar = true + p.need_zip = true +end diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec new file mode 100644 index 0000000000..c3ff624419 --- /dev/null +++ b/activemodel/activemodel.gemspec @@ -0,0 +1,19 @@ +Gem::Specification.new do |s| + s.platform = Gem::Platform::RUBY + s.name = 'activemodel' + s.version = '3.0.pre' + s.summary = "A toolkit for building other modeling frameworks like ActiveRecord" + s.description = %q{Extracts common modeling concerns from ActiveRecord to share between similar frameworks like ActiveResource.} + + s.author = "David Heinemeier Hansson" + s.email = "david@loudthinking.com" + s.rubyforge_project = "activemodel" + s.homepage = "http://www.rubyonrails.org" + + s.has_rdoc = true + + s.add_dependency('activesupport', '= 3.0.pre') + + s.require_path = 'lib' + s.files = Dir["CHANGELOG", "MIT-LICENSE", "README", "lib/**/*"] +end diff --git a/activemodel/examples/validations.rb b/activemodel/examples/validations.rb index 3f8311ff96..b039897ea5 100644 --- a/activemodel/examples/validations.rb +++ b/activemodel/examples/validations.rb @@ -1,4 +1,4 @@ -require 'activemodel' +require 'active_model' class Person include ActiveModel::Conversion diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index 244f3a546e..67f529262d 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -31,6 +31,7 @@ module ActiveModel autoload :DeprecatedErrorMethods, 'active_model/deprecated_error_methods' autoload :Dirty, 'active_model/dirty' autoload :Errors, 'active_model/errors' + autoload :Lint, 'active_model/lint' autoload :Name, 'active_model/naming' autoload :Naming, 'active_model/naming' autoload :Observer, 'active_model/observing' @@ -40,6 +41,7 @@ module ActiveModel autoload :TestCase, 'active_model/test_case' autoload :Validations, 'active_model/validations' autoload :ValidationsRepairHelper, 'active_model/validations_repair_helper' + autoload :VERSION, 'active_model/version' module Serializers autoload :JSON, 'active_model/serializers/json' diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index 1091ad3095..aa35a2726e 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -181,7 +181,7 @@ module ActiveModel end def attribute_methods_generated? - @attribute_methods_generated ? true : false + @attribute_methods_generated ||= nil end protected diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb index 624c3647ca..735c61df74 100644 --- a/activemodel/lib/active_model/dirty.rb +++ b/activemodel/lib/active_model/dirty.rb @@ -72,12 +72,26 @@ module ActiveModel changed.inject({}) { |h, attr| h[attr] = attribute_change(attr); h } end + # Map of attributes that were changed when the model was saved. + # person.name # => 'bob' + # person.name = 'robert' + # person.save + # person.previous_changes # => {'name' => ['bob, 'robert']} + def previous_changes + previously_changed_attributes + end + private # Map of change <tt>attr => original value</tt>. def changed_attributes @changed_attributes ||= {} end + # Map of fields that were changed when the model was saved + def previously_changed_attributes + @previously_changed || {} + end + # Handle <tt>*_changed?</tt> for +method_missing+. def attribute_changed?(attr) changed_attributes.include?(attr) diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index 7a3001174f..7a48960f89 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -1,7 +1,8 @@ require 'active_support/core_ext/string/inflections' +require 'active_support/ordered_hash' module ActiveModel - class Errors < Hash + class Errors < ActiveSupport::OrderedHash include DeprecatedErrorMethods def initialize(base) @@ -113,7 +114,7 @@ module ActiveModel full_messages end - # Translates an error message in it's default scope (<tt>activemodel.errrors.messages</tt>). + # Translates an error message in its default scope (<tt>activemodel.errors.messages</tt>). # Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>, if it's not there, # it's looked up in <tt>models.MODEL.MESSAGE</tt> and if that is not there it returns the translation of the # default message (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model name, diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb new file mode 100644 index 0000000000..ceaa29dc8c --- /dev/null +++ b/activemodel/lib/active_model/lint.rb @@ -0,0 +1,95 @@ +# You can test whether an object is compliant with the ActiveModel API by +# calling ActiveModel::Lint.test(object). It will emit a Test::Unit +# output that tells you whether your object is fully compliant, or if not, +# which aspects of the API are not implemented. +# +# These tests do not attempt to determine the semantic correctness of the +# returned values. For instance, you could implement valid? to always +# return true, and the tests would pass. It is up to you to ensure that +# the values are semantically meaningful. +# +# Objects you pass in are expected to return a compliant object from a +# call to to_model. It is perfectly fine for to_model to return self. +module ActiveModel + module Lint + def self.test(object, verbosity = 2, output = STDOUT) + require "test/unit" + require "test/unit/ui/console/testrunner" + + test_class = Class.new(::Test::Unit::TestCase) do + include Test + + define_method(:setup) do + assert object.respond_to?(:to_model), "The object should respond_to :to_model" + @object = object.to_model + super + end + end + + ::Test::Unit::UI::Console::TestRunner.new(test_class, verbosity, output).start + end + + module Test + def assert_boolean(name, result) + assert result == true || result == false, "#{name} should be a boolean" + end + + # valid? + # ------ + # + # Returns a boolean that specifies whether the object is in a valid or invalid + # state. + def test_valid? + assert @object.respond_to?(:valid?), "The model should respond to valid?" + assert_boolean "valid?", @object.valid? + end + + # new_record? + # ----------- + # + # Returns a boolean that specifies whether the object has been persisted yet. + # This is used when calculating the URL for an object. If the object is + # not persisted, a form for that object, for instance, will be POSTed to the + # collection. If it is persisted, a form for the object will put PUTed to the + # URL for the object. + def test_new_record? + assert @object.respond_to?(:new_record?), "The model should respond to new_record?" + assert_boolean "new_record?", @object.new_record? + end + + def test_destroyed? + assert @object.respond_to?(:destroyed?), "The model should respond to destroyed?" + assert_boolean "destroyed?", @object.destroyed? + end + + # errors + # ------ + # + # Returns an object that has :[] and :full_messages defined on it. See below + # for more details. + def setup + assert @object.respond_to?(:errors), "The model should respond to errors" + @errors = @object.errors + end + + # This module tests the #errors object + module Errors + # Returns an Array of Strings that are the errors for the attribute in + # question. If localization is used, the Strings should be localized + # for the current locale. If no error is present, this method should + # return an empty Array. + def test_errors_aref + assert @errors[:hello].is_a?(Array), "errors#[] should return an Array" + end + + # Returns an Array of all error messages for the object. Each message + # should contain information about the field, if applicable. + def test_errors_full_messages + assert @errors.full_messages.is_a?(Array), "errors#full_messages should return an Array" + end + end + + include Errors + end + end +end diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb index 3b230c43b9..d9d1ab8967 100644 --- a/activemodel/lib/active_model/observing.rb +++ b/activemodel/lib/active_model/observing.rb @@ -40,23 +40,6 @@ module ActiveModel observers.each { |o| instantiate_observer(o) } end - # Wraps methods with before and after notifications. - # - # wrap_with_notifications :create, :save, :update, :destroy - def wrap_with_notifications(*methods) - methods.each do |method| - class_eval(<<-EOS, __FILE__, __LINE__ + 1) - def #{method}_with_notifications(*args, &block) - notify_observers(:before_#{method}) - result = #{method}_without_notifications(*args, &block) - notify_observers(:after_#{method}) - result - end - EOS - alias_method_chain(method, :notifications) - end - end - protected def instantiate_observer(observer) #:nodoc: # string/symbol diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb index 7d49e60790..edeb508a08 100644 --- a/activemodel/lib/active_model/validations.rb +++ b/activemodel/lib/active_model/validations.rb @@ -6,10 +6,10 @@ require 'active_support/callbacks' module ActiveModel module Validations extend ActiveSupport::Concern - include ActiveSupport::Callbacks + include ActiveSupport::NewCallbacks included do - define_callbacks :validate + define_callbacks :validate, :scope => :name end module ClassMethods @@ -64,7 +64,7 @@ module ActiveModel attrs = attrs.flatten # Declare the validation. - send(validation_method(options[:on]), options) do |record| + validate options do |record| attrs.each do |attr| value = record.send(:read_attribute_for_validation, attr) next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank]) @@ -73,10 +73,14 @@ module ActiveModel end end - private - def validation_method(on) - :validate + def validate(*args, &block) + options = args.last + if options.is_a?(Hash) && options.key?(:on) + options[:if] = Array(options[:if]) + options[:if] << "@_on_validate == :#{options[:on]}" end + set_callback(:validate, *args, &block) + end end # Returns the Errors object that holds all information about attribute error messages. @@ -87,7 +91,7 @@ module ActiveModel # Runs all the specified validations and returns true if no errors were added otherwise false. def valid? errors.clear - run_callbacks(:validate) + _run_validate_callbacks errors.empty? end diff --git a/activemodel/lib/active_model/validations/numericality.rb b/activemodel/lib/active_model/validations/numericality.rb index ada6e28594..32dbcd82d0 100644 --- a/activemodel/lib/active_model/validations/numericality.rb +++ b/activemodel/lib/active_model/validations/numericality.rb @@ -31,6 +31,21 @@ module ActiveModel # * <tt>:unless</tt> - Specifies a method, proc or string to call to determine if the validation should # not occur (e.g. <tt>:unless => :skip_validation</tt>, or <tt>:unless => Proc.new { |user| user.signup_step <= 2 }</tt>). The # method, proc or string should return or evaluate to a true or false value. + # + # The following checks can also be supplied with a proc or a symbol which corresponds to a method: + # * <tt>:greater_than</tt> + # * <tt>:greater_than_or_equal_to</tt> + # * <tt>:equal_to</tt> + # * <tt>:less_than</tt> + # * <tt>:less_than_or_equal_to</tt> + # + # class Person < ActiveRecord::Base + # validates_numericality_of :width, :less_than => Proc.new { |person| person.height } + # validates_numericality_of :width, :greater_than => :minimum_weight + # end + # + # + def validates_numericality_of(*attr_names) configuration = { :only_integer => false, :allow_nil => false } configuration.update(attr_names.extract_options!) @@ -38,7 +53,8 @@ module ActiveModel numericality_options = ALL_NUMERICALITY_CHECKS.keys & configuration.keys (numericality_options - [ :odd, :even ]).each do |option| - raise ArgumentError, ":#{option} must be a number" unless configuration[option].is_a?(Numeric) + value = configuration[option] + raise ArgumentError, ":#{option} must be a number, a symbol or a proc" unless value.is_a?(Numeric) || value.is_a?(Proc) || value.is_a?(Symbol) end validates_each(attr_names,configuration) do |record, attr_name, value| @@ -74,6 +90,9 @@ module ActiveModel record.errors.add(attr_name, option, :value => raw_value, :default => configuration[:message]) end else + configuration[option] = configuration[option].call(record) if configuration[option].is_a? Proc + configuration[option] = record.method(configuration[option]).call if configuration[option].is_a? Symbol + unless raw_value.method(ALL_NUMERICALITY_CHECKS[option])[configuration[option]] record.errors.add(attr_name, option, :default => configuration[:message], :value => raw_value, :count => configuration[option]) end diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb index 72d6b1c6f0..3ff677c137 100644 --- a/activemodel/lib/active_model/validations/presence.rb +++ b/activemodel/lib/active_model/validations/presence.rb @@ -32,7 +32,7 @@ module ActiveModel # can't use validates_each here, because it cannot cope with nonexistent attributes, # while errors.add_on_empty can - send(validation_method(configuration[:on]), configuration) do |record| + validate configuration do |record| record.errors.add_on_blank(attr_names, configuration[:message]) end end diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb index 851cdfebf0..edc2133ddc 100644 --- a/activemodel/lib/active_model/validations/with.rb +++ b/activemodel/lib/active_model/validations/with.rb @@ -51,7 +51,7 @@ module ActiveModel def validates_with(*args) configuration = args.extract_options! - send(validation_method(configuration[:on]), configuration) do |record| + validate configuration do |record| args.each do |klass| klass.new(record, configuration.except(:on, :if, :unless)).validate end diff --git a/activemodel/lib/active_model/validations_repair_helper.rb b/activemodel/lib/active_model/validations_repair_helper.rb index 432e411308..40741e6dbe 100644 --- a/activemodel/lib/active_model/validations_repair_helper.rb +++ b/activemodel/lib/active_model/validations_repair_helper.rb @@ -2,43 +2,34 @@ module ActiveModel module ValidationsRepairHelper extend ActiveSupport::Concern - module Toolbox - def self.record_validations(*model_classes) - model_classes.inject({}) do |repair, klass| - repair[klass] ||= {} - [:validate, :validate_on_create, :validate_on_update].each do |callback| - the_callback = klass.instance_variable_get("@#{callback.to_s}_callbacks") - repair[klass][callback] = (the_callback.nil? ? nil : the_callback.dup) - end - repair - end - end - - def self.reset_validations(recorded) - recorded.each do |klass, repairs| - [:validate, :validate_on_create, :validate_on_update].each do |callback| - klass.instance_variable_set("@#{callback.to_s}_callbacks", repairs[callback]) - end - end - end - end - module ClassMethods def repair_validations(*model_classes) setup do - @validation_repairs = Toolbox.record_validations(*model_classes) + @_stored_callbacks = {} + model_classes.each do |k| + @_stored_callbacks[k] = k._validate_callbacks.dup + end end teardown do - Toolbox.reset_validations(@validation_repairs) + model_classes.each do |k| + k._validate_callbacks = @_stored_callbacks[k] + k.__update_callbacks(:validate) + end end end end def repair_validations(*model_classes, &block) - validation_repairs = Toolbox.record_validations(*model_classes) + @__stored_callbacks = {} + model_classes.each do |k| + @__stored_callbacks[k] = k._validate_callbacks.dup + end return block.call ensure - Toolbox.reset_validations(validation_repairs) + model_classes.each do |k| + k._validate_callbacks = @__stored_callbacks[k] + k.__update_callbacks(:validate) + end end end end diff --git a/activemodel/lib/active_model/version.rb b/activemodel/lib/active_model/version.rb new file mode 100644 index 0000000000..0c233b7b4f --- /dev/null +++ b/activemodel/lib/active_model/version.rb @@ -0,0 +1,9 @@ +module ActiveModel + module VERSION #:nodoc: + MAJOR = 3 + MINOR = 0 + TINY = "pre" + + STRING = [MAJOR, MINOR, TINY].join('.') + end +end diff --git a/activemodel/lib/activemodel.rb b/activemodel/lib/activemodel.rb deleted file mode 100644 index da3133103b..0000000000 --- a/activemodel/lib/activemodel.rb +++ /dev/null @@ -1 +0,0 @@ -require 'active_model' diff --git a/activemodel/test/cases/lint_test.rb b/activemodel/test/cases/lint_test.rb new file mode 100644 index 0000000000..ed576a91e2 --- /dev/null +++ b/activemodel/test/cases/lint_test.rb @@ -0,0 +1,50 @@ +require "cases/helper" + +class TestLint < ActiveModel::TestCase + class CompliantObject + def to_model + self + end + + def valid?() true end + def new_record?() true end + def destroyed?() true end + + def errors + obj = Object.new + def obj.[](key) [] end + def obj.full_messages() [] end + obj + end + end + + def assert_output(object, failures, errors, *test_names) + ActiveModel::Lint.test(object, 3, output = StringIO.new) + regex = %r{#{failures} failures, #{errors} errors} + assert_match regex, output.string + + test_names.each do |test_name| + assert_match test_name, output.string + end + end + + def test_valid + assert_output(CompliantObject.new, 0, 0, /test_valid/) + end + + def test_new_record + assert_output(CompliantObject.new, 0, 0, /test_new_record?/) + end + + def test_destroyed + assert_output(CompliantObject.new, 0, 0, /test_destroyed/) + end + + def test_errors_aref + assert_output(CompliantObject.new, 0, 0, /test_errors_aref/) + end + + def test_errors_full_messages + assert_output(CompliantObject.new, 0, 0, /test_errors_aref/) + end +end diff --git a/activemodel/test/cases/naming_test.rb b/activemodel/test/cases/naming_test.rb index e75d4541a3..4d97af3d13 100644 --- a/activemodel/test/cases/naming_test.rb +++ b/activemodel/test/cases/naming_test.rb @@ -1,6 +1,6 @@ require 'cases/helper' -class NamingTest < Test::Unit::TestCase +class NamingTest < ActiveModel::TestCase def setup @model_name = ActiveModel::Name.new('Post::TrackBack') end diff --git a/activemodel/test/cases/observing_test.rb b/activemodel/test/cases/observing_test.rb index fbf93c19ef..e23bda0528 100644 --- a/activemodel/test/cases/observing_test.rb +++ b/activemodel/test/cases/observing_test.rb @@ -78,6 +78,7 @@ class ObserverTest < ActiveModel::TestCase def teardown FooObserver.instance_eval do + undef_method :observed_classes alias_method :observed_classes, :original_observed_classes end end diff --git a/activemodel/test/cases/serializeration/json_serialization_test.rb b/activemodel/test/cases/serializeration/json_serialization_test.rb index 6227aedc39..81df52fcb9 100644 --- a/activemodel/test/cases/serializeration/json_serialization_test.rb +++ b/activemodel/test/cases/serializeration/json_serialization_test.rb @@ -1,12 +1,14 @@ require 'cases/helper' require 'models/contact' +require 'active_support/core_ext/object/instance_variables' class Contact + extend ActiveModel::Naming include ActiveModel::Serializers::JSON def attributes instance_values - end + end unless method_defined?(:attributes) end class JsonSerializationTest < ActiveModel::TestCase diff --git a/activemodel/test/cases/serializeration/xml_serialization_test.rb b/activemodel/test/cases/serializeration/xml_serialization_test.rb index 428e5a6bd1..6340aad531 100644 --- a/activemodel/test/cases/serializeration/xml_serialization_test.rb +++ b/activemodel/test/cases/serializeration/xml_serialization_test.rb @@ -1,12 +1,14 @@ require 'cases/helper' require 'models/contact' +require 'active_support/core_ext/object/instance_variables' class Contact + extend ActiveModel::Naming include ActiveModel::Serializers::Xml def attributes instance_values - end + end unless method_defined?(:attributes) end module Admin diff --git a/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb b/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb index 37bba5e95e..443a81c6ac 100644 --- a/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_generate_message_validation_test.rb @@ -3,9 +3,9 @@ require 'cases/tests_database' require 'models/person' -class I18nGenerateMessageValidationTest < Test::Unit::TestCase +class I18nGenerateMessageValidationTest < ActiveModel::TestCase def setup - reset_callbacks Person + Person.reset_callbacks(:validate) @person = Person.new @old_load_path, @old_backend = I18n.load_path, I18n.backend @@ -45,12 +45,6 @@ class I18nGenerateMessageValidationTest < Test::Unit::TestCase I18n.backend = @old_backend end - def reset_callbacks(*models) - models.each do |model| - model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - end - end - # validates_inclusion_of: generate_message(attr_name, :inclusion, :default => configuration[:message], :value => value) def test_generate_message_inclusion_with_default_message assert_equal 'is not included in the list', @person.errors.generate_message(:title, :inclusion, :default => nil, :value => 'title') diff --git a/activemodel/test/cases/validations/i18n_validation_test.rb b/activemodel/test/cases/validations/i18n_validation_test.rb index cc68d847a2..fc4f1926b0 100644 --- a/activemodel/test/cases/validations/i18n_validation_test.rb +++ b/activemodel/test/cases/validations/i18n_validation_test.rb @@ -7,8 +7,7 @@ class I18nValidationTest < ActiveModel::TestCase include ActiveModel::TestsDatabase def setup - reset_callbacks Person - + Person.reset_callbacks(:validate) @person = Person.new @old_load_path, @old_backend = I18n.load_path, I18n.backend @@ -18,17 +17,11 @@ class I18nValidationTest < ActiveModel::TestCase end def teardown - reset_callbacks Person + Person.reset_callbacks(:validate) I18n.load_path.replace @old_load_path I18n.backend = @old_backend end - def reset_callbacks(*models) - models.each do |model| - model.instance_variable_set("@validate_callbacks", ActiveSupport::Callbacks::CallbackChain.new) - end - end - def test_percent_s_interpolation_syntax_in_error_messages_was_deprecated assert_not_deprecated do default = "%s interpolation syntax was deprecated" @@ -107,32 +100,6 @@ class I18nValidationTest < ActiveModel::TestCase @person.valid? end - def test_validates_length_of_within_generates_message_with_title_too_short - Person.validates_length_of :title, :within => 3..5 - @person.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => nil}) - @person.valid? - end - - def test_validates_length_of_within_generates_message_with_title_too_short_and_custom_default_message - Person.validates_length_of :title, :within => 3..5, :too_short => 'custom' - @person.errors.expects(:generate_message).with(:title, :too_short, {:count => 3, :default => 'custom'}) - @person.valid? - end - - def test_validates_length_of_within_generates_message_with_title_too_long - Person.validates_length_of :title, :within => 3..5 - @person.title = 'this title is too long' - @person.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => nil}) - @person.valid? - end - - def test_validates_length_of_within_generates_message_with_title_too_long_and_custom_default_message - Person.validates_length_of :title, :within => 3..5, :too_long => 'custom' - @person.title = 'this title is too long' - @person.errors.expects(:generate_message).with(:title, :too_long, {:count => 5, :default => 'custom'}) - @person.valid? - end - # validates_length_of :within w/ mocha def test_validates_length_of_within_generates_message_with_title_too_short @@ -280,7 +247,7 @@ class I18nValidationTest < ActiveModel::TestCase @person.valid? end - def test_validates_numericality_of_odd_generates_message_with_custom_default_message + def test_validates_numericality_of_less_than_odd_generates_message_with_custom_default_message Person.validates_numericality_of :title, :only_integer => true, :less_than => 0, :message => 'custom' @person.title = 1 @person.errors.expects(:generate_message).with(:title, :less_than, {:value => 1, :count => 0, :default => 'custom'}) @@ -384,24 +351,6 @@ class I18nValidationTest < ActiveModel::TestCase assert_equal ['global message'], @person.errors[:title] end - def test_validates_length_of_is_finds_custom_model_key_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:models => {:person => {:attributes => {:title => {:wrong_length => 'custom message'}}}}}} - I18n.backend.store_translations 'en', :activemodel => {:errors => {:messages => {:wrong_length => 'global message'}}} - - Person.validates_length_of :title, :is => 5 - @person.valid? - assert_equal ['custom message'], @person.errors[:title] - end - - def test_validates_length_of_is_finds_global_default_translation - I18n.backend.store_translations 'en', :activemodel => {:errors => {:messages => {:wrong_length => 'global message'}}} - - Person.validates_length_of :title, :is => 5 - @person.valid? - assert_equal ['global message'], @person.errors[:title] - end - - # validates_format_of w/o mocha def test_validates_format_of_finds_custom_model_key_translation @@ -576,4 +525,4 @@ class I18nValidationTest < ActiveModel::TestCase assert_equal ["I am a custom error"], @person.errors[:title] end -end
\ No newline at end of file +end diff --git a/activemodel/test/cases/validations/numericality_validation_test.rb b/activemodel/test/cases/validations/numericality_validation_test.rb index 0af6eb69ce..d3201966dc 100644 --- a/activemodel/test/cases/validations/numericality_validation_test.rb +++ b/activemodel/test/cases/validations/numericality_validation_test.rb @@ -106,6 +106,24 @@ class NumericalityValidationTest < ActiveModel::TestCase valid!([2]) end + def test_validates_numericality_with_proc + Topic.send(:define_method, :min_approved, lambda { 5 }) + Topic.validates_numericality_of :approved, :greater_than_or_equal_to => Proc.new {|topic| topic.min_approved } + + invalid!([3, 4]) + valid!([5, 6]) + Topic.send(:remove_method, :min_approved) + end + + def test_validates_numericality_with_symbol + Topic.send(:define_method, :max_approved, lambda { 5 }) + Topic.validates_numericality_of :approved, :less_than_or_equal_to => :max_approved + + invalid!([6]) + valid!([4, 5]) + Topic.send(:remove_method, :max_approved) + end + def test_validates_numericality_with_numeric_message Topic.validates_numericality_of :approved, :less_than => 4, :message => "smaller than {{count}}" topic = Topic.new("title" => "numeric test", "approved" => 10) diff --git a/activemodel/test/cases/validations/presence_validation_test.rb b/activemodel/test/cases/validations/presence_validation_test.rb index bb6fb91774..90b0951a77 100644 --- a/activemodel/test/cases/validations/presence_validation_test.rb +++ b/activemodel/test/cases/validations/presence_validation_test.rb @@ -5,6 +5,7 @@ require 'cases/tests_database' require 'models/topic' require 'models/developer' require 'models/person' +require 'models/custom_reader' class PresenceValidationTest < ActiveModel::TestCase include ActiveModel::TestsDatabase diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb index f55fdc5864..c290b49a28 100644 --- a/activemodel/test/cases/validations/with_validation_test.rb +++ b/activemodel/test/cases/validations/with_validation_test.rb @@ -1,9 +1,11 @@ # encoding: utf-8 require 'cases/helper' +require 'cases/tests_database' require 'models/topic' class ValidatesWithTest < ActiveRecord::TestCase + include ActiveModel::TestsDatabase include ActiveModel::ValidationsRepairHelper repair_validations(Topic) diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb index 0b340e68bf..78565177d8 100644 --- a/activemodel/test/cases/validations_test.rb +++ b/activemodel/test/cases/validations_test.rb @@ -121,8 +121,8 @@ class ValidationsTest < ActiveModel::TestCase end def test_invalid_validator - Topic.validate 3 - assert_raise(ArgumentError) { t = Topic.create } + Topic.validate :i_dont_exist + assert_raise(NameError) { t = Topic.create } end def test_errors_to_xml @@ -141,6 +141,22 @@ class ValidationsTest < ActiveModel::TestCase t = Topic.new("title" => "") assert !t.valid? assert_equal "can't be blank", t.errors["title"].first + Topic.validates_presence_of :title, :author_name + Topic.validate {|topic| topic.errors.add('author_email_address', 'will never be valid')} + Topic.validates_length_of :title, :content, :minimum => 2 + + t = Topic.new :title => '' + assert !t.valid? + + assert_equal :title, key = t.errors.keys.first + assert_equal "can't be blank", t.errors[key].first + assert_equal 'is too short (minimum is 2 characters)', t.errors[key].second + assert_equal :author_name, key = t.errors.keys.second + assert_equal "can't be blank", t.errors[key].first + assert_equal :author_email_address, key = t.errors.keys.third + assert_equal 'will never be valid', t.errors[key].first + assert_equal :content, key = t.errors.keys.fourth + assert_equal 'is too short (minimum is 2 characters)', t.errors[key].first end def test_invalid_should_be_the_opposite_of_valid @@ -189,4 +205,4 @@ class ValidationsTest < ActiveModel::TestCase all_errors = t.errors.to_a assert_deprecated { assert_equal all_errors, t.errors.each_full{|err| err} } end -end
\ No newline at end of file +end diff --git a/activemodel/test/models/reply.rb b/activemodel/test/models/reply.rb index acfd801674..e86692677f 100644 --- a/activemodel/test/models/reply.rb +++ b/activemodel/test/models/reply.rb @@ -2,11 +2,11 @@ require 'models/topic' class Reply < Topic validate :errors_on_empty_content - validate_on_create :title_is_wrong_create + validate :title_is_wrong_create, :on => :create validate :check_empty_title - validate_on_create :check_content_mismatch - validate_on_update :check_wrong_update + validate :check_content_mismatch, :on => :create + validate :check_wrong_update, :on => :update attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read |