rails/rails
Active Model
Active contributors: rafaelfranca, jeremy, dhh
Purpose
Active Model gives plain Ruby objects the same conventions an Active Record model has — validations, callbacks, dirty tracking, attribute typing, and serialization — without requiring a database. It is designed so a non-AR class (a form object, a value object, a TableLess model) can be used with Action View form helpers, JSON serializers, and validators.
Source: activemodel/lib/active_model/. ~9,000 lines of Ruby.
Directory layout
activemodel/
├── lib/
│ └── active_model/
│ ├── attribute.rb # Single attribute representation
│ ├── attribute_set/ # Collection of attributes for a model
│ ├── attributes.rb # The attribute DSL
│ ├── callbacks.rb # before_*/after_*/around_* callbacks
│ ├── dirty.rb # Track changed attributes
│ ├── errors.rb # ActiveModel::Errors collection
│ ├── model.rb # ActiveModel::Model convenience module
│ ├── naming.rb # ActiveModel::Name (singular/plural/route_key)
│ ├── secure_password/ # has_secure_password (bcrypt + argon2)
│ ├── serialization.rb # to_json, to_xml extension points
│ ├── translation.rb # I18n integration
│ ├── type/ # Type coercion (string, integer, decimal, etc.)
│ ├── validations/ # Built-in validators
│ ├── validations.rb # validates / validate DSL
│ ├── validator.rb # Base class for custom validators
│ └── api.rb # ActiveModel::API convenience module
├── test/
└── activemodel.gemspecKey abstractions
| Constant | File | Purpose |
|---|---|---|
ActiveModel::Model |
activemodel/lib/active_model/model.rb |
Convenience include for naming, conversion, validations, attribute assignment |
ActiveModel::API |
activemodel/lib/active_model/api.rb |
Lighter-weight version of Model for API-only objects |
ActiveModel::Validations |
activemodel/lib/active_model/validations.rb |
The validates DSL |
ActiveModel::Callbacks |
activemodel/lib/active_model/callbacks.rb |
Define before_save-style callbacks on plain objects |
ActiveModel::Dirty |
activemodel/lib/active_model/dirty.rb |
name_changed?, changes, previous_changes |
ActiveModel::Attributes |
activemodel/lib/active_model/attributes.rb |
Declare typed attributes (attribute :age, :integer) |
ActiveModel::AttributeSet |
activemodel/lib/active_model/attribute_set/ |
Container for an instance's attributes |
ActiveModel::Type::Value |
activemodel/lib/active_model/type/value.rb |
Base class for type coercers |
ActiveModel::Errors |
activemodel/lib/active_model/errors.rb |
Validation error collection with I18n |
ActiveModel::Name |
activemodel/lib/active_model/naming.rb |
User, users, user_path, etc. derivations |
ActiveModel::SecurePassword |
activemodel/lib/active_model/secure_password.rb |
has_secure_password for bcrypt or argon2 |
ActiveModel::Serialization |
activemodel/lib/active_model/serialization.rb |
as_json, to_json, to_xml |
Validations
Validations are the most-used feature. The DSL is:
class User
include ActiveModel::Model
attr_accessor :name, :email
validates :name, presence: true
validates :email, format: /@/
endBuilt-in validators live in activemodel/lib/active_model/validations/:
presence.rb/absence.rbacceptance.rb(e.g., terms-of-service checkbox)confirmation.rb(password_confirmationmatching)inclusion.rb/exclusion.rbformat.rblength.rbnumericality.rbcomparison.rbwith.rb(delegate to a custom validator)
Each is a subclass of ActiveModel::EachValidator. Custom validators inherit from ActiveModel::Validator (activemodel/lib/active_model/validator.rb).
Attributes
ActiveModel::Attributes lets a plain Ruby object declare typed attributes:
class Order
include ActiveModel::Attributes
attribute :total, :decimal
attribute :placed_at, :datetime
endTypes live in activemodel/lib/active_model/type/. Each type coerces on assignment, supports serialization, and integrates with Dirty and Errors. Active Record extends this same type system in activerecord/lib/active_record/type/.
Dirty tracking
ActiveModel::Dirty provides attribute_changed?, attribute_was, changes, and previous_changes. It works on top of ActiveModel::Attributes (each attribute's underlying Attribute knows its prior value).
person.name = "Bob"
person.name_changed? # => true
person.name_was # => "Alice"
person.changes # => { "name" => ["Alice", "Bob"] }The mutation tracker in attribute_mutation_tracker.rb handles in-place mutation of mutable types (e.g., Hash / Array attributes).
Errors
ActiveModel::Errors (activemodel/lib/active_model/errors.rb) is the collection used by record.errors. It supports:
- Per-attribute lookup (
errors[:name]). - Full-message formatting via I18n.
- Nested errors for has_many / has_one validations (
nested_error.rb).
Action View's form helpers automatically render error messages from this collection, so any class that exposes errors works with form_with.
Secure password
has_secure_password adds password hashing to a class. By default it uses bcrypt, with optional argon2 support if the argon2 gem is loaded. Implementation in activemodel/lib/active_model/secure_password.rb. The Gemfile lists both bcrypt and argon2 with require: false so the framework doesn't depend on a binary library at load time.
Naming
ActiveModel::Naming derives "name forms" used by helpers and routes:
ActiveModel::Name.new(User)
# => #<ActiveModel::Name: name="User", singular="user", plural="users",
# route_key="users", param_key="user", human="User">Action View's form_with, polymorphic_url, and most URL helpers rely on this.
Integration points
- Active Record mixes most of Active Model into
ActiveRecord::Base(activerecord/lib/active_record/base.rb). - Action View form helpers accept anything that responds to the Active Model lint methods.
- Action Mailer uses
ActiveModel::Namingto derive mailer view paths. ActiveModel::Lint::Tests(activemodel/lib/active_model/lint.rb) is a Minitest mixin for verifying that a class implements the Active Model contract.
Entry points for modification
- Adding a validator: subclass
ActiveModel::EachValidator, place underactivemodel/lib/active_model/validations/, add a test underactivemodel/test/cases/validations/. - Adding an attribute type: subclass
ActiveModel::Type::Value, register viaActiveModel::Type.register(:my_type, ::Type::MyType). - Adding a callback group: use
define_model_callbacksfromActiveModel::Callbacks(delegates toActiveSupport::Callbacks).
Related pages
- packages/active-record — the largest consumer of Active Model.
- packages/active-support —
ActiveSupport::Callbackspowers the callback DSL. - packages/action-view — where form helpers consume
ActiveModel::ErrorsandNaming.
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.