aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel')
-rw-r--r--activemodel/README.rdoc8
-rwxr-xr-xactivemodel/Rakefile2
-rw-r--r--activemodel/activemodel.gemspec3
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb22
-rw-r--r--activemodel/lib/active_model/callbacks.rb7
-rw-r--r--activemodel/lib/active_model/errors.rb71
-rw-r--r--activemodel/lib/active_model/mass_assignment_security.rb38
-rw-r--r--activemodel/lib/active_model/observing.rb10
-rw-r--r--activemodel/lib/active_model/secure_password.rb34
-rw-r--r--activemodel/lib/active_model/serialization.rb4
-rw-r--r--activemodel/lib/active_model/validations.rb8
-rw-r--r--activemodel/lib/active_model/validations/callbacks.rb2
-rw-r--r--activemodel/lib/active_model/validations/validates.rb11
-rw-r--r--activemodel/lib/active_model/validations/with.rb12
-rw-r--r--activemodel/test/cases/attribute_methods_test.rb30
-rw-r--r--activemodel/test/cases/callbacks_test.rb4
-rw-r--r--activemodel/test/cases/errors_test.rb12
-rw-r--r--activemodel/test/cases/secure_password_test.rb13
-rw-r--r--activemodel/test/cases/serializeration/xml_serialization_test.rb2
-rw-r--r--activemodel/test/cases/validations/validates_test.rb22
-rw-r--r--activemodel/test/cases/validations/with_validation_test.rb21
-rw-r--r--activemodel/test/cases/validations_test.rb18
-rw-r--r--activemodel/test/models/administrator.rb10
-rw-r--r--activemodel/test/models/topic.rb12
-rw-r--r--activemodel/test/models/visitor.rb9
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