Active Model is a Rails library containing various modules used in developing frameworks that need to interact with the Rails Action Pack and Action View libraries. This came about by extracting common functionality that was not persistence specific out of Active Record, so that third-party libraries did not have to copy code from Rails or monkey patch helpers in order to conform to the API.
Out of this extraction came extremely useful reusable functionality to developers of Rails compatible libraries, such as dirty attributes, validations, and serialization into JSON or XML. And simply by using these modules, developers could be DRY and not need to rewrite what has already been done before.
Section headings reflect the name of the class or module where the API method is located and are organized in alphabetical order for easy lookup. Subsections appear according to the name of the Ruby file in which they exist within Active Model’s lib
directory. Finally, the sub-subsections are the API methods themselves.
Adds the ability for your class to have custom prefixes and suffixes on your methods. It’s used by adding the definitions for the prefixes and suffixes, defining which methods on the object will use them and then implementing the common behavior for when those methods are called. An example implementation is as follows:
1 class Record
2 include ActiveModel::AttributeMethods
3
4 attribute_method_prefix 'reset_'
5 attribute_method_suffix '_highest?'
6 define_attribute_methods :score
7
8 attr_accessor :score
9 attr_accessor :previous_score
10
11 private
12
13 def reset_attribute(attribute)
14 send("#{attribute}=", nil)
15 end
16
17 def attribute_highest?(attribute)
18 attribute > 1000 ? true : false
19 end
20 end
This useful method allows you to easily make aliases for attributes, including their reader and writer methods.
1 class Person
2 include ActiveModel::AttributeMethods
3 attr_accessor :name
4 alias_attribute :full_name, :name
5 end
6
7
8 person = Person.new
9 person.name = "John Smith"
10 person.name # => "John Smith"
11 person.full_name # => "John Smith"
Defines a prefix and suffix that, when used in conjunction with define_attribute_methods
, creates a instance method with the prefix and suffix wrapping the previous method name.
Defines a prefix that, when used in conjunction with define_attribute_methods
, creates an instance method with the prefix and the previous method name.
Defines a suffix that, when used in conjunction with define_attribute_methods
, creates an instance method with the suffix and the previous method name.
Declares an attribute that will get prefixed and suffixed. The define_attribute_method
should be defined after any prefix, suffix, or affix definitions or they will not hook in.
1 class Record
2 include ActiveModel::AttributeMethods
3
4 attribute_method_prefix 'reset_'
5 define_attribute_methods :score
6
7 attr_accessor :score
8
9 private
10
11 def reset_attribute(attribute)
12 send("#{attribute}=", nil)
13 end
14 end
15
16 record = Record.new
17 record.score = 1
18 record.reset_score # => nil
Declares the attributes that will get prefixed and suffixed. Note that define_attribute_methods
should be defined after any prefix, suffix, or affix definitions.
Returns whether or not the dynamic attribute methods have been generated.
Removes all the attribute method definitions previously defined.
Gives any class Active Record style callbacks. It is used by defining the callbacks that the model will use and then in your model running the callbacks at the appropriate time. Once defined you have access to before
, after
, and around
custom methods.
1 class Record
2 extend ActiveModel::Callbacks
3
4 define_model_callbacks :create
5 define_model_callbacks :update, only: :before
6
7 before_update :my_callback
8
9 def create
10 run_callbacks :create do
11 # Your create code here
12 end
13 end
14
15 def update
16 run_callbacks :update do
17 # Your update code here
18 end
19 end
20
21 private
22
23 def my_callback
24 # Your callback code here
25 end
26 end
Defines the callback hooks that can be used in the model, which will dynamically provide you with a before
, after
, and around
hook for each name passed. Optionally, one can supply an :only
option to specify which callbacks you want created.
1 define_model_callbacks :create, only: :after
Defined callbacks can accept a callback class by passing the given callback an object that responds to the name of the callback and takes the model object as a parameter.
1 class Record
2 extend ActiveModel::Callbacks
3 define_model_callbacks :create
4
5 before_create SomeCallbackClass
6 end
7
8 class SomeCallbackClass
9 def self.before_create(obj)
10 # Obj is the Record instance the callback is being called on.
11 end
12 end
A simple module that, when included, gives the standard Rails conversion methods to your model. The only requirement for including this class is that your model contains a persisted?
method and an id
method.
Returns self
. If your model is not Active Model compliant, then override this method.
Returns an enumerable of primary key attributes or nil
if the object is not persisted.
Return a URL-friendly version of the object’s primary key or nil
if the object is not persisted.
Returns a string identifying the path associated with the object.
record = Record.new
record.to_partial_path # => "records/record"
Used by Action View to find a suitable partial to represent the object.
A powerful module that allows for tracking in your object what changes have been made to it since it was last initialized. It creates a handful of dynamic methods based on which attributes you define as attribute methods on your class and requires that you also tell the attribute setters that they are being tracked for changes. (You can optionally also store previous changes each time your object is persisted as well.)
1 class User
2 include ActiveModel::Dirty
3
4 define_attribute_methods :email
5
6 def email
7 @email
8 end
9
10 def email=(value)
11 email_will_change! unless value == @email
12 @email = value
13 end
14
15 def save
16 @previously_changed = changes
17 @changed_attributes.clear
18 end
19 end
In the previous example, the following dynamic methods would then be available for checking the dirty state of the flagged field. (Assume user is an instance of the User
class.)
1 # Returns an array of the old and new values.
2 user.email_change
3
4 # Returns true if the value has changed.
5 user.email_changed?
6
7 # Resets the attribute back to the original value.
8 user.reset_email!
9
10 # Returns the old value of a changed field.
11 user.email_was
12
13 # Flags an attribute that will be changed.
14 user.email_will_change!
Returns an array of fields whose values have changed on the object.
Returns whether or not the object’s attributes have changed.
As of Rails 4.1, one can determine if an attribute has changed from one value to another by supplying hash options :from
and :to
.
user.name_changed?(from: 'Prince', to: 'Symbol')
Returns a hash of the fields that have changed with their original values.
Returns a hash of changes with the attribute names as the keys and the values being an array of the old and new value for that field.
Returns a hash of previous changes before the object was persisted with the attribute names as the keys and the values being an array of the old and new value for that field.
A module that provides a common interface for handling application error messages.
Note that in order for your object to be compatible with the Errors
API with I18n and validations support, it needs to extend ActiveModel::Naming
and ActiveModel::Translation
and include ActiveModel::Validations
.
1 class User
2 extend ActiveModel::Naming
3 extend ActiveModel::Translation
4 include ActiveModel::Validations
5
6 attr_reader :errors
7 attr_accessor :name
8
9 def initialize
10 @errors = ActiveModel::Errors.new(self)
11 end
12 end
Returns the errors for the supplied attribute as an array.
1 user.errors[:name] # => ["is invalid"]
1 user.errors[:name] = 'must be implemented'
Adds an error message for the supplied attribute. If no message is provided, :invalid
is assumed. Options allowed are the following:
:strict If set to true
, will raise ActiveModel::StrictValidationFailed
over adding an error.
>> user.errors.add(:name)
=> ["is invalid"]
>> user.errors.add(:name, 'must be implemented')
=> ["is invalid", "must be implemented"]
Adds a “blank” error message for each specified attribute that is blank.
user.errors.add_on_blank(:name)
user.errors[:name] # => ["can't be blank"]
Adds an error message for each specified attribute that is empty.
user.errors.add_on_empty(:name)
user.errors[:name] # => ["can't be empty"]
Returns true
if an error on the attribute with the given message is present.
user.errors.add :name, :blank
user.errors.added? :name, :blank # => true
Returns a hash that can be used as the JSON representation for this object. Available options are the following:
:full_messages If set to true
, returns full errors messages for each attribute.
>> user.errors.as_json
=> {:name=>["can't be blank"]}
>> user.errors.as_json(full_messages: true)
=> {:name=>["Name can't be blank"]}
Returns true
if there are no errors on the object and false
otherwise.
Returns the total number of error messages.
Deletes all messages for specified key
.
1 user.errors[:name] # => ["can't be blank"]
2 user.errors.delete(:name)
3 user.errors[:name] # => []
Iterates through the error keys, yielding the attribute and the errors for each. If an attribute has more than one error message, it will yield for each one.
1 user.errors.each do |attribute, error|
2 ...
3 end
Returns a full message for a given attribute.
Returns all the error messages as an array.
Returns an array of all the full error messages for a given attribute.
1 user.errors.full_messages_for(:name)
Generates a translated error message under the scope activemodel.errors.messages
for the supplied attribute. Messages are looked up via the following pattern: models.MODEL.attributes.ATTRIBUTE.MESSAGE
. If a translation is not found, Active Model will then look in models.MODEL.MESSAGE
. If that yields no translations, it will return a default message (activemodel.errors.messages.MESSAGE
).
The following are available options:
:strict If set to true
, will raise ActiveModel::StrictValidationFailed
over adding an error.
If inheritance is being used in your models and no error messages are found for the model, messages will be looked up on the parent model.
Returns an array of error messages for the given key
.
1 user.errors.get(:name)
Returns true
if the error messages include an error for the given attribute
.
user.errors.include?(:name) # => true
Returns all message keys.
Sets the messages for a key
.
user.errors.set(:name, ['must be implemented'])
Returns the total number of error messages.
Returns an array of all the error messages with the attribute name included in each.
Returns a hash of all the error messages with the attribute name set as the key and messages as values. If full_messages
is set to true
, it will contain full messages.
Returns all message values.
Defines the ForbiddenAttributesError
exception, which is raised when forbidden attributes are used for mass assignment.
1 params = ActionController::Parameters.new(name: 'Bob')
2 User.new(params) # => ActiveModel::ForbiddenAttributesError
3 params.permit!
4 User.new(params) # => #<User:0x007fefd4389020 ...>
You can check whether an object is compatible with the Active Model API by including ActiveModel::Lint::Tests
. It contains assertions that tell you whether your object is fully compliant.
The tests only check compatibility. They don’t attempt to determine the correctness of the returned values. For instance, you could implement valid?
to always return true
and the tests would still pass. It’s up to you to ensure that the values are correct.
Objects you pass in are expected to return a compliant object from a call to to_model
. Generally speaking, to_model
just returns self
.
Model
is a module mixin that includes the required interface for a Ruby object to work with Action Pack and Action View. Classes that include Model
get several other Active Model features out of the box, such as the following:
• Model name introspection
• Conversions
• Translations
• Validations
Like Active Record objects, Model
objects can also be initialized with a hash of attributes.
1 class Contact
2 include ActiveModel::Model
3
4 attr_accessor :name, :email, :message
5
6 validates :name, presence: true
7 validates :email, presence: true
8 validates :message, presence: true, length: { maximum: 300 }
9 end
The implementation of Model
is only 24 lines of code, reproduced here for reference purposes:
1 module ActiveModel
2 module Model
3 def self.included(base)
4 base.class_eval do
5 extend ActiveModel::Naming
6 extend ActiveModel::Translation
7 include ActiveModel::Validations
8 include ActiveModel::Conversion
9 end
10 end
11
12 def initialize(params={})
13 params.each do |attr, value|
14 self.public_send("#{attr}=", value)
15 end if params
16
17 super()
18 end
19
20 def persisted?
21 false
22 end
23 end
24 end
Name
extends String
and wraps a bunch of logic around your object’s name information so that it can be used with Rails.
How much name information could there be? Take a look at Name
’s constructor.
1 def initialize(klass, namespace = nil, name = nil)
2 @name = name || klass.name
3
4 raise ArgumentError, "Class name cannot be blank. You need to supply a
5 name argument when anonymous class given" if @name.blank?
6
7 @unnamespaced = @name.sub(/^#{namespace.name}::/, '') if namespace
8 @klass = klass
9 @singular = _singularize(@name)
10 @plural = ActiveSupport::Inflector.pluralize(@singular)
11 @element = ActiveSupport::Inflector.
12 underscore(ActiveSupport::Inflector.demodulize(@name))
13 @human = ActiveSupport::Inflector.humanize(@element)
14 @collection = ActiveSupport::Inflector.tableize(@name)
15 @param_key = (namespace ? _singularize(@unnamespaced) : @singular)
16 @i18n_key = @name.underscore.to_sym
17
18 @route_key = (namespace ? ActiveSupport::Inflector.
19 pluralize(@param_key) : @plural.dup)
20 @singular_route_key = ActiveSupport::Inflector.singularize(@route_key)
21 @route_key << "_index" if @plural == @singular
22 end
All this information is calculated and stored at initialization time, presumably since it’s used all over Rails.
Returns an underscored plural version of the model name.
Returns an underscored version of the model name.
Returns a translated human-readable version of the model name using I18n. The basic recipe is to capitalize the first word of the name.
1 BlogPost.model_name.human # => "Blog post"
Returns a symbol of the model name to be used as an I18n key.
Returns a version of the model name to be used for params names.
Returns a pluralized version of the model name.
Returns a singularized version of the model name.
Returns a singularized version of the model name to use while generating route names.
Naming
is the module that you extend in your class to get name type information for your model.
Returns an ActiveModel::Name
instance for the object. Used by Action Pack and Action View for naming-related functionality, such as routing.
Including the SecurePassword
module adds a single macro-style method has_secure_password
to your class, which adds the ability to set and authenticate against a BCrypt password.
A full explanation of how to use has_secure_password
is provided in the Chapter 14, “Authentication and Authorization,” in the section “has_secure_password
.”
Serialization
is a module to include in your models when you want to represent your model as a serializable hash. You only need to define an attributes
method, and the rest is handled for you.
1 class User
2 include ActiveModel::Serialization
3 attr_accessor :first_name, :last_name
4
5 def attributes
6 { 'first_name' => @first_name, 'last_name' => @last_name }
7 end
8 end
Returns the serializable hash representation of your model. Options provided can be one of the following:
:except Do not include these attributes.
:methods Include the supplied methods. The method name will be set as the key and its output the value.
:only Only include the supplied attributes.
Serializers::JSON
is a module to include in your models when you want to provide a JSON representation of your object. It automatically includes the module and depends on the attributes
and attributes=
methods to be present.
1 class User
2 include ActiveModel::Serializers::JSON
3 attr_accessor :first_name, :last_name
4
5 def attributes
6 { 'first_name' => @first_name, 'last_name' => @last_name }
7 end
8
9 def attributes=(attrs)
10 @first_name = attrs['first_name']
11 @last_name = attrs['last_name']
12 end
13 end
Returns a hash that can be used as the JSON representation for this object.
Decodes the supplied JSON, sets the attributes on the model, and returns self
.
Serializers::Xml
is a module to include in your models when you want to provide an XML representation of your object. It automatically includes the module and depends on the attributes
and attributes=
methods to be present.
1 class Pet
2 include ActiveModel::Serializers::XML
3 attr_accessor :name
4
5 def attributes
6 { 'name' => @name }
7 end
8
9 def attributes=(attrs)
10 @name = attrs['name']
11 end
12 end
Decodes the supplied XML, sets the attributes on the model, and returns self
.
Returns an XML representation of the object. Available options are the following:
:builder Supply a custom builder to generate the markup.
:except Do not include supplied attributes in the XML.
:indent Number of spaces to indent the XML.
:methods Include the supplied methods. The method name will be set as the key and its output the value.
:namespace Sets the XMLNS.
:only Only include the supplied attributes.
:skip_instruct Skip processing instructions.
:skip_types Skip typing.
:type Add a type to the XML tags.
Translation
provides the ability to add internationalization support to your model.
1 class User
2 extend ActiveModel::Translation
3 end
Transforms attribute names into a human-readable format with options. Available options are the following:
:default The default text for the attribute name.
Returns the i18n_scope
for the class (:activemodel
). Can be overridden if you want a custom lookup namespace.
Gets all ancestors of this class that support I18n.
Validations
adds a fully featured validations framework to your model. This includes the means to validate the following types of scenarios plus the ability to create custom validators.
• Absence of a field
• Acceptance of a field
• Confirmation of a field
• Exclusion of a field from a set of values
• Format of a field against a regular expression
• Inclusion of a field in a set of values
• Length of a field
• Numericality of a field
• Presence of a field
• Size of a field
1 class User
2 include ActiveModel::Validations
3
4 attr_accessor :name
5
6 validates_each :name do |record, attribute, value|
7 record.errors.add(attribute, 'should be present') if value.nil?
8 end
9 end
Note that available base options for validation macros that use options are as follows. If the specific validation has additional options, they will be explained there. All options are supplied as a hash and are the last element of the first set of arguments to the macros.
:allow_nil Specify whether to validate nil
attributes.
:if Only run if the supplied method or proc returns true
.
:on Define when the validation will run.
:strict If set to true
, will raise ActiveModel::StrictValidationFailed
over adding an error. It can also be set to any other exception.
:unless Only run if the supplied method or proc returns false
.
Returns true
if a method is defined for the supplied attribute.
1 class User
2 include ActiveModel::Validations
3
4 attr_accessor :name
5 end
6
7 User.attribute_method?(:name) # => true
Clears all the validators and validations.
Get all the errors for the model.
Checks if the object is invalid given the optional context.
Adds a single validation to the model. Can be a method name as a symbol or a block with options. An additional option is the following:
:allow_blank Specify whether to validate blank attributes.
Validates each of the attribute names against the supplied block. Options are passed in as a hash as the last element in the attrs
argument. An additional option is the following:
:allow_blank Specify whether to validate blank attributes.
Validates that an attribute is blank.
1 validates_absence_of :name
An additional option is the following:
:message An optional custom error message. Defaults to “must be blank.”
Validates that an attribute was accepted.
validates_acceptance_of :terms, on: :create
The following are additional options:
:accept Specify the value that is considered accepted.
:message An optional custom error message. Defaults to “must be accepted.”
The ActiveModel::Validations::Callbacks
module callbacks before_validation
and after_validation
to your model.
1 class Record
2 include ActiveModel::Validations::Callbacks
3
4 before_validation :some_before_validation_logic
5 after_validation :some_after_validation_logic
6 end
The interface is the same as ActiveModel::Callbacks
covered earlier in this appendix.
Validates that an attribute was confirmed. Adds a virtual *_confirmation
attribute that exists for validating the confirmation of the attribute. For example, validating the confirmation of a password
attribute would result in the validator adding an accessor for password_confirmation
.
validates_confirmation_of :password, message: "Please try again."
The following is an additional option:
:message An optional custom error message. Defaults to “doesn’t match confirmation.”
Validates that an attribute does not have a value supplied in the list.
validates_exclusion_of :age, in: 18..55
The following are additional options:
:allow_blank Specify whether to validate blank attributes.
:in An enumerable or range to check the value against. Can also be supplied as a proc, lambda, or symbol that returns an enumerable.
:message An optional custom error message. Defaults to “is reserved.”
Validates that an attribute conforms to the supplied format.
validates_format_of :phone, with: /A[d-()sx]+z/
The following are additional options:
:allow_blank Specify whether to validate blank attributes.
:message An optional custom error message. Defaults to “is invalid.”
:multiline Set to true
if the regular expression contains anchors that match the beginning or end of lines as opposed to the beginning or end of the string.
:with The regular expression to check if the format matches.
:without The regular expression to check that the format does not match.
Validates that an attribute is a value supplied in the list.
validates_inclusion_of :state, in: [ "CA", "NY" ]
The following are additional options:
:allow_blank Specify whether to validate blank attributes.
:in An enumerable or range to check the value against. Can also be supplied as a proc, lambda, or symbol that returns an enumerable.
:message An optional custom error message. Defaults to “is not included in the list.”
Validates that an attribute adheres to the supplied length limitations.
validates_length_of :name, maximum: 48
The following are additional options:
:allow_blank Specify whether to validate blank attributes.
:in Specify the range the length of the attribute can fall within.
:is Specify the exact length of the attribute.
:maximum Specify the maximum length of the attribute.
:message The error message to use for a :minimum
, :maximum
or :is
violation.
:minimum Specify the minimum length of the attribute.
:tokenizer A block to define how the string should be broken up. Defaults to ->(value) { value.split(//) }
.
:too_long Define a custom message if the attribute is too long. Defaults to “is too long (maximum is %{count} characters).”
:too_short Define a custom message if the attribute is too short. Defaults to “is too short (min is %{count} characters).”
:within Specify the range the length of the attribute can fall within.
* :wrong_length Define a custom message for an incorrect length. Defaults to “is the wrong length (should be %{count} characters).”
Validates that an attribute is numeric and optionally in a specified value range.
validates_numericality_of :score, only_integer: true
The following are additional options:
:equal_to Specify a value the attribute must be exactly.
:even Specify that the value must be even.
:greater_than Specify a value the attribute must be greater than.
:greater_than_or_equal_to Specify a value the attribute must be greater than or equal to.
:less_than Specify a value the attribute must be less than.
:less_than_or_equal_to Specify a value the attribute must be less than or equal to.
:message An optional custom error message, defaulting to “is not a number.”
:odd Specify that the value must be odd.
:only_integer Specify whether the value has to be an integer.
:other_than Specify a value the attribute must be other than.
The following can also be supplied with a proc or a symbol that corresponds to a method:
• :equal_to
• :greater_than
• :greater_than_or_equal_to
• :less_than
• :less_than_or_equal_to
validates_numericality_of :width, less_than: ->(person) { person.height }
Validates that an attribute is not blank.
validates_presence_of :foo
The following is an additional option:
:message An optional custom error message. Defaults to “can’t be blank.”
A method that allows setting all default validators and any custom validator classes ending in “Validator.” To illustrate, with a single declaration to validates
, we can set an attribute to validate presence and uniqueness.
validates :username, presence: true, uniqueness: true
The hash supplied to validates
can also handle arrays, ranges, regular expressions, and strings in shortcut form.
1 validates :email, format: /@/
2 validates :gender, inclusion: %w(male female)
3 validates :password, length: 6..20
The validates!
method allows setting all default validators and any custom validator classes ending in “Validator.” The difference between validates
and validates!
is that in the latter all errors are considered exception. Essentially, it’s the same as defining validates
with the :strict
option set to true
.
Validates the model with a supplied custom validator. The validator class must respond to and handle the options and error message addition internally.
1 class NameValidator < ActiveModel::Validator
2 def validate(object)
3 # Some validation logic here
4 end
5 end
6
7 class User
8 include ActiveModel::Validations
9 validates_with NameValidator, on: :update
10 end
Get all the validators being used by the class.
Get all the validators for the supplied attributes.
User.validators_on(:name)
Validator
provides a class that custom validators can extend to seamlessly integrate into the ActiveModel::Validations
API. It only requires that the new class defines a validate
method.
A full explanation of how to use Validator
and EachValidator
is provided in the Chapter 8, “Validations,” in the section “Custom Validation Techniques.”
1 class ScoreValidator < ActiveModel::Validator
2 include ActiveModel::Validations
3
4 def validate(object)
5 # Perform validations and add errors here.
6 end
7 end
Returns the type of the validator, which is a symbol of the underscored class name without “Validator” included.
This method must be overwritten in the validator in order to actually handle the validation itself.
18.191.178.211