2010-07-22 07:59:58 +00:00
|
|
|
= Active Model -- model interfaces for Rails
|
|
|
|
|
|
|
|
Active Model provides a known set of interfaces for usage in model classes.
|
2013-01-15 10:52:02 +00:00
|
|
|
They allow for Action Pack helpers to interact with non-Active Record models,
|
2013-03-13 01:44:10 +00:00
|
|
|
for example. Active Model also helps with building custom ORMs for use outside of
|
2010-07-22 07:59:58 +00:00
|
|
|
the Rails framework.
|
|
|
|
|
2019-03-09 21:47:01 +00:00
|
|
|
You can read more about Active Model in the {Active Model Basics}[https://edgeguides.rubyonrails.org/active_model_basics.html] guide.
|
|
|
|
|
2010-07-22 07:59:58 +00:00
|
|
|
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
|
2011-03-04 03:23:30 +00:00
|
|
|
that did not exactly conform to the Active Record interface. This would result
|
2012-02-07 22:17:24 +00:00
|
|
|
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
|
2013-01-15 10:52:02 +00:00
|
|
|
API in <tt>ActiveModel::Lint::Tests</tt>.
|
2010-07-22 07:59:58 +00:00
|
|
|
|
2012-03-03 07:19:39 +00:00
|
|
|
Active Model provides a default module that implements the basic API required
|
2021-04-21 18:54:16 +00:00
|
|
|
to integrate with Action Pack out of the box: <tt>ActiveModel::API</tt>.
|
2012-03-03 07:19:39 +00:00
|
|
|
|
|
|
|
class Person
|
2021-04-21 18:54:16 +00:00
|
|
|
include ActiveModel::API
|
2012-03-03 07:19:39 +00:00
|
|
|
|
|
|
|
attr_accessor :name, :age
|
|
|
|
validates_presence_of :name
|
|
|
|
end
|
|
|
|
|
2012-10-10 11:45:47 +00:00
|
|
|
person = Person.new(name: 'bob', age: '18')
|
2013-04-12 15:22:39 +00:00
|
|
|
person.name # => 'bob'
|
|
|
|
person.age # => '18'
|
2012-04-21 20:16:38 +00:00
|
|
|
person.valid? # => true
|
2012-03-03 07:19:39 +00:00
|
|
|
|
2012-03-05 17:16:44 +00:00
|
|
|
It includes model name introspections, conversions, translations and
|
|
|
|
validations, resulting in a class suitable to be used with Action Pack.
|
2021-04-21 18:54:16 +00:00
|
|
|
See <tt>ActiveModel::API</tt> for more examples.
|
2012-03-03 07:19:39 +00:00
|
|
|
|
2012-02-07 22:17:24 +00:00
|
|
|
Active Model also provides the following functionality to have ORM-like
|
|
|
|
behavior out of the box:
|
2010-07-22 07:59:58 +00:00
|
|
|
|
|
|
|
* Add attribute magic to objects
|
|
|
|
|
2010-01-17 01:42:53 +00:00
|
|
|
class Person
|
|
|
|
include ActiveModel::AttributeMethods
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2010-01-17 01:42:53 +00:00
|
|
|
attribute_method_prefix 'clear_'
|
2012-05-14 16:38:23 +00:00
|
|
|
define_attribute_methods :name, :age
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2010-01-17 01:42:53 +00:00
|
|
|
attr_accessor :name, :age
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2010-01-17 01:42:53 +00:00
|
|
|
def clear_attribute(attr)
|
|
|
|
send("#{attr}=", nil)
|
|
|
|
end
|
|
|
|
end
|
2015-02-20 22:57:56 +00:00
|
|
|
|
2014-03-07 14:08:08 +00:00
|
|
|
person = Person.new
|
2010-07-22 07:59:58 +00:00
|
|
|
person.clear_name
|
|
|
|
person.clear_age
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2012-05-07 15:13:04 +00:00
|
|
|
{Learn more}[link:classes/ActiveModel/AttributeMethods.html]
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2010-07-22 07:59:58 +00:00
|
|
|
* Callbacks for certain operations
|
2010-01-16 12:09:32 +00:00
|
|
|
|
2010-01-17 01:42:53 +00:00
|
|
|
class Person
|
2010-01-16 12:09:32 +00:00
|
|
|
extend ActiveModel::Callbacks
|
|
|
|
define_model_callbacks :create
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2010-01-16 12:09:32 +00:00
|
|
|
def create
|
2011-01-09 18:15:05 +00:00
|
|
|
run_callbacks :create do
|
2010-01-16 12:09:32 +00:00
|
|
|
# Your create action methods here
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2010-07-22 07:59:58 +00:00
|
|
|
This generates +before_create+, +around_create+ and +after_create+
|
|
|
|
class methods that wrap your create method.
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2012-05-07 15:13:04 +00:00
|
|
|
{Learn more}[link:classes/ActiveModel/Callbacks.html]
|
2007-11-09 14:59:15 +00:00
|
|
|
|
2010-07-22 07:59:58 +00:00
|
|
|
* Tracking value changes
|
2010-01-14 21:29:08 +00:00
|
|
|
|
2012-10-19 14:58:30 +00:00
|
|
|
class Person
|
|
|
|
include ActiveModel::Dirty
|
|
|
|
|
2014-03-07 14:08:08 +00:00
|
|
|
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
|
2012-10-19 14:58:30 +00:00
|
|
|
end
|
2010-01-16 12:09:32 +00:00
|
|
|
|
2010-01-16 11:21:07 +00:00
|
|
|
person = Person.new
|
2013-04-12 15:22:39 +00:00
|
|
|
person.name # => nil
|
|
|
|
person.changed? # => false
|
2010-01-14 23:01:40 +00:00
|
|
|
person.name = 'bob'
|
2013-04-12 15:22:39 +00:00
|
|
|
person.changed? # => true
|
|
|
|
person.changed # => ['name']
|
|
|
|
person.changes # => { 'name' => [nil, 'bob'] }
|
2014-03-07 14:08:08 +00:00
|
|
|
person.save
|
2010-01-14 23:01:40 +00:00
|
|
|
person.name = 'robert'
|
|
|
|
person.save
|
|
|
|
person.previous_changes # => {'name' => ['bob, 'robert']}
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2012-05-07 15:13:04 +00:00
|
|
|
{Learn more}[link:classes/ActiveModel/Dirty.html]
|
2010-01-16 12:03:20 +00:00
|
|
|
|
2010-07-22 07:59:58 +00:00
|
|
|
* Adding +errors+ interface to objects
|
2010-01-17 04:17:54 +00:00
|
|
|
|
2010-07-22 07:59:58 +00:00
|
|
|
Exposing error messages allows objects to interact with Action Pack
|
|
|
|
helpers seamlessly.
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2010-01-17 04:17:54 +00:00
|
|
|
class Person
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2010-01-17 04:17:54 +00:00
|
|
|
def initialize
|
|
|
|
@errors = ActiveModel::Errors.new(self)
|
|
|
|
end
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2010-01-17 04:17:54 +00:00
|
|
|
attr_accessor :name
|
|
|
|
attr_reader :errors
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2010-01-17 04:17:54 +00:00
|
|
|
def validate!
|
2014-01-03 22:02:31 +00:00
|
|
|
errors.add(:name, "cannot be nil") if name.nil?
|
2010-01-17 04:17:54 +00:00
|
|
|
end
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2012-02-07 22:29:02 +00:00
|
|
|
def self.human_attribute_name(attr, options = {})
|
2010-01-17 04:17:54 +00:00
|
|
|
"Name"
|
|
|
|
end
|
|
|
|
end
|
2015-02-20 22:57:56 +00:00
|
|
|
|
2014-03-07 14:08:08 +00:00
|
|
|
person = Person.new
|
|
|
|
person.name = nil
|
|
|
|
person.validate!
|
2010-01-17 04:17:54 +00:00
|
|
|
person.errors.full_messages
|
2014-01-03 22:02:31 +00:00
|
|
|
# => ["Name cannot be nil"]
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2012-05-07 15:13:04 +00:00
|
|
|
{Learn more}[link:classes/ActiveModel/Errors.html]
|
2010-01-17 04:35:18 +00:00
|
|
|
|
2010-07-22 07:59:58 +00:00
|
|
|
* Model name introspection
|
2010-01-17 04:35:18 +00:00
|
|
|
|
2010-01-17 04:52:33 +00:00
|
|
|
class NamedPerson
|
|
|
|
extend ActiveModel::Naming
|
|
|
|
end
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2014-05-29 15:01:10 +00:00
|
|
|
NamedPerson.model_name.name # => "NamedPerson"
|
2010-08-12 15:09:58 +00:00
|
|
|
NamedPerson.model_name.human # => "Named person"
|
2010-01-17 04:52:33 +00:00
|
|
|
|
2012-05-07 15:13:04 +00:00
|
|
|
{Learn more}[link:classes/ActiveModel/Naming.html]
|
2010-01-17 04:52:33 +00:00
|
|
|
|
2010-07-22 07:59:58 +00:00
|
|
|
* Making objects serializable
|
2010-01-17 09:14:14 +00:00
|
|
|
|
2015-07-08 02:58:22 +00:00
|
|
|
<tt>ActiveModel::Serialization</tt> provides a standard interface for your object
|
2015-05-05 18:11:35 +00:00
|
|
|
to provide +to_json+ serialization.
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2012-10-19 14:58:30 +00:00
|
|
|
class SerialPerson
|
|
|
|
include ActiveModel::Serialization
|
|
|
|
|
|
|
|
attr_accessor :name
|
|
|
|
|
|
|
|
def attributes
|
|
|
|
{'name' => name}
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-01-17 09:14:14 +00:00
|
|
|
s = SerialPerson.new
|
|
|
|
s.serializable_hash # => {"name"=>nil}
|
2012-10-19 15:24:34 +00:00
|
|
|
|
|
|
|
class SerialPerson
|
|
|
|
include ActiveModel::Serializers::JSON
|
|
|
|
end
|
|
|
|
|
|
|
|
s = SerialPerson.new
|
2010-01-17 09:14:14 +00:00
|
|
|
s.to_json # => "{\"name\":null}"
|
2012-10-19 15:24:34 +00:00
|
|
|
|
2012-05-07 15:13:04 +00:00
|
|
|
{Learn more}[link:classes/ActiveModel/Serialization.html]
|
2010-01-18 06:20:25 +00:00
|
|
|
|
2010-07-22 07:59:58 +00:00
|
|
|
* Internationalization (i18n) support
|
2010-01-18 07:13:49 +00:00
|
|
|
|
|
|
|
class Person
|
|
|
|
extend ActiveModel::Translation
|
|
|
|
end
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2010-07-22 07:59:58 +00:00
|
|
|
Person.human_attribute_name('my_attribute')
|
2010-08-12 15:09:58 +00:00
|
|
|
# => "My attribute"
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2012-05-07 15:13:04 +00:00
|
|
|
{Learn more}[link:classes/ActiveModel/Translation.html]
|
2010-01-18 07:29:33 +00:00
|
|
|
|
2010-07-22 07:59:58 +00:00
|
|
|
* Validation support
|
2010-01-18 07:29:33 +00:00
|
|
|
|
2014-03-07 14:08:08 +00:00
|
|
|
class Person
|
|
|
|
include ActiveModel::Validations
|
2010-01-18 07:29:33 +00:00
|
|
|
|
2014-03-07 14:08:08 +00:00
|
|
|
attr_accessor :first_name, :last_name
|
2010-01-18 07:29:33 +00:00
|
|
|
|
2014-03-07 14:08:08 +00:00
|
|
|
validates_each :first_name, :last_name do |record, attr, value|
|
2020-05-29 01:20:13 +00:00
|
|
|
record.errors.add attr, "starts with z." if value.start_with?("z")
|
2014-03-07 14:08:08 +00:00
|
|
|
end
|
|
|
|
end
|
2010-01-18 07:29:33 +00:00
|
|
|
|
2014-03-07 14:08:08 +00:00
|
|
|
person = Person.new
|
|
|
|
person.first_name = 'zoolander'
|
|
|
|
person.valid? # => false
|
2010-01-18 07:29:33 +00:00
|
|
|
|
2012-05-07 15:13:04 +00:00
|
|
|
{Learn more}[link:classes/ActiveModel/Validations.html]
|
2010-08-14 05:13:00 +00:00
|
|
|
|
2010-07-22 07:59:58 +00:00
|
|
|
* Custom validators
|
2015-02-20 22:57:56 +00:00
|
|
|
|
2014-03-07 14:08:08 +00:00
|
|
|
class HasNameValidator < ActiveModel::Validator
|
|
|
|
def validate(record)
|
2015-02-20 22:57:56 +00:00
|
|
|
record.errors.add(:name, "must exist") if record.name.blank?
|
2014-03-07 14:08:08 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
class ValidatorPerson
|
|
|
|
include ActiveModel::Validations
|
|
|
|
validates_with HasNameValidator
|
|
|
|
attr_accessor :name
|
|
|
|
end
|
2010-01-19 00:29:01 +00:00
|
|
|
|
2014-03-07 14:08:08 +00:00
|
|
|
p = ValidatorPerson.new
|
|
|
|
p.valid? # => false
|
|
|
|
p.errors.full_messages # => ["Name must exist"]
|
|
|
|
p.name = "Bob"
|
|
|
|
p.valid? # => true
|
2010-01-19 00:29:01 +00:00
|
|
|
|
2012-05-07 15:13:04 +00:00
|
|
|
{Learn more}[link:classes/ActiveModel/Validator.html]
|
2012-02-07 22:17:24 +00:00
|
|
|
|
2011-07-21 07:33:45 +00:00
|
|
|
|
|
|
|
== Download and installation
|
|
|
|
|
2011-08-05 08:17:09 +00:00
|
|
|
The latest version of Active Model can be installed with RubyGems:
|
2011-07-21 07:33:45 +00:00
|
|
|
|
2015-12-06 18:16:26 +00:00
|
|
|
$ gem install activemodel
|
2011-07-21 07:33:45 +00:00
|
|
|
|
|
|
|
Source code can be downloaded as part of the Rails project on GitHub
|
|
|
|
|
2021-01-19 20:43:24 +00:00
|
|
|
* https://github.com/rails/rails/tree/main/activemodel
|
2011-07-21 07:33:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
== License
|
|
|
|
|
2011-12-23 21:03:21 +00:00
|
|
|
Active Model is released under the MIT license:
|
|
|
|
|
2017-08-21 23:46:02 +00:00
|
|
|
* https://opensource.org/licenses/MIT
|
2011-07-21 07:33:45 +00:00
|
|
|
|
|
|
|
|
|
|
|
== Support
|
|
|
|
|
2017-11-28 18:27:43 +00:00
|
|
|
API documentation is at:
|
2011-07-21 07:33:45 +00:00
|
|
|
|
2019-03-08 11:36:16 +00:00
|
|
|
* https://api.rubyonrails.org
|
2011-07-21 07:33:45 +00:00
|
|
|
|
2017-11-28 18:27:43 +00:00
|
|
|
Bug reports for the Ruby on Rails project can be filed here:
|
2011-07-21 07:33:45 +00:00
|
|
|
|
|
|
|
* https://github.com/rails/rails/issues
|
2014-06-02 02:11:39 +00:00
|
|
|
|
|
|
|
Feature requests should be discussed on the rails-core mailing list here:
|
|
|
|
|
2020-03-26 06:21:37 +00:00
|
|
|
* https://discuss.rubyonrails.org/c/rubyonrails-core
|