c2ca73c9ee
`Observable#notify_observers` from Ruby always returns false (which halts ActiveRecord callback chains) and has extra features (like `changed`) that were never used. Signed-off-by: Jeremy Kemper <jeremy@bitsweat.net> |
||
---|---|---|
.. | ||
examples | ||
lib | ||
test | ||
activemodel.gemspec | ||
CHANGELOG | ||
MIT-LICENSE | ||
Rakefile | ||
README |
= Active Model - defined interfaces for Rails Prior to Rails 3.0, if a plugin or gem developer wanted to be able 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 look like Active Record. This generated code duplication and fragile applications that broke on upgrades. Active Model is a solution for this problem. Active Model provides a known set of interfaces that your objects can implement to then present a common interface to the Action Pack helpers. You can include functionality from the following modules: * Adding attribute magic to your objects Add prefixes and suffixes to defined attribute methods... 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 ...gives you clear_name, clear_age. {Learn more}[link:classes/ActiveModel/AttributeMethods.html] * Adding callbacks to your objects class Person extend ActiveModel::Callbacks define_model_callbacks :create def create _run_create_callbacks do # Your create action methods here end end end ...gives you before_create, around_create and after_create class methods that wrap your create method. {Learn more}[link:classes/ActiveModel/CallBacks.html] * For classes that already look like an Active Record object class Person include ActiveModel::Conversion end ...returns the class itself when sent :to_model {Learn more}[link:classes/ActiveModel/Conversion.html] * Tracking changes in your object Provides all the value tracking features implemented by ActiveRecord... person = Person.new person.name # => nil person.changed? # => false person.name = 'bob' person.changed? # => true person.changed # => ['name'] person.changes # => { 'name' => [nil, 'bob'] } person.name = 'robert' person.save person.previous_changes # => {'name' => ['bob, 'robert']} {Learn more}[link:classes/ActiveModel/Dirty.html] * Adding +errors+ support to your object Provides the error messages to allow your object 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, "can not be nil") if name == nil end def ErrorsPerson.human_attribute_name(attr, options = {}) "Name" end end ... gives you... person.errors.full_messages # => ["Name Can not be nil"] person.errors.full_messages # => ["Name Can not be nil"] {Learn more}[link:classes/ActiveModel/Errors.html] * Testing the compliance of your object Use ActiveModel::Lint to test the compliance of your object to the basic ActiveModel API... {Learn more}[link:classes/ActiveModel/Lint/Tests.html] * Providing a human face to your object ActiveModel::Naming provides your model with the model_name convention and a human_name attribute... class NamedPerson extend ActiveModel::Naming end ...gives you... NamedPerson.model_name #=> "NamedPerson" NamedPerson.model_name.human #=> "Named person" {Learn more}[link:classes/ActiveModel/Naming.html] * Adding observer support to your objects ActiveModel::Observers allows your object to implement the Observer pattern in a Rails App and take advantage of all the standard observer functions. {Learn more}[link:classes/ActiveModel/Observer.html] * Making your object serializable ActiveModel::Serialization provides a standard interface for your object to provide to_json or to_xml serialization... 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... {Learn more}[link:classes/ActiveModel/Serialization.html] * Integrating with Rail's internationalization (i18n) handling through ActiveModel::Translations... class Person extend ActiveModel::Translation end {Learn more}[link:classes/ActiveModel/Translation.html] * Providing a full Validation stack for your objects... 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 person = Person.new(:first_name => 'zoolander') person.valid? #=> false {Learn more}[link:classes/ActiveModel/Validations.html] * Make custom validators class Person include ActiveModel::Validations validates_with HasNameValidator attr_accessor :name end class HasNameValidator < ActiveModel::Validator def validate(record) record.errors[:name] = "must exist" if record.name.blank? end 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]