aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/lib/active_model
diff options
context:
space:
mode:
Diffstat (limited to 'activemodel/lib/active_model')
-rw-r--r--activemodel/lib/active_model/attribute_methods.rb6
-rw-r--r--activemodel/lib/active_model/errors.rb47
-rw-r--r--activemodel/lib/active_model/secure_password.rb4
-rw-r--r--activemodel/lib/active_model/serialization.rb27
-rw-r--r--activemodel/lib/active_model/serializers/json.rb4
-rw-r--r--activemodel/lib/active_model/validations/validates.rb4
-rw-r--r--activemodel/lib/active_model/validations/with.rb2
-rw-r--r--activemodel/lib/active_model/validator.rb6
8 files changed, 70 insertions, 30 deletions
diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb
index a201e983cd..ef0b95424e 100644
--- a/activemodel/lib/active_model/attribute_methods.rb
+++ b/activemodel/lib/active_model/attribute_methods.rb
@@ -93,10 +93,10 @@ module ActiveModel
#
# Provides you with:
#
- # AttributePerson.primary_key
+ # Person.primary_key
# # => "sysid"
- # AttributePerson.inheritance_column = 'address'
- # AttributePerson.inheritance_column
+ # Person.inheritance_column = 'address'
+ # Person.inheritance_column
# # => 'address_id'
def define_attr_method(name, value=nil, &block)
sing = singleton_class
diff --git a/activemodel/lib/active_model/errors.rb b/activemodel/lib/active_model/errors.rb
index d91e4a2b6a..8337b04c0d 100644
--- a/activemodel/lib/active_model/errors.rb
+++ b/activemodel/lib/active_model/errors.rb
@@ -205,21 +205,14 @@ module ActiveModel
messages.dup
end
- # Adds +message+ to the error messages on +attribute+, which will be returned on a call to
- # <tt>on(attribute)</tt> for the same attribute. More than one error can be added to the same
- # +attribute+ in which case an array will be returned on a call to <tt>on(attribute)</tt>.
+ # Adds +message+ to the error messages on +attribute+. More than one error can be added to the same
+ # +attribute+.
# If no +message+ is supplied, <tt>:invalid</tt> is assumed.
#
# If +message+ is a symbol, it will be translated using the appropriate scope (see +translate_error+).
# If +message+ is a proc, it will be called, allowing for things like <tt>Time.now</tt> to be used within an error.
def add(attribute, message = nil, options = {})
- message ||= :invalid
-
- if message.is_a?(Symbol)
- message = generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
- elsif message.is_a?(Proc)
- message = message.call
- end
+ message = normalize_message(attribute, message, options)
if options[:strict]
raise ActiveModel::StrictValidationFailed, message
end
@@ -244,6 +237,15 @@ module ActiveModel
end
end
+ # Returns true if an error on the attribute with the given message is present, false otherwise.
+ # +message+ is treated the same as for +add+.
+ # p.errors.add :name, :blank
+ # p.errors.added? :name, :blank # => true
+ def added?(attribute, message = nil, options = {})
+ message = normalize_message(attribute, message, options)
+ self[attribute].include? message
+ end
+
# Returns all the full error messages in an array.
#
# class Company
@@ -300,13 +302,17 @@ module ActiveModel
def generate_message(attribute, type = :invalid, options = {})
type = options.delete(:message) if options[:message].is_a?(Symbol)
- defaults = @base.class.lookup_ancestors.map do |klass|
- [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
- :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
+ if @base.class.respond_to?(:i18n_scope)
+ defaults = @base.class.lookup_ancestors.map do |klass|
+ [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
+ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
+ end
+ else
+ defaults = []
end
defaults << options.delete(:message)
- defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}"
+ defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
defaults << :"errors.attributes.#{attribute}.#{type}"
defaults << :"errors.messages.#{type}"
@@ -325,6 +331,19 @@ module ActiveModel
I18n.translate(key, options)
end
+
+ private
+ def normalize_message(attribute, message, options)
+ message ||= :invalid
+
+ if message.is_a?(Symbol)
+ generate_message(attribute, message, options.except(*CALLBACKS_OPTIONS))
+ elsif message.is_a?(Proc)
+ message.call
+ else
+ message
+ end
+ end
end
class StrictValidationFailed < StandardError
diff --git a/activemodel/lib/active_model/secure_password.rb b/activemodel/lib/active_model/secure_password.rb
index 7a109d9a52..db78864c67 100644
--- a/activemodel/lib/active_model/secure_password.rb
+++ b/activemodel/lib/active_model/secure_password.rb
@@ -32,8 +32,8 @@ module ActiveModel
# User.find_by_name("david").try(:authenticate, "notright") # => nil
# User.find_by_name("david").try(:authenticate, "mUc3m00RsqyRe") # => user
def has_secure_password
- # Load bcrypt-ruby only when has_secured_password is used to avoid make ActiveModel
- # (and by extension the entire framework) dependent on a binary library.
+ # Load bcrypt-ruby only when has_secure_password is used.
+ # This is to avoid ActiveModel (and by extension the entire framework) being dependent on a binary library.
gem 'bcrypt-ruby', '~> 3.0.0'
require 'bcrypt'
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb
index a756b9f205..a4b58ab456 100644
--- a/activemodel/lib/active_model/serialization.rb
+++ b/activemodel/lib/active_model/serialization.rb
@@ -78,7 +78,8 @@ module ActiveModel
attribute_names -= Array.wrap(except).map(&:to_s)
end
- hash = attributes.slice(*attribute_names)
+ hash = {}
+ attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) }
method_names = Array.wrap(options[:methods]).select { |n| respond_to?(n) }
method_names.each { |n| hash[n] = send(n) }
@@ -95,17 +96,37 @@ module ActiveModel
end
private
+
+ # Hook method defining how an attribute value should be retrieved for
+ # serialization. By default this is assumed to be an instance named after
+ # the attribute. Override this method in subclasses should you need to
+ # retrieve the value for a given attribute differently:
+ #
+ # class MyClass
+ # include ActiveModel::Validations
+ #
+ # def initialize(data = {})
+ # @data = data
+ # end
+ #
+ # def read_attribute_for_serialization(key)
+ # @data[key]
+ # end
+ # end
+ #
+ alias :read_attribute_for_serialization :send
+
# Add associations specified via the <tt>:include</tt> option.
#
# Expects a block that takes as arguments:
# +association+ - name of the association
# +records+ - the association record(s) to be serialized
# +opts+ - options for the association records
- def serializable_add_includes(options = {})
+ def serializable_add_includes(options = {}) #:nodoc:
return unless include = options[:include]
unless include.is_a?(Hash)
- include = Hash[Array.wrap(include).map { |n| [n, {}] }]
+ include = Hash[Array.wrap(include).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }]
end
include.each do |association, opts|
diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb
index 885964633f..c845440120 100644
--- a/activemodel/lib/active_model/serializers/json.rb
+++ b/activemodel/lib/active_model/serializers/json.rb
@@ -15,7 +15,7 @@ module ActiveModel
self.include_root_in_json = true
end
- # Returns a JSON string representing the model. Some configuration can be
+ # Returns a hash representing the model. Some configuration can be
# passed through +options+.
#
# The option <tt>include_root_in_json</tt> controls the top-level behavior
@@ -42,7 +42,7 @@ module ActiveModel
# The remainder of the examples in this section assume include_root_in_json is set to
# <tt>false</tt>.
#
- # Without any +options+, the returned JSON string will include all the model's
+ # Without any +options+, the returned Hash will include all the model's
# attributes. For example:
#
# user = User.find(1)
diff --git a/activemodel/lib/active_model/validations/validates.rb b/activemodel/lib/active_model/validations/validates.rb
index b85c2453fb..fbceb81e8f 100644
--- a/activemodel/lib/active_model/validations/validates.rb
+++ b/activemodel/lib/active_model/validations/validates.rb
@@ -27,7 +27,7 @@ module ActiveModel
#
# class EmailValidator < ActiveModel::EachValidator
# def validate_each(record, attribute, value)
- # record.errors[attribute] << (options[:message] || "is not an email") unless
+ # record.errors.add attribute, (options[:message] || "is not an email") unless
# value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
# end
# end
@@ -48,7 +48,7 @@ module ActiveModel
#
# class TitleValidator < ActiveModel::EachValidator
# def validate_each(record, attribute, value)
- # record.errors[attribute] << "must start with 'the'" unless value =~ /\Athe/i
+ # record.errors.add attribute, "must start with 'the'" unless value =~ /\Athe/i
# end
# end
#
diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb
index 83aae206a6..93a340eb39 100644
--- a/activemodel/lib/active_model/validations/with.rb
+++ b/activemodel/lib/active_model/validations/with.rb
@@ -32,7 +32,7 @@ module ActiveModel
# class MyValidator < ActiveModel::Validator
# def validate(record)
# if some_complex_logic
- # record.errors[:base] << "This record is invalid"
+ # record.errors.add :base, "This record is invalid"
# end
# end
#
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index 5304743389..0e444738ba 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -48,8 +48,8 @@ module ActiveModel #:nodoc:
#
# class MyValidator < ActiveModel::Validator
# def validate(record)
- # record.errors[:base] << "This is some custom error message"
- # record.errors[:first_name] << "This is some complex validation"
+ # record.errors.add :base, "This is some custom error message"
+ # record.errors.add :first_name, "This is some complex validation"
# # etc...
# end
# end
@@ -68,7 +68,7 @@ module ActiveModel #:nodoc:
#
# class TitleValidator < ActiveModel::EachValidator
# def validate_each(record, attribute, value)
- # record.errors[attribute] << 'must be Mr. Mrs. or Dr.' unless value.in?(['Mr.', 'Mrs.', 'Dr.'])
+ # record.errors.add attribute, 'must be Mr. Mrs. or Dr.' unless value.in?(['Mr.', 'Mrs.', 'Dr.'])
# end
# end
#