diff options
Diffstat (limited to 'activemodel')
25 files changed, 290 insertions, 95 deletions
diff --git a/activemodel/README.rdoc b/activemodel/README.rdoc index 9b96bfaba7..d9a9bdae3e 100644 --- a/activemodel/README.rdoc +++ b/activemodel/README.rdoc @@ -41,7 +41,7 @@ modules: define_model_callbacks :create def create - _run_create_callbacks do + run_callbacks :create do # Your create action methods here end end @@ -84,7 +84,7 @@ modules: attr_reader :errors def validate! - errors.add(:name, "can not be nil") if name == nil + errors.add(:name, "can not be nil") if name.nil? end def ErrorsPerson.human_attribute_name(attr, options = {}) @@ -94,10 +94,10 @@ modules: end person.errors.full_messages - # => ["Name Can not be nil"] + # => ["Name can not be nil"] person.errors.full_messages - # => ["Name Can not be nil"] + # => ["Name can not be nil"] {Learn more}[link:classes/ActiveModel/Errors.html] diff --git a/activemodel/Rakefile b/activemodel/Rakefile index 0372c7a03e..0a10912695 100755 --- a/activemodel/Rakefile +++ b/activemodel/Rakefile @@ -14,7 +14,7 @@ namespace :test do task :isolated do ruby = File.join(*RbConfig::CONFIG.values_at('bindir', 'RUBY_INSTALL_NAME')) Dir.glob("#{dir}/test/**/*_test.rb").all? do |file| - system(ruby, '-w', "-I#{dir}/lib", "-I#{dir}/test", file) + sh(ruby, '-w', "-I#{dir}/lib", "-I#{dir}/test", file) end or raise "Failures" end end diff --git a/activemodel/activemodel.gemspec b/activemodel/activemodel.gemspec index 64aa7ad922..fec9c7ff8b 100644 --- a/activemodel/activemodel.gemspec +++ b/activemodel/activemodel.gemspec @@ -22,6 +22,5 @@ Gem::Specification.new do |s| s.add_dependency('activesupport', version) s.add_dependency('builder', '~> 3.0.0') s.add_dependency('i18n', '~> 0.5.0') - s.add_dependency('bcrypt-ruby', '~> 2.1.2') - + s.add_dependency('bcrypt-ruby', '~> 2.1.4') end diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index fc5f5c4c66..8f3782eb48 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -98,7 +98,7 @@ module ActiveModel def define_attr_method(name, value=nil, &block) sing = singleton_class sing.class_eval <<-eorb, __FILE__, __LINE__ + 1 - if method_defined?(:'original_#{name}') + if method_defined?('original_#{name}') undef :'original_#{name}' end alias_method :'original_#{name}', :'#{name}' @@ -109,7 +109,7 @@ module ActiveModel # use eval instead of a block to work around a memory leak in dev # mode in fcgi sing.class_eval <<-eorb, __FILE__, __LINE__ + 1 - def #{name}; #{value.to_s.inspect}; end + def #{name}; #{value.nil? ? 'nil' : value.to_s.inspect}; end eorb end end @@ -229,15 +229,13 @@ module ActiveModel def alias_attribute(new_name, old_name) attribute_method_matchers.each do |matcher| - module_eval <<-STR, __FILE__, __LINE__ + 1 - def #{matcher.method_name(new_name)}(*args) - send(:#{matcher.method_name(old_name)}, *args) - end - STR + define_method(matcher.method_name(new_name)) do |*args| + send(matcher.method_name(old_name), *args) + end end end - # Declares a the attributes that should be prefixed and suffixed by + # Declares the attributes that should be prefixed and suffixed by # ActiveModel::AttributeMethods. # # To use, pass in an array of attribute names (as strings or symbols), @@ -274,11 +272,11 @@ module ActiveModel method_name = matcher.method_name(attr_name) generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1 - if method_defined?(:'#{method_name}') + if method_defined?('#{method_name}') undef :'#{method_name}' end - def #{method_name}(*args) - send(:#{matcher.method_missing_target}, '#{attr_name}', *args) + define_method('#{method_name}') do |*args| + send('#{matcher.method_missing_target}', '#{attr_name}', *args) end STR end @@ -325,7 +323,7 @@ module ActiveModel options.symbolize_keys! @prefix, @suffix = options[:prefix] || '', options[:suffix] || '' @regex = /^(#{Regexp.escape(@prefix)})(.+?)(#{Regexp.escape(@suffix)})$/ - @method_missing_target = :"#{@prefix}attribute#{@suffix}" + @method_missing_target = "#{@prefix}attribute#{@suffix}" @method_name = "#{prefix}%s#{suffix}" end diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb index aaa41f5ec6..2a1f51a9a7 100644 --- a/activemodel/lib/active_model/callbacks.rb +++ b/activemodel/lib/active_model/callbacks.rb @@ -24,14 +24,11 @@ module ActiveModel # you want callbacks on in a block so that the callbacks get a chance to fire: # # def create - # _run_create_callbacks do + # run_callbacks :create do # # Your create action methods here # end # end # - # The _run_<method_name>_callbacks methods are dynamically created when you extend - # the <tt>ActiveModel::Callbacks</tt> module. - # # Then in your class, you can use the +before_create+, +after_create+ and +around_create+ # methods, just as you would in an Active Record module. # @@ -102,7 +99,7 @@ module ActiveModel define_callbacks(callback, options) types.each do |type| - send(:"_define_#{type}_model_callback", self, callback) + send("_define_#{type}_model_callback", self, callback) end end end diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb index fdca852c7a..5e3cf510b0 100644 --- a/activemodel/lib/active_model/errors.rb +++ b/activemodel/lib/active_model/errors.rb @@ -60,9 +60,13 @@ module ActiveModel # p.validate! # => ["can not be nil"] # p.errors.full_messages # => ["name can not be nil"] # # etc.. - class Errors < ActiveSupport::OrderedHash + class Errors + include Enumerable + CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank] + attr_reader :messages + # Pass in the instance of the object that is using the errors object. # # class Person @@ -71,12 +75,29 @@ module ActiveModel # end # end def initialize(base) - @base = base - super() + @base = base + @messages = ActiveSupport::OrderedHash.new + end + + # Clear the messages + def clear + messages.clear + end + + # Do the error messages include an error with key +error+? + def include?(error) + messages.include? error end - alias_method :get, :[] - alias_method :set, :[]= + # Get messages for +key+ + def get(key) + messages[key] + end + + # Set messages for +key+ to +value+ + def set(key, value) + messages[key] = value + end # When passed a symbol or a name of a method, returns an array of errors # for the method. @@ -110,7 +131,7 @@ module ActiveModel # # then yield :name and "must be specified" # end def each - each_key do |attribute| + messages.each_key do |attribute| self[attribute].each { |error| yield attribute, error } end end @@ -125,6 +146,16 @@ module ActiveModel values.flatten.size end + # Returns all message values + def values + messages.values + end + + # Returns all message keys + def keys + messages.keys + end + # Returns an array of error messages, with the attribute name included # # p.errors.add(:name, "can't be blank") @@ -147,7 +178,7 @@ module ActiveModel def empty? all? { |k, v| v && v.empty? } end - + alias_method :blank?, :empty? # Returns an xml formatted representation of the Errors hash. # # p.errors.add(:name, "can't be blank") @@ -169,9 +200,7 @@ module ActiveModel end def to_hash - hash = ActiveSupport::OrderedHash.new - each { |k, v| (hash[k] ||= []) << v } - hash + messages.dup end # Adds +message+ to the error messages on +attribute+, which will be returned on a call to @@ -221,26 +250,20 @@ module ActiveModel # company.errors.full_messages # => # ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Address can't be blank"] def full_messages - full_messages = [] - - each do |attribute, messages| - messages = Array.wrap(messages) - next if messages.empty? - + map { |attribute, message| if attribute == :base - messages.each {|m| full_messages << m } + message else attr_name = attribute.to_s.gsub('.', '_').humanize attr_name = @base.class.human_attribute_name(attribute, :default => attr_name) - options = { :default => "%{attribute} %{message}", :attribute => attr_name } - messages.each do |m| - full_messages << I18n.t(:"errors.format", options.merge(:message => m)) - end + I18n.t(:"errors.format", { + :default => "%{attribute} %{message}", + :attribute => attr_name, + :message => message + }) end - end - - full_messages + } end # Translates an error message in its default scope diff --git a/activemodel/lib/active_model/mass_assignment_security.rb b/activemodel/lib/active_model/mass_assignment_security.rb index 66cd9fdde6..97e31d4243 100644 --- a/activemodel/lib/active_model/mass_assignment_security.rb +++ b/activemodel/lib/active_model/mass_assignment_security.rb @@ -20,32 +20,32 @@ module ActiveModel # For example, a logged in user may need to assign additional attributes depending # on their role: # - # class AccountsController < ApplicationController - # include ActiveModel::MassAssignmentSecurity + # class AccountsController < ApplicationController + # include ActiveModel::MassAssignmentSecurity # - # attr_accessible :first_name, :last_name + # attr_accessible :first_name, :last_name # - # def self.admin_accessible_attributes - # accessible_attributes + [ :plan_id ] - # end + # def self.admin_accessible_attributes + # accessible_attributes + [ :plan_id ] + # end # - # def update - # ... - # @account.update_attributes(account_params) - # ... - # end + # def update + # ... + # @account.update_attributes(account_params) + # ... + # end # - # protected + # protected # - # def account_params - # sanitize_for_mass_assignment(params[:account]) - # end + # def account_params + # sanitize_for_mass_assignment(params[:account]) + # end # - # def mass_assignment_authorizer - # admin ? admin_accessible_attributes : super - # end + # def mass_assignment_authorizer + # admin ? admin_accessible_attributes : super + # end # - # end + # end # module ClassMethods # Attributes named in this macro are protected from mass-assignment diff --git a/activemodel/lib/active_model/observing.rb b/activemodel/lib/active_model/observing.rb index 0d2dd36e59..bf4fd0740c 100644 --- a/activemodel/lib/active_model/observing.rb +++ b/activemodel/lib/active_model/observing.rb @@ -13,14 +13,18 @@ module ActiveModel # # Activates the observers assigned. Examples: # + # class ORM + # include ActiveModel::Observing + # end + # # # Calls PersonObserver.instance - # ActiveRecord::Base.observers = :person_observer + # ORM.observers = :person_observer # # # Calls Cacher.instance and GarbageCollector.instance - # ActiveRecord::Base.observers = :cacher, :garbage_collector + # ORM.observers = :cacher, :garbage_collector # # # Same as above, just using explicit class references - # ActiveRecord::Base.observers = Cacher, GarbageCollector + # ORM.observers = Cacher, GarbageCollector # # Note: Setting this does not instantiate the observers yet. # +instantiate_observers+ is called during startup, and before diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb index 52941942b8..957d0ddaaa 100644 --- a/activemodel/lib/active_model/secure_password.rb +++ b/activemodel/lib/active_model/secure_password.rb @@ -33,26 +33,34 @@ module ActiveModel attr_reader :password attr_accessor :password_confirmation - attr_protected(:password_digest) if respond_to?(:attr_protected) - validates_confirmation_of :password validates_presence_of :password_digest + + include InstanceMethodsOnActivation + + if respond_to?(:attributes_protected_by_default) + def self.attributes_protected_by_default + super + ['password_digest'] + end + end end end - # Returns self if the password is correct, otherwise false. - def authenticate(unencrypted_password) - if BCrypt::Password.new(password_digest) == unencrypted_password - self - else - false + module InstanceMethodsOnActivation + # Returns self if the password is correct, otherwise false. + def authenticate(unencrypted_password) + if BCrypt::Password.new(password_digest) == unencrypted_password + self + else + false + end end - end - # Encrypts the password into the password_digest attribute. - def password=(unencrypted_password) - @password = unencrypted_password - self.password_digest = BCrypt::Password.create(unencrypted_password) + # Encrypts the password into the password_digest attribute. + def password=(unencrypted_password) + @password = unencrypted_password + self.password_digest = BCrypt::Password.create(unencrypted_password) + end end end end diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb index 37739b98a1..caf44a2ee0 100644 --- a/activemodel/lib/active_model/serialization.rb +++ b/activemodel/lib/active_model/serialization.rb @@ -15,7 +15,7 @@ module ActiveModel # attr_accessor :name # # def attributes - # @attributes ||= {'name' => 'nil'} + # {'name' => name} # end # # end @@ -45,7 +45,7 @@ module ActiveModel # attr_accessor :name # # def attributes - # @attributes ||= {'name' => 'nil'} + # {'name' => name} # end # # end diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb index 6cb015a144..efd071fedc 100644 --- a/activemodel/lib/active_model/validations.rb +++ b/activemodel/lib/active_model/validations.rb @@ -146,8 +146,10 @@ module ActiveModel end # List all validators that being used to validate a specific attribute. - def validators_on(attribute) - _validators[attribute.to_sym] + def validators_on(*attributes) + attributes.map do |attribute| + _validators[attribute.to_sym] + end.flatten end # Check if method is an attribute method or not. @@ -207,7 +209,7 @@ module ActiveModel protected def run_validations! - _run_validate_callbacks + run_callbacks :validate errors.empty? end end diff --git a/activemodel/lib/active_model/validations/callbacks.rb b/activemodel/lib/active_model/validations/callbacks.rb index 621518de5b..adc2867ad0 100644 --- a/activemodel/lib/active_model/validations/callbacks.rb +++ b/activemodel/lib/active_model/validations/callbacks.rb @@ -50,7 +50,7 @@ module ActiveModel # Overwrite run validations to include callbacks. def run_validations! - _run_validation_callbacks { super } + run_callbacks(:validation) { super } end end end diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb index 0132f68282..7ff42de00b 100644 --- a/activemodel/lib/active_model/validations/validates.rb +++ b/activemodel/lib/active_model/validations/validates.rb @@ -81,10 +81,9 @@ module ActiveModel # def validates(*attributes) defaults = attributes.extract_options! - validations = defaults.slice!(:if, :unless, :on, :allow_blank, :allow_nil) + validations = defaults.slice!(*_validates_default_keys) raise ArgumentError, "You need to supply at least one attribute" if attributes.empty? - raise ArgumentError, "Attribute names must be symbols" if attributes.any?{ |attribute| !attribute.is_a?(Symbol) } raise ArgumentError, "You need to supply at least one validation" if validations.empty? defaults.merge!(:attributes => attributes) @@ -104,6 +103,12 @@ module ActiveModel protected + # When creating custom validators, it might be useful to be able to specify + # additional default keys. This can be done by overwriting this method. + def _validates_default_keys + [ :if, :unless, :on, :allow_blank, :allow_nil ] + end + def _parse_validates_options(options) #:nodoc: case options when TrueClass @@ -118,4 +123,4 @@ module ActiveModel end end end -end
\ No newline at end of file +end diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb index 200efd4eb5..1663697727 100644 --- a/activemodel/lib/active_model/validations/with.rb +++ b/activemodel/lib/active_model/validations/with.rb @@ -8,6 +8,18 @@ module ActiveModel end end + class WithValidator < EachValidator + def validate_each(record, attr, val) + method_name = options[:with] + + if record.method(method_name).arity == 0 + record.send method_name + else + record.send method_name, attr + end + end + end + module ClassMethods # Passes the record off to the class or classes specified and allows them # to add errors based on more complex conditions. diff --git a/activemodel/test/cases/attribute_methods_test.rb b/activemodel/test/cases/attribute_methods_test.rb index 54cf8380ab..422aa25668 100644 --- a/activemodel/test/cases/attribute_methods_test.rb +++ b/activemodel/test/cases/attribute_methods_test.rb @@ -21,6 +21,21 @@ class ModelWithAttributes2 attribute_method_suffix '_test' end +class ModelWithAttributesWithSpaces + include ActiveModel::AttributeMethods + + attribute_method_suffix '' + + def attributes + { :'foo bar' => 'value of foo bar'} + end + +private + def attribute(name) + attributes[name.to_sym] + end +end + class AttributeMethodsTest < ActiveModel::TestCase test 'unrelated classes should not share attribute method matchers' do assert_not_equal ModelWithAttributes.send(:attribute_method_matchers), @@ -35,6 +50,21 @@ class AttributeMethodsTest < ActiveModel::TestCase assert_equal "value of foo", ModelWithAttributes.new.foo end + test '#define_attribute_methods generates attribute methods with spaces in their names' do + ModelWithAttributesWithSpaces.define_attribute_methods([:'foo bar']) + + assert ModelWithAttributesWithSpaces.attribute_methods_generated? + assert_respond_to ModelWithAttributesWithSpaces.new, :'foo bar' + assert_equal "value of foo bar", ModelWithAttributesWithSpaces.new.send(:'foo bar') + end + + test '#alias_attribute works with attributes with spaces in their names' do + ModelWithAttributesWithSpaces.define_attribute_methods([:'foo bar']) + ModelWithAttributesWithSpaces.alias_attribute(:'foo_bar', :'foo bar') + + assert_equal "value of foo bar", ModelWithAttributesWithSpaces.new.foo_bar + end + test '#undefine_attribute_methods removes attribute methods' do ModelWithAttributes.define_attribute_methods([:foo]) ModelWithAttributes.undefine_attribute_methods diff --git a/activemodel/test/cases/callbacks_test.rb b/activemodel/test/cases/callbacks_test.rb index 069d907fb2..086e7266ff 100644 --- a/activemodel/test/cases/callbacks_test.rb +++ b/activemodel/test/cases/callbacks_test.rb @@ -37,7 +37,7 @@ class CallbacksTest < ActiveModel::TestCase end def create - _run_create_callbacks do + run_callbacks :create do @callbacks << :create @valid end @@ -92,7 +92,7 @@ class CallbacksTest < ActiveModel::TestCase def callback1; self.history << 'callback1'; end def callback2; self.history << 'callback2'; end def create - _run_create_callbacks {} + run_callbacks(:create) {} self end end diff --git a/activemodel/test/cases/errors_test.rb b/activemodel/test/cases/errors_test.rb index 27821c333b..a24cac40ad 100644 --- a/activemodel/test/cases/errors_test.rb +++ b/activemodel/test/cases/errors_test.rb @@ -25,7 +25,19 @@ class ErrorsTest < ActiveModel::TestCase def self.lookup_ancestors [self] end + end + + def test_include? + errors = ActiveModel::Errors.new(self) + errors[:foo] = 'omg' + assert errors.include?(:foo), 'errors should include :foo' + end + test "should return true if no errors" do + person = Person.new + person.errors[:foo] + assert person.errors.empty? + assert person.errors.blank? end test "method validate! should work" do diff --git a/activemodel/test/cases/secure_password_test.rb b/activemodel/test/cases/secure_password_test.rb index 79be715730..4a47a7a226 100644 --- a/activemodel/test/cases/secure_password_test.rb +++ b/activemodel/test/cases/secure_password_test.rb @@ -1,5 +1,7 @@ require 'cases/helper' require 'models/user' +require 'models/visitor' +require 'models/administrator' class SecurePasswordTest < ActiveModel::TestCase @@ -29,4 +31,15 @@ class SecurePasswordTest < ActiveModel::TestCase assert !@user.authenticate("wrong") assert @user.authenticate("secret") end + + test "visitor#password_digest should be protected against mass assignment" do + assert Visitor.active_authorizer.kind_of?(ActiveModel::MassAssignmentSecurity::BlackList) + assert Visitor.active_authorizer.include?(:password_digest) + end + + test "Administrator's mass_assignment_authorizer should be WhiteList" do + assert Administrator.active_authorizer.kind_of?(ActiveModel::MassAssignmentSecurity::WhiteList) + assert !Administrator.active_authorizer.include?(:password_digest) + assert Administrator.active_authorizer.include?(:name) + end end diff --git a/activemodel/test/cases/serializeration/xml_serialization_test.rb b/activemodel/test/cases/serializeration/xml_serialization_test.rb index cc19d322b3..b6a2f88667 100644 --- a/activemodel/test/cases/serializeration/xml_serialization_test.rb +++ b/activemodel/test/cases/serializeration/xml_serialization_test.rb @@ -114,7 +114,7 @@ class XmlSerializationTest < ActiveModel::TestCase end test "should serialize yaml" do - assert_match %r{<preferences type=\"yaml\">--- !ruby/struct:Customer \nname: John\n</preferences>}, @contact.to_xml + assert_match %r{<preferences type=\"yaml\">--- !ruby/struct:Customer(\s*)\nname: John\n</preferences>}, @contact.to_xml end test "should call proc on object" do diff --git a/activemodel/test/cases/validations/validates_test.rb b/activemodel/test/cases/validations/validates_test.rb index 3a9900939e..779f6c8448 100644 --- a/activemodel/test/cases/validations/validates_test.rb +++ b/activemodel/test/cases/validations/validates_test.rb @@ -1,6 +1,7 @@ # encoding: utf-8 require 'cases/helper' require 'models/person' +require 'models/topic' require 'models/person_with_validator' require 'validators/email_validator' require 'validators/namespace/email_validator' @@ -11,6 +12,7 @@ class ValidatesTest < ActiveModel::TestCase def reset_callbacks Person.reset_callbacks(:validate) + Topic.reset_callbacks(:validate) PersonWithValidator.reset_callbacks(:validate) end @@ -21,6 +23,17 @@ class ValidatesTest < ActiveModel::TestCase assert_equal ['is not a number'], person.errors[:title] end + def test_validates_with_attribute_specified_as_string + Person.validates "title", :numericality => true + person = Person.new + person.valid? + assert_equal ['is not a number'], person.errors[:title] + + person = Person.new + person.title = 123 + assert person.valid? + end + def test_validates_with_built_in_validation_and_options Person.validates :salary, :numericality => { :message => 'my custom message' } person = Person.new @@ -128,4 +141,13 @@ class ValidatesTest < ActiveModel::TestCase person.valid? assert_equal ['does not appear to be like Mr.'], person.errors[:title] end + + def test_defining_extra_default_keys_for_validates + Topic.validates :title, :confirmation => true, :message => 'Y U NO CONFIRM' + topic = Topic.new + topic.title = "What's happening" + topic.title_confirmation = "Not this" + assert !topic.valid? + assert_equal ['Y U NO CONFIRM'], topic.errors[:title] + end end diff --git a/activemodel/test/cases/validations/with_validation_test.rb b/activemodel/test/cases/validations/with_validation_test.rb index 6d825cd316..07c1bd0533 100644 --- a/activemodel/test/cases/validations/with_validation_test.rb +++ b/activemodel/test/cases/validations/with_validation_test.rb @@ -171,4 +171,25 @@ class ValidatesWithTest < ActiveModel::TestCase assert topic.errors[:title].empty? assert topic.errors[:content].empty? end + + test "validates_with can validate with an instance method" do + Topic.validates :title, :with => :my_validation + + topic = Topic.new :title => "foo" + assert topic.valid? + assert topic.errors[:title].empty? + + topic = Topic.new + assert !topic.valid? + assert_equal ['is missing'], topic.errors[:title] + end + + test "optionally pass in the attribute being validated when validating with an instance method" do + Topic.validates :title, :content, :with => :my_validation_with_arg + + topic = Topic.new :title => "foo" + assert !topic.valid? + assert topic.errors[:title].empty? + assert_equal ['is missing'], topic.errors[:content] + end end diff --git a/activemodel/test/cases/validations_test.rb b/activemodel/test/cases/validations_test.rb index e90dc7d4e3..2f36195627 100644 --- a/activemodel/test/cases/validations_test.rb +++ b/activemodel/test/cases/validations_test.rb @@ -254,6 +254,24 @@ class ValidationsTest < ActiveModel::TestCase assert_equal 10, Topic.validators_on(:title).first.options[:minimum] end + def test_list_of_validators_on_multiple_attributes + Topic.validates :title, :length => { :minimum => 10 } + Topic.validates :author_name, :presence => true, :format => /a/ + + validators = Topic.validators_on(:title, :author_name) + + assert_equal [ + ActiveModel::Validations::FormatValidator, + ActiveModel::Validations::LengthValidator, + ActiveModel::Validations::PresenceValidator + ], validators.map { |v| v.class }.sort_by { |c| c.to_s } + end + + def test_list_of_validators_will_be_empty_when_empty + Topic.validates :title, :length => { :minimum => 10 } + assert_equal [], Topic.validators_on(:author_name) + end + def test_validations_on_the_instance_level auto = Automobile.new diff --git a/activemodel/test/models/administrator.rb b/activemodel/test/models/administrator.rb new file mode 100644 index 0000000000..a48f8b064f --- /dev/null +++ b/activemodel/test/models/administrator.rb @@ -0,0 +1,10 @@ +class Administrator + include ActiveModel::Validations + include ActiveModel::SecurePassword + include ActiveModel::MassAssignmentSecurity + + attr_accessor :name, :password_digest + attr_accessible :name + + has_secure_password +end diff --git a/activemodel/test/models/topic.rb b/activemodel/test/models/topic.rb index ff34565bdb..c9af78f595 100644 --- a/activemodel/test/models/topic.rb +++ b/activemodel/test/models/topic.rb @@ -2,6 +2,10 @@ class Topic include ActiveModel::Validations include ActiveModel::Validations::Callbacks + def self._validates_default_keys + super | [ :message ] + end + attr_accessor :title, :author_name, :content, :approved attr_accessor :after_validation_performed @@ -25,4 +29,12 @@ class Topic self.after_validation_performed = true end + def my_validation + errors.add :title, "is missing" unless title + end + + def my_validation_with_arg(attr) + errors.add attr, "is missing" unless send(attr) + end + end diff --git a/activemodel/test/models/visitor.rb b/activemodel/test/models/visitor.rb new file mode 100644 index 0000000000..36c0a16688 --- /dev/null +++ b/activemodel/test/models/visitor.rb @@ -0,0 +1,9 @@ +class Visitor + include ActiveModel::Validations + include ActiveModel::SecurePassword + include ActiveModel::MassAssignmentSecurity + + has_secure_password + + attr_accessor :password_digest +end |