README.rdoc 5.5 KB
Newer Older
1
= Active Model - defined interfaces for Rails
2
 
3 4 5 6 7
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.
8
 
9
Active Model is a solution for this problem.
10
 
11
Active Model provides a known set of interfaces that your objects can implement
12 13
to then present a common interface to the Action Pack helpers.  You can include
functionality from the following modules:
14

15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
* 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]
  
M
Mikel Lindsaar 已提交
36 37
* Adding callbacks to your objects

38
    class Person
M
Mikel Lindsaar 已提交
39 40 41 42 43 44 45 46 47 48 49 50
      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.
51 52
   
  {Learn more}[link:classes/ActiveModel/CallBacks.html]
53

54 55
* For classes that already look like an Active Record object

56
    class Person
57 58 59 60 61
      include ActiveModel::Conversion
    end
    
    ...returns the class itself when sent :to_model

M
Mikel Lindsaar 已提交
62 63
   {Learn more}[link:classes/ActiveModel/Conversion.html]

M
Mikel Lindsaar 已提交
64 65
* Tracking changes in your object

66
    Provides all the value tracking features implemented by ActiveRecord...
M
Mikel Lindsaar 已提交
67
    
68 69
    person = Person.new
    person.name # => nil
M
Mikel Lindsaar 已提交
70 71 72 73
    person.changed? # => false
    person.name = 'bob'
    person.changed? # => true
    person.changed # => ['name']
74
    person.changes # => { 'name' => [nil, 'bob'] }
M
Mikel Lindsaar 已提交
75 76 77
    person.name = 'robert'
    person.save
    person.previous_changes # => {'name' => ['bob, 'robert']}
78
 
79 80
  {Learn more}[link:classes/ActiveModel/Dirty.html]

81 82 83 84 85 86
* Adding +errors+ support to your object

    Provides the error messages to allow your object to interact with Action Pack
    helpers seamlessly...
    
    class Person
87
    
88 89 90
      def initialize
        @errors = ActiveModel::Errors.new(self)
      end
91
    
92 93
      attr_accessor :name
      attr_reader   :errors
94
    
95 96 97
      def validate!
        errors.add(:name, "can not be nil") if name == nil
      end
98
    
99 100 101
      def ErrorsPerson.human_attribute_name(attr, options = {})
        "Name"
      end
102
    
103 104 105 106 107 108 109 110 111 112
    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]
113 114 115

* Testing the compliance of your object

116
    Use ActiveModel::Lint to test the compliance of your object to the
117 118 119
    basic ActiveModel API...
    
  {Learn more}[link:classes/ActiveModel/Lint/Tests.html]
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136

* 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]

137 138 139 140 141 142 143
* 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]
144 145 146 147 148 149 150 151 152 153 154 155

* 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]
M
Mikel Lindsaar 已提交
156

157 158 159 160 161 162 163 164
* Integrating with Rail's internationalization (i18n) handling through
  ActiveModel::Translations...

    class Person
      extend ActiveModel::Translation
    end
  
  {Learn more}[link:classes/ActiveModel/Translation.html]
165 166 167 168 169 170 171 172

* Providing a full Validation stack for your objects...

   class Person
     include ActiveModel::Validations

     attr_accessor :first_name, :last_name

173

174 175 176 177 178
     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

179

180 181
   person = Person.new
   person.first_name = 'zoolander'
182 183 184 185
   person.valid?          #=> false

  {Learn more}[link:classes/ActiveModel/Validations.html]
  
186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206
* 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]