aboutsummaryrefslogtreecommitdiffstats
path: root/activemodel/README
blob: 0d6fd1f21c51534598d5bc5a65722bfd8b1e368a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
= 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]
    
 
* Turning your object into a finite State Machine
 
    ActiveModel::StateMachine provides a clean way to include all the methods
    you need to transform your object into a finite State Machine...
 
    light = TrafficLight.new
    light.current_state       #=> :red
    light.change_color!       #=> true
    light.current_state       #=> :green
 
  {Learn more}[link:classes/ActiveModel/StateMachine.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]