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/callbacks.rb2
-rw-r--r--activemodel/lib/active_model/dirty.rb43
-rw-r--r--activemodel/lib/active_model/lint.rb60
-rw-r--r--activemodel/lib/active_model/locale/en.yml3
-rw-r--r--activemodel/lib/active_model/validations/length.rb24
-rw-r--r--activemodel/lib/active_model/validator.rb4
6 files changed, 75 insertions, 61 deletions
diff --git a/activemodel/lib/active_model/callbacks.rb b/activemodel/lib/active_model/callbacks.rb
index 6214802074..2cf39b68fb 100644
--- a/activemodel/lib/active_model/callbacks.rb
+++ b/activemodel/lib/active_model/callbacks.rb
@@ -49,7 +49,7 @@ module ActiveModel
# puts 'block successfully called.'
# end
#
- # You can choose not to have all three callbacks by passing a hash to the
+ # You can choose to have only specific callbacks by passing a hash to the
# +define_model_callbacks+ method.
#
# define_model_callbacks :create, only: [:after, :before]
diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb
index 614bc6a5d8..c03e5fac79 100644
--- a/activemodel/lib/active_model/dirty.rb
+++ b/activemodel/lib/active_model/dirty.rb
@@ -52,10 +52,10 @@ module ActiveModel
# end
# end
#
- # A newly instantiated object is unchanged:
+ # A newly instantiated +Person+ object is unchanged:
#
- # person = Person.find_by(name: 'Uncle Bob')
- # person.changed? # => false
+ # person = Person.new
+ # person.changed? # => false
#
# Change the name:
#
@@ -71,8 +71,8 @@ module ActiveModel
# Save the changes:
#
# person.save
- # person.changed? # => false
- # person.name_changed? # => false
+ # person.changed? # => false
+ # person.name_changed? # => false
#
# Reset the changes:
#
@@ -84,20 +84,20 @@ module ActiveModel
#
# person.name = "Uncle Bob"
# person.rollback!
- # person.name # => "Bill"
- # person.name_changed? # => false
+ # person.name # => "Bill"
+ # person.name_changed? # => false
#
# Assigning the same value leaves the attribute unchanged:
#
# person.name = 'Bill'
- # person.name_changed? # => false
- # person.name_change # => nil
+ # person.name_changed? # => false
+ # person.name_change # => nil
#
# Which attributes have changed?
#
# person.name = 'Bob'
- # person.changed # => ["name"]
- # person.changes # => {"name" => ["Bill", "Bob"]}
+ # person.changed # => ["name"]
+ # person.changes # => {"name" => ["Bill", "Bob"]}
#
# If an attribute is modified in-place then make use of
# +[attribute_name]_will_change!+ to mark that the attribute is changing.
@@ -106,9 +106,9 @@ module ActiveModel
# not need to call +[attribute_name]_will_change!+ on Active Record models.
#
# person.name_will_change!
- # person.name_change # => ["Bill", "Bill"]
+ # person.name_change # => ["Bill", "Bill"]
# person.name << 'y'
- # person.name_change # => ["Bill", "Billy"]
+ # person.name_change # => ["Bill", "Billy"]
module Dirty
extend ActiveSupport::Concern
include ActiveModel::AttributeMethods
@@ -118,7 +118,7 @@ module ActiveModel
attribute_method_affix prefix: 'restore_', suffix: '!'
end
- # Returns +true+ if any attribute have unsaved changes, +false+ otherwise.
+ # Returns +true+ if any of the attributes have unsaved changes, +false+ otherwise.
#
# person.changed? # => false
# person.name = 'bob'
@@ -166,7 +166,7 @@ module ActiveModel
@changed_attributes ||= ActiveSupport::HashWithIndifferentAccess.new
end
- # Handle <tt>*_changed?</tt> for +method_missing+.
+ # Handles <tt>*_changed?</tt> for +method_missing+.
def attribute_changed?(attr, options = {}) #:nodoc:
result = changes_include?(attr)
result &&= options[:to] == __send__(attr) if options.key?(:to)
@@ -174,7 +174,7 @@ module ActiveModel
result
end
- # Handle <tt>*_was</tt> for +method_missing+.
+ # Handles <tt>*_was</tt> for +method_missing+.
def attribute_was(attr) # :nodoc:
attribute_changed?(attr) ? changed_attributes[attr] : __send__(attr)
end
@@ -186,6 +186,7 @@ module ActiveModel
private
+ # Returns +true+ if attr_name is changed, +false+ otherwise.
def changes_include?(attr_name)
attributes_changed_by_setter.include?(attr_name)
end
@@ -197,18 +198,18 @@ module ActiveModel
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
end
- # Clear all dirty data: current changes and previous changes.
+ # Clears all dirty data: current changes and previous changes.
def clear_changes_information # :doc:
@previously_changed = ActiveSupport::HashWithIndifferentAccess.new
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
end
- # Handle <tt>*_change</tt> for +method_missing+.
+ # Handles <tt>*_change</tt> for +method_missing+.
def attribute_change(attr)
[changed_attributes[attr], __send__(attr)] if attribute_changed?(attr)
end
- # Handle <tt>*_will_change!</tt> for +method_missing+.
+ # Handles <tt>*_will_change!</tt> for +method_missing+.
def attribute_will_change!(attr)
return if attribute_changed?(attr)
@@ -221,7 +222,7 @@ module ActiveModel
set_attribute_was(attr, value)
end
- # Handle <tt>restore_*!</tt> for +method_missing+.
+ # Handles <tt>restore_*!</tt> for +method_missing+.
def restore_attribute!(attr)
if attribute_changed?(attr)
__send__("#{attr}=", changed_attributes[attr])
@@ -230,7 +231,7 @@ module ActiveModel
end
# This is necessary because `changed_attributes` might be overridden in
- # other implemntations (e.g. in `ActiveRecord`)
+ # other implementations (e.g. in `ActiveRecord`)
alias_method :attributes_changed_by_setter, :changed_attributes # :nodoc:
# Force an attribute to have a particular "before" value
diff --git a/activemodel/lib/active_model/lint.rb b/activemodel/lib/active_model/lint.rb
index 38087521a2..010eaeb170 100644
--- a/activemodel/lib/active_model/lint.rb
+++ b/activemodel/lib/active_model/lint.rb
@@ -21,28 +21,27 @@ module ActiveModel
# +self+.
module Tests
- # == Responds to <tt>to_key</tt>
+ # Passes if the object's model responds to <tt>to_key</tt> and if calling
+ # this method returns +nil+ when the object is not persisted.
+ # Fails otherwise.
#
- # Returns an Enumerable of all (primary) key attributes
- # or nil if <tt>model.persisted?</tt> is false. This is used by
- # <tt>dom_id</tt> to generate unique ids for the object.
+ # <tt>to_key</tt> returns an Enumerable of all (primary) key attributes
+ # of the model, and is used to a generate unique DOM id for the object.
def test_to_key
assert model.respond_to?(:to_key), "The model should respond to to_key"
def model.persisted?() false end
assert model.to_key.nil?, "to_key should return nil when `persisted?` returns false"
end
- # == Responds to <tt>to_param</tt>
- #
- # Returns a string representing the object's key suitable for use in URLs
- # or +nil+ if <tt>model.persisted?</tt> is +false+.
+ # Passes if the object's model responds to <tt>to_param</tt> and if
+ # calling this method returns +nil+ when the object is not persisted.
+ # Fails otherwise.
#
+ # <tt>to_param</tt> is used to represent the object's key in URLs.
# Implementers can decide to either raise an exception or provide a
# default in case the record uses a composite primary key. There are no
# tests for this behavior in lint because it doesn't make sense to force
# any of the possible implementation strategies on the implementer.
- # However, if the resource is not persisted?, then <tt>to_param</tt>
- # should always return +nil+.
def test_to_param
assert model.respond_to?(:to_param), "The model should respond to to_param"
def model.to_key() [1] end
@@ -50,32 +49,34 @@ module ActiveModel
assert model.to_param.nil?, "to_param should return nil when `persisted?` returns false"
end
- # == Responds to <tt>to_partial_path</tt>
+ # Passes if the object's model responds to <tt>to_partial_path</tt> and if
+ # calling this method returns a string. Fails otherwise.
#
- # Returns a string giving a relative path. This is used for looking up
- # partials. For example, a BlogPost model might return "blog_posts/blog_post"
+ # <tt>to_partial_path</tt> is used for looking up partials. For example,
+ # a BlogPost model might return "blog_posts/blog_post".
def test_to_partial_path
assert model.respond_to?(:to_partial_path), "The model should respond to to_partial_path"
assert_kind_of String, model.to_partial_path
end
- # == Responds to <tt>persisted?</tt>
+ # Passes if the object's model responds to <tt>persisted?</tt> and if
+ # calling this method returns either +true+ or +false+. Fails otherwise.
#
- # 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 route to
- # the create action. If it is persisted, a form for the object will routes
- # to the update action.
+ # <tt>persisted?</tt> is used when calculating the URL for an object.
+ # If the object is not persisted, a form for that object, for instance,
+ # will route to the create action. If it is persisted, a form for the
+ # object will route to the update action.
def test_persisted?
assert model.respond_to?(:persisted?), "The model should respond to persisted?"
assert_boolean model.persisted?, "persisted?"
end
- # == \Naming
+ # Passes if the object's model responds to <tt>model_name</tt> both as
+ # an instance method and as a class method, and if calling this method
+ # returns a string with some convenience methods: <tt>:human</tt>,
+ # <tt>:singular</tt> and <tt>:plural</tt>.
#
- # Model.model_name and Model#model_name must return a string with some
- # convenience methods: # <tt>:human</tt>, <tt>:singular</tt> and
- # <tt>:plural</tt>. Check ActiveModel::Naming for more information.
+ # Check ActiveModel::Naming for more information.
def test_model_naming
assert model.class.respond_to?(:model_name), "The model class should respond to model_name"
model_name = model.class.model_name
@@ -88,12 +89,15 @@ module ActiveModel
assert_equal model.model_name, model.class.model_name
end
- # == \Errors Testing
+ # Passes if the object's model responds to <tt>errors</tt> and if calling
+ # <tt>[](attribute)</tt> on the result of this method returns an array.
+ # Fails otherwise.
#
- # Returns an object that implements [](attribute) defined which 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.
+ # <tt>errors[attribute]</tt> is used to retrieve the errors of a model
+ # for a given attribute. If errors are present, the method should return
+ # 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, the method should return an empty array.
def test_errors_aref
assert model.respond_to?(:errors), "The model should respond to errors"
assert model.errors[:hello].is_a?(Array), "errors#[] should return an Array"
diff --git a/activemodel/lib/active_model/locale/en.yml b/activemodel/lib/active_model/locale/en.yml
index b0b20ee0bf..b11c8f53b4 100644
--- a/activemodel/lib/active_model/locale/en.yml
+++ b/activemodel/lib/active_model/locale/en.yml
@@ -14,10 +14,9 @@ en:
empty: "can't be empty"
blank: "can't be blank"
present: "must be blank"
- required: "must exist"
too_long:
one: "is too long (maximum is 1 character)"
- other: "is too long (maximum is %{count} characters)"
+ other: "is too long (maximum is %{count} characters)"
too_short:
one: "is too short (minimum is 1 character)"
other: "is too short (minimum is %{count} characters)"
diff --git a/activemodel/lib/active_model/validations/length.rb b/activemodel/lib/active_model/validations/length.rb
index a96b30cadd..23201b264a 100644
--- a/activemodel/lib/active_model/validations/length.rb
+++ b/activemodel/lib/active_model/validations/length.rb
@@ -38,7 +38,7 @@ module ActiveModel
end
def validate_each(record, attribute, value)
- value = tokenize(value)
+ value = tokenize(record, value)
value_length = value.respond_to?(:length) ? value.length : value.to_s.length
errors_options = options.except(*RESERVED_OPTIONS)
@@ -59,10 +59,14 @@ module ActiveModel
end
private
-
- def tokenize(value)
- if options[:tokenizer] && value.kind_of?(String)
- options[:tokenizer].call(value)
+ def tokenize(record, value)
+ tokenizer = options[:tokenizer]
+ if tokenizer && value.kind_of?(String)
+ if tokenizer.kind_of?(Proc)
+ tokenizer.call(value)
+ elsif record.respond_to?(tokenizer)
+ record.send(tokenizer, value)
+ end
end || value
end
@@ -108,10 +112,12 @@ module ActiveModel
# * <tt>:message</tt> - The error message to use for a <tt>:minimum</tt>,
# <tt>:maximum</tt>, or <tt>:is</tt> violation. An alias of the appropriate
# <tt>too_long</tt>/<tt>too_short</tt>/<tt>wrong_length</tt> message.
- # * <tt>:tokenizer</tt> - Specifies how to split up the attribute string.
- # (e.g. <tt>tokenizer: ->(str) { str.scan(/\w+/) }</tt> to count words
- # as in above example). Defaults to <tt>->(value) { value.split(//) }</tt>
- # which counts individual characters.
+ # * <tt>:tokenizer</tt> - A method (as a symbol), proc or string to
+ # specify how to split up the attribute string. (e.g.
+ # <tt>tokenizer: :word_tokenizer</tt> to call the +word_tokenizer+ method
+ # or <tt>tokenizer: ->(str) { str.scan(/\w+/) }</tt> to count words as in
+ # above example). Defaults to <tt>->(value) { value.split(//) }</tt> which
+ # counts individual characters.
#
# There is also a list of default options supported by every validator:
# +:if+, +:unless+, +:on+ and +:strict+.
diff --git a/activemodel/lib/active_model/validator.rb b/activemodel/lib/active_model/validator.rb
index ac32750946..b98585912e 100644
--- a/activemodel/lib/active_model/validator.rb
+++ b/activemodel/lib/active_model/validator.rb
@@ -163,6 +163,10 @@ module ActiveModel
# +ArgumentError+ when invalid options are supplied.
def check_validity!
end
+
+ def should_validate?(record) # :nodoc:
+ !record.persisted? || record.changed? || record.marked_for_destruction?
+ end
end
# +BlockValidator+ is a special +EachValidator+ which receives a block on initialization