2010-07-22 03:59:58 -04:00
|
|
|
= 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-ActiveRecord models,
|
|
|
|
for example. Active Model also helps building custom ORMs for use outside of
|
|
|
|
the Rails framework.
|
|
|
|
|
|
|
|
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 exacly conform to the Active Record interface. This would result
|
|
|
|
in code duplication and fragile applications that broke on upgrades.
|
|
|
|
|
|
|
|
Active Model solves this. You can include functionality from the following
|
|
|
|
modules:
|
|
|
|
|
|
|
|
* Add attribute magic to objects
|
|
|
|
|
2010-01-16 20:42:53 -05:00
|
|
|
class Person
|
|
|
|
include ActiveModel::AttributeMethods
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-16 20:42:53 -05:00
|
|
|
attribute_method_prefix 'clear_'
|
|
|
|
define_attribute_methods [:name, :age]
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-16 20:42:53 -05:00
|
|
|
attr_accessor :name, :age
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-16 20:42:53 -05:00
|
|
|
def clear_attribute(attr)
|
|
|
|
send("#{attr}=", nil)
|
|
|
|
end
|
|
|
|
end
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
person.clear_name
|
|
|
|
person.clear_age
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-16 20:42:53 -05:00
|
|
|
{Learn more}[link:classes/ActiveModel/AttributeMethods.html]
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
* Callbacks for certain operations
|
2010-01-16 07:09:32 -05:00
|
|
|
|
2010-01-16 20:42:53 -05:00
|
|
|
class Person
|
2010-01-16 07:09:32 -05:00
|
|
|
extend ActiveModel::Callbacks
|
|
|
|
define_model_callbacks :create
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-16 07:09:32 -05:00
|
|
|
def create
|
|
|
|
_run_create_callbacks do
|
|
|
|
# Your create action methods here
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
This generates +before_create+, +around_create+ and +after_create+
|
|
|
|
class methods that wrap your create method.
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-14 16:19:53 -05:00
|
|
|
{Learn more}[link:classes/ActiveModel/CallBacks.html]
|
2007-11-09 09:59:15 -05:00
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
* Tracking value changes
|
2010-01-14 16:29:08 -05:00
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
The ActiveModel::Dirty module allows for tracking attribute changes:
|
2010-01-16 07:09:32 -05:00
|
|
|
|
2010-01-16 06:21:07 -05:00
|
|
|
person = Person.new
|
|
|
|
person.name # => nil
|
2010-01-14 18:01:40 -05:00
|
|
|
person.changed? # => false
|
|
|
|
person.name = 'bob'
|
|
|
|
person.changed? # => true
|
|
|
|
person.changed # => ['name']
|
2010-01-16 06:21:07 -05:00
|
|
|
person.changes # => { 'name' => [nil, 'bob'] }
|
2010-01-14 18:01:40 -05:00
|
|
|
person.name = 'robert'
|
|
|
|
person.save
|
|
|
|
person.previous_changes # => {'name' => ['bob, 'robert']}
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-16 07:03:20 -05:00
|
|
|
{Learn more}[link:classes/ActiveModel/Dirty.html]
|
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
* Adding +errors+ interface to objects
|
2010-01-16 23:17:54 -05:00
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
Exposing error messages allows objects to interact with Action Pack
|
|
|
|
helpers seamlessly.
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-16 23:17:54 -05:00
|
|
|
class Person
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-16 23:17:54 -05:00
|
|
|
def initialize
|
|
|
|
@errors = ActiveModel::Errors.new(self)
|
|
|
|
end
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-16 23:17:54 -05:00
|
|
|
attr_accessor :name
|
|
|
|
attr_reader :errors
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-16 23:17:54 -05:00
|
|
|
def validate!
|
2010-12-19 14:37:33 -05:00
|
|
|
errors.add(:name, "can not be nil") if name.nil?
|
2010-01-16 23:17:54 -05:00
|
|
|
end
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-16 23:17:54 -05:00
|
|
|
def ErrorsPerson.human_attribute_name(attr, options = {})
|
|
|
|
"Name"
|
|
|
|
end
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-16 23:17:54 -05:00
|
|
|
end
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-16 23:17:54 -05:00
|
|
|
person.errors.full_messages
|
2010-12-19 14:37:33 -05:00
|
|
|
# => ["Name can not be nil"]
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-16 23:17:54 -05:00
|
|
|
person.errors.full_messages
|
2010-12-19 14:37:33 -05:00
|
|
|
# => ["Name can not be nil"]
|
2010-01-16 23:17:54 -05:00
|
|
|
|
|
|
|
{Learn more}[link:classes/ActiveModel/Errors.html]
|
2010-01-16 23:35:18 -05:00
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
* Model name introspection
|
2010-01-16 23:35:18 -05:00
|
|
|
|
2010-01-16 23:52:33 -05:00
|
|
|
class NamedPerson
|
|
|
|
extend ActiveModel::Naming
|
|
|
|
end
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-08-12 11:09:58 -04:00
|
|
|
NamedPerson.model_name # => "NamedPerson"
|
|
|
|
NamedPerson.model_name.human # => "Named person"
|
2010-01-16 23:52:33 -05:00
|
|
|
|
|
|
|
{Learn more}[link:classes/ActiveModel/Naming.html]
|
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
* Observer support
|
2010-01-17 03:12:12 -05:00
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
ActiveModel::Observers allows your object to implement the Observer
|
|
|
|
pattern in a Rails App and take advantage of all the standard observer
|
|
|
|
functions.
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-17 03:12:12 -05:00
|
|
|
{Learn more}[link:classes/ActiveModel/Observer.html]
|
2010-01-17 04:14:14 -05:00
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
* Making objects serializable
|
2010-01-17 04:14:14 -05:00
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
ActiveModel::Serialization provides a standard interface for your object
|
|
|
|
to provide +to_json+ or +to_xml+ serialization.
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-17 04:14:14 -05:00
|
|
|
s = SerialPerson.new
|
|
|
|
s.serializable_hash # => {"name"=>nil}
|
|
|
|
s.to_json # => "{\"name\":null}"
|
|
|
|
s.to_xml # => "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<serial-person...
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-17 04:14:14 -05:00
|
|
|
{Learn more}[link:classes/ActiveModel/Serialization.html]
|
2010-01-18 01:20:25 -05:00
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
* Internationalization (i18n) support
|
2010-01-18 02:13:49 -05:00
|
|
|
|
|
|
|
class Person
|
|
|
|
extend ActiveModel::Translation
|
|
|
|
end
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
Person.human_attribute_name('my_attribute')
|
2010-08-12 11:09:58 -04:00
|
|
|
# => "My attribute"
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-18 02:13:49 -05:00
|
|
|
{Learn more}[link:classes/ActiveModel/Translation.html]
|
2010-01-18 02:29:33 -05:00
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
* Validation support
|
2010-01-18 02:29:33 -05:00
|
|
|
|
|
|
|
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.to_s[0] == ?z
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2010-06-11 06:20:30 -04:00
|
|
|
person = Person.new
|
|
|
|
person.first_name = 'zoolander'
|
2010-08-12 11:09:58 -04:00
|
|
|
person.valid? # => false
|
2010-01-18 02:29:33 -05:00
|
|
|
|
|
|
|
{Learn more}[link:classes/ActiveModel/Validations.html]
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-07-22 03:59:58 -04:00
|
|
|
* Custom validators
|
2010-01-18 19:29:01 -05:00
|
|
|
|
|
|
|
class Person
|
|
|
|
include ActiveModel::Validations
|
|
|
|
validates_with HasNameValidator
|
|
|
|
attr_accessor :name
|
|
|
|
end
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-18 19:29:01 -05:00
|
|
|
class HasNameValidator < ActiveModel::Validator
|
|
|
|
def validate(record)
|
|
|
|
record.errors[:name] = "must exist" if record.name.blank?
|
|
|
|
end
|
|
|
|
end
|
2010-08-14 01:13:00 -04:00
|
|
|
|
2010-01-18 19:29:01 -05:00
|
|
|
p = ValidatorPerson.new
|
2010-08-12 11:09:58 -04:00
|
|
|
p.valid? # => false
|
|
|
|
p.errors.full_messages # => ["Name must exist"]
|
2010-01-18 19:29:01 -05:00
|
|
|
p.name = "Bob"
|
2010-08-12 11:09:58 -04:00
|
|
|
p.valid? # => true
|
2010-01-18 19:29:01 -05:00
|
|
|
|
|
|
|
{Learn more}[link:classes/ActiveModel/Validator.html]
|