mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
c477d95604
Currently `ActiveModel::Model` is defined as the minimum API to talk with Action Pack and Action View. Its name suggests it can be included to create Active Record type models, but for creating models it's probably too minimal. For example it's very common to include ActiveModel::Attributes as well. By moving `ActiveModel::Model`'s implementation to a new `ActiveModel::API` we keep a definition of the minimum API to talk with Action Pack and Action View. For `ActiveModel::Model` we only need to include `ActiveModel::API`. This will allow adding more funcationality to `ActiveModel::Model` while keeping backwards compatibility. Co-authored-by: Nathaniel Watts <1141717+thewatts@users.noreply.github.com>
266 lines
6.5 KiB
Text
266 lines
6.5 KiB
Text
= Active Model -- model interfaces for Rails
|
|
|
|
Active Model provides a known set of interfaces for usage in model classes.
|
|
They allow for Action Pack helpers to interact with non-Active Record models,
|
|
for example. Active Model also helps with building custom ORMs for use outside of
|
|
the Rails framework.
|
|
|
|
You can read more about Active Model in the {Active Model Basics}[https://edgeguides.rubyonrails.org/active_model_basics.html] guide.
|
|
|
|
Prior to Rails 3.0, if a plugin or gem developer wanted to have an object
|
|
interact with Action Pack helpers, it was required to either copy chunks of
|
|
code from Rails, or monkey patch entire helpers to make them handle objects
|
|
that did not exactly conform to the Active Record interface. This would result
|
|
in code duplication and fragile applications that broke on upgrades. Active
|
|
Model solves this by defining an explicit API. You can read more about the
|
|
API in <tt>ActiveModel::Lint::Tests</tt>.
|
|
|
|
Active Model provides a default module that implements the basic API required
|
|
to integrate with Action Pack out of the box: <tt>ActiveModel::API</tt>.
|
|
|
|
class Person
|
|
include ActiveModel::API
|
|
|
|
attr_accessor :name, :age
|
|
validates_presence_of :name
|
|
end
|
|
|
|
person = Person.new(name: 'bob', age: '18')
|
|
person.name # => 'bob'
|
|
person.age # => '18'
|
|
person.valid? # => true
|
|
|
|
It includes model name introspections, conversions, translations and
|
|
validations, resulting in a class suitable to be used with Action Pack.
|
|
See <tt>ActiveModel::API</tt> for more examples.
|
|
|
|
Active Model also provides the following functionality to have ORM-like
|
|
behavior out of the box:
|
|
|
|
* Add attribute magic to objects
|
|
|
|
class Person
|
|
include ActiveModel::AttributeMethods
|
|
|
|
attribute_method_prefix 'clear_'
|
|
define_attribute_methods :name, :age
|
|
|
|
attr_accessor :name, :age
|
|
|
|
def clear_attribute(attr)
|
|
send("#{attr}=", nil)
|
|
end
|
|
end
|
|
|
|
person = Person.new
|
|
person.clear_name
|
|
person.clear_age
|
|
|
|
{Learn more}[link:classes/ActiveModel/AttributeMethods.html]
|
|
|
|
* Callbacks for certain operations
|
|
|
|
class Person
|
|
extend ActiveModel::Callbacks
|
|
define_model_callbacks :create
|
|
|
|
def create
|
|
run_callbacks :create do
|
|
# Your create action methods here
|
|
end
|
|
end
|
|
end
|
|
|
|
This generates +before_create+, +around_create+ and +after_create+
|
|
class methods that wrap your create method.
|
|
|
|
{Learn more}[link:classes/ActiveModel/Callbacks.html]
|
|
|
|
* Tracking value changes
|
|
|
|
class Person
|
|
include ActiveModel::Dirty
|
|
|
|
define_attribute_methods :name
|
|
|
|
def name
|
|
@name
|
|
end
|
|
|
|
def name=(val)
|
|
name_will_change! unless val == @name
|
|
@name = val
|
|
end
|
|
|
|
def save
|
|
# do persistence work
|
|
changes_applied
|
|
end
|
|
end
|
|
|
|
person = Person.new
|
|
person.name # => nil
|
|
person.changed? # => false
|
|
person.name = 'bob'
|
|
person.changed? # => true
|
|
person.changed # => ['name']
|
|
person.changes # => { 'name' => [nil, 'bob'] }
|
|
person.save
|
|
person.name = 'robert'
|
|
person.save
|
|
person.previous_changes # => {'name' => ['bob, 'robert']}
|
|
|
|
{Learn more}[link:classes/ActiveModel/Dirty.html]
|
|
|
|
* Adding +errors+ interface to objects
|
|
|
|
Exposing error messages allows objects to interact with Action Pack
|
|
helpers seamlessly.
|
|
|
|
class Person
|
|
|
|
def initialize
|
|
@errors = ActiveModel::Errors.new(self)
|
|
end
|
|
|
|
attr_accessor :name
|
|
attr_reader :errors
|
|
|
|
def validate!
|
|
errors.add(:name, "cannot be nil") if name.nil?
|
|
end
|
|
|
|
def self.human_attribute_name(attr, options = {})
|
|
"Name"
|
|
end
|
|
end
|
|
|
|
person = Person.new
|
|
person.name = nil
|
|
person.validate!
|
|
person.errors.full_messages
|
|
# => ["Name cannot be nil"]
|
|
|
|
{Learn more}[link:classes/ActiveModel/Errors.html]
|
|
|
|
* Model name introspection
|
|
|
|
class NamedPerson
|
|
extend ActiveModel::Naming
|
|
end
|
|
|
|
NamedPerson.model_name.name # => "NamedPerson"
|
|
NamedPerson.model_name.human # => "Named person"
|
|
|
|
{Learn more}[link:classes/ActiveModel/Naming.html]
|
|
|
|
* Making objects serializable
|
|
|
|
<tt>ActiveModel::Serialization</tt> provides a standard interface for your object
|
|
to provide +to_json+ serialization.
|
|
|
|
class SerialPerson
|
|
include ActiveModel::Serialization
|
|
|
|
attr_accessor :name
|
|
|
|
def attributes
|
|
{'name' => name}
|
|
end
|
|
end
|
|
|
|
s = SerialPerson.new
|
|
s.serializable_hash # => {"name"=>nil}
|
|
|
|
class SerialPerson
|
|
include ActiveModel::Serializers::JSON
|
|
end
|
|
|
|
s = SerialPerson.new
|
|
s.to_json # => "{\"name\":null}"
|
|
|
|
{Learn more}[link:classes/ActiveModel/Serialization.html]
|
|
|
|
* Internationalization (i18n) support
|
|
|
|
class Person
|
|
extend ActiveModel::Translation
|
|
end
|
|
|
|
Person.human_attribute_name('my_attribute')
|
|
# => "My attribute"
|
|
|
|
{Learn more}[link:classes/ActiveModel/Translation.html]
|
|
|
|
* Validation support
|
|
|
|
class Person
|
|
include ActiveModel::Validations
|
|
|
|
attr_accessor :first_name, :last_name
|
|
|
|
validates_each :first_name, :last_name do |record, attr, value|
|
|
record.errors.add attr, "starts with z." if value.start_with?("z")
|
|
end
|
|
end
|
|
|
|
person = Person.new
|
|
person.first_name = 'zoolander'
|
|
person.valid? # => false
|
|
|
|
{Learn more}[link:classes/ActiveModel/Validations.html]
|
|
|
|
* Custom validators
|
|
|
|
class HasNameValidator < ActiveModel::Validator
|
|
def validate(record)
|
|
record.errors.add(:name, "must exist") if record.name.blank?
|
|
end
|
|
end
|
|
|
|
class ValidatorPerson
|
|
include ActiveModel::Validations
|
|
validates_with HasNameValidator
|
|
attr_accessor :name
|
|
end
|
|
|
|
p = ValidatorPerson.new
|
|
p.valid? # => false
|
|
p.errors.full_messages # => ["Name must exist"]
|
|
p.name = "Bob"
|
|
p.valid? # => true
|
|
|
|
{Learn more}[link:classes/ActiveModel/Validator.html]
|
|
|
|
|
|
== Download and installation
|
|
|
|
The latest version of Active Model can be installed with RubyGems:
|
|
|
|
$ gem install activemodel
|
|
|
|
Source code can be downloaded as part of the Rails project on GitHub
|
|
|
|
* https://github.com/rails/rails/tree/main/activemodel
|
|
|
|
|
|
== License
|
|
|
|
Active Model is released under the MIT license:
|
|
|
|
* https://opensource.org/licenses/MIT
|
|
|
|
|
|
== Support
|
|
|
|
API documentation is at:
|
|
|
|
* https://api.rubyonrails.org
|
|
|
|
Bug reports for the Ruby on Rails project can be filed here:
|
|
|
|
* https://github.com/rails/rails/issues
|
|
|
|
Feature requests should be discussed on the rails-core mailing list here:
|
|
|
|
* https://discuss.rubyonrails.org/c/rubyonrails-core
|