Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 282 lines (251 sloc) 11.36 kB
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
1 module ActiveRecord
d827780 @rizwanreza Adds title and minor changes.
rizwanreza authored
2 # = Active Record Persistence
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
3 module Persistence
d827780 @rizwanreza Adds title and minor changes.
rizwanreza authored
4 # Returns true if this object hasn't been saved yet -- that is, a record
5 # for the object doesn't exist in the data store yet; otherwise, returns false.
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
6 def new_record?
7 @new_record
8 end
9
10 # Returns true if this object has been destroyed, otherwise returns false.
11 def destroyed?
12 @destroyed
13 end
14
d827780 @rizwanreza Adds title and minor changes.
rizwanreza authored
15 # Returns if the record is persisted, i.e. it's not a new record and it was
16 # not destroyed.
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
17 def persisted?
18 !(new_record? || destroyed?)
19 end
20
21 # :call-seq:
22 # save(options)
23 #
24 # Saves the model.
25 #
26 # If the model is new a record gets created in the database, otherwise
27 # the existing record gets updated.
28 #
29 # By default, save always run validations. If any of them fail the action
30 # is cancelled and +save+ returns +false+. However, if you supply
31 # :validate => false, validations are bypassed altogether. See
32 # ActiveRecord::Validations for more information.
33 #
34 # There's a series of callbacks associated with +save+. If any of the
35 # <tt>before_*</tt> callbacks return +false+ the action is cancelled and
36 # +save+ returns +false+. See ActiveRecord::Callbacks for further
37 # details.
38 def save(*)
39 create_or_update
40 end
41
42 # Saves the model.
43 #
44 # If the model is new a record gets created in the database, otherwise
45 # the existing record gets updated.
46 #
47 # With <tt>save!</tt> validations always run. If any of them fail
48 # ActiveRecord::RecordInvalid gets raised. See ActiveRecord::Validations
49 # for more information.
50 #
51 # There's a series of callbacks associated with <tt>save!</tt>. If any of
52 # the <tt>before_*</tt> callbacks return +false+ the action is cancelled
53 # and <tt>save!</tt> raises ActiveRecord::RecordNotSaved. See
54 # ActiveRecord::Callbacks for further details.
55 def save!(*)
56 create_or_update || raise(RecordNotSaved)
57 end
58
59 # Deletes the record in the database and freezes this instance to
60 # reflect that no changes should be made (since they can't be
61 # persisted). Returns the frozen instance.
62 #
63 # The row is simply removed with a SQL +DELETE+ statement on the
64 # record's primary key, and no callbacks are executed.
65 #
66 # To enforce the object's +before_destroy+ and +after_destroy+
67 # callbacks, Observer methods, or any <tt>:dependent</tt> association
68 # options, use <tt>#destroy</tt>.
69 def delete
70 self.class.delete(id) if persisted?
71 @destroyed = true
72 freeze
73 end
74
d827780 @rizwanreza Adds title and minor changes.
rizwanreza authored
75 # Deletes the record in the database and freezes this instance to reflect
76 # that no changes should be made (since they can't be persisted).
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
77 def destroy
78 if persisted?
79 self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).delete_all
80 end
81
82 @destroyed = true
83 freeze
84 end
85
d827780 @rizwanreza Adds title and minor changes.
rizwanreza authored
86 # Returns an instance of the specified +klass+ with the attributes of the
87 # current record. This is mostly useful in relation to single-table
88 # inheritance structures where you want a subclass to appear as the
89 # superclass. This can be used along with record identification in
90 # Action Pack to allow, say, <tt>Client < Company</tt> to do something
91 # like render <tt>:partial => @client.becomes(Company)</tt> to render that
92 # instance using the companies/company partial instead of clients/client.
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
93 #
94 # Note: The new instance will share a link to the same attributes as the original class. So any change to the attributes in either
95 # instance will affect the other.
96 def becomes(klass)
97 became = klass.new
98 became.instance_variable_set("@attributes", @attributes)
99 became.instance_variable_set("@attributes_cache", @attributes_cache)
100 became.instance_variable_set("@new_record", new_record?)
101 became.instance_variable_set("@destroyed", destroyed?)
102 became
103 end
104
5c13793 @neerajdotname expanded comment for update_attribute method
neerajdotname authored
105 # Updates a single attribute and saves the record.
106 # This is especially useful for boolean flags on existing records. Also note that
107 #
51924d4 @neerajdotname more documentation for update_attribute
neerajdotname authored
108 # * The attribute being updated must be a column name.
31f8a0c @neerajdotname adding punctuations
neerajdotname authored
109 # * Validation is skipped.
110 # * No callbacks are invoked.
111 # * updated_at/updated_on column is updated if that column is available.
112 # * Does not work on associations.
51924d4 @neerajdotname more documentation for update_attribute
neerajdotname authored
113 # * Does not work on attr_accessor attributes.
114 # * Does not work on new record. <tt>record.new_record?</tt> should return false for this method to work.
115 # * Updates only the attribute that is input to the method. If there are other changed attributes then
c9fe3c3 @neerajdotname adding comments to update_attribute method
neerajdotname authored
116 # those attributes are left alone. In that case even after this method has done its work <tt>record.changed?</tt>
117 # will return true.
5c13793 @neerajdotname expanded comment for update_attribute method
neerajdotname authored
118 #
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
119 def update_attribute(name, value)
992711a @neerajdotname update_attribute should not update readonly attributes
neerajdotname authored
120 raise ActiveRecordError, "#{name.to_s} is marked as readonly" if self.class.readonly_attributes.include? name.to_s
121
3401311 @josevalim Tidying up a bit, so update_attribute is not called twice on touch.
josevalim authored
122 changes = record_update_timestamps || {}
87f64ef @josevalim Improve a bit the code in latest commits.
josevalim authored
123
3401311 @josevalim Tidying up a bit, so update_attribute is not called twice on touch.
josevalim authored
124 if name
125 name = name.to_s
126 send("#{name}=", value)
127 changes[name] = read_attribute(name)
01629d1 @neerajdotname This patch changes update_attribute implementatino so:
neerajdotname authored
128 end
87f64ef @josevalim Improve a bit the code in latest commits.
josevalim authored
129
3401311 @josevalim Tidying up a bit, so update_attribute is not called twice on touch.
josevalim authored
130 @changed_attributes.except!(*changes.keys)
87f64ef @josevalim Improve a bit the code in latest commits.
josevalim authored
131 primary_key = self.class.primary_key
3401311 @josevalim Tidying up a bit, so update_attribute is not called twice on touch.
josevalim authored
132 self.class.update_all(changes, { primary_key => self[primary_key] }) == 1
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
133 end
134
b7944e1 @fxn revises the rdoc of update_attributes and update_attributes! to docum…
fxn authored
135 # Updates the attributes of the model from the passed-in hash and saves the
136 # record, all wrapped in a transaction. If the object is invalid, the saving
137 # will fail and false will be returned.
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
138 def update_attributes(attributes)
b7944e1 @fxn revises the rdoc of update_attributes and update_attributes! to docum…
fxn authored
139 # The following transaction covers any possible database side-effects of the
140 # attributes assignment. For example, setting the IDs of a child collection.
f4fbc2c @neerajdotname update_attributes and update_attributes! are now wrapped in a transac…
neerajdotname authored
141 with_transaction_returning_status do
142 self.attributes = attributes
143 save
144 end
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
145 end
146
b7944e1 @fxn revises the rdoc of update_attributes and update_attributes! to docum…
fxn authored
147 # Updates its receiver just like +update_attributes+ but calls <tt>save!</tt> instead
148 # of +save+, so an exception is raised if the record is invalid.
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
149 def update_attributes!(attributes)
b7944e1 @fxn revises the rdoc of update_attributes and update_attributes! to docum…
fxn authored
150 # The following transaction covers any possible database side-effects of the
151 # attributes assignment. For example, setting the IDs of a child collection.
f4fbc2c @neerajdotname update_attributes and update_attributes! are now wrapped in a transac…
neerajdotname authored
152 with_transaction_returning_status do
153 self.attributes = attributes
154 save!
155 end
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
156 end
157
158 # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
159 # The increment is performed directly on the underlying attribute, no setter is invoked.
160 # Only makes sense for number-based attributes. Returns +self+.
161 def increment(attribute, by = 1)
162 self[attribute] ||= 0
163 self[attribute] += by
164 self
165 end
166
167 # Wrapper around +increment+ that saves the record. This method differs from
168 # its non-bang version in that it passes through the attribute setter.
169 # Saving is not subjected to validation checks. Returns +true+ if the
170 # record could be saved.
171 def increment!(attribute, by = 1)
172 increment(attribute, by).update_attribute(attribute, self[attribute])
173 end
174
175 # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
176 # The decrement is performed directly on the underlying attribute, no setter is invoked.
177 # Only makes sense for number-based attributes. Returns +self+.
178 def decrement(attribute, by = 1)
179 self[attribute] ||= 0
180 self[attribute] -= by
181 self
182 end
183
184 # Wrapper around +decrement+ that saves the record. This method differs from
185 # its non-bang version in that it passes through the attribute setter.
186 # Saving is not subjected to validation checks. Returns +true+ if the
187 # record could be saved.
188 def decrement!(attribute, by = 1)
189 decrement(attribute, by).update_attribute(attribute, self[attribute])
190 end
191
192 # Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
193 # if the predicate returns +true+ the attribute will become +false+. This
194 # method toggles directly the underlying value without calling any setter.
195 # Returns +self+.
196 def toggle(attribute)
197 self[attribute] = !send("#{attribute}?")
198 self
199 end
200
201 # Wrapper around +toggle+ that saves the record. This method differs from
202 # its non-bang version in that it passes through the attribute setter.
203 # Saving is not subjected to validation checks. Returns +true+ if the
204 # record could be saved.
205 def toggle!(attribute)
206 toggle(attribute).update_attribute(attribute, self[attribute])
207 end
208
209 # Reloads the attributes of this object from the database.
210 # The optional options argument is passed to find when reloading so you
211 # may do e.g. record.reload(:lock => true) to reload the same record with
212 # an exclusive row lock.
213 def reload(options = nil)
214 clear_aggregation_cache
215 clear_association_cache
bd1666a @josevalim Add scoping and unscoped as the syntax to replace the old with_scope …
josevalim authored
216 @attributes.update(self.class.unscoped { self.class.find(self.id, options) }.instance_variable_get('@attributes'))
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
217 @attributes_cache = {}
218 self
219 end
220
b613c3c @josevalim Add an internal (private API) after_touch callback. [#5271 state:reso…
josevalim authored
221 # Saves the record with the updated_at/on attributes set to the current time.
222 # Please note that no validation is performed and no callbacks are executed.
223 # If an attribute name is passed, that attribute is updated along with
224 # updated_at/on attributes.
225 #
226 # Examples:
227 #
228 # product.touch # updates updated_at/on
229 # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
230 def touch(attribute = nil)
231 update_attribute(attribute, current_time_from_proper_timezone)
232 end
233
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored
234 private
235 def create_or_update
236 raise ReadOnlyRecord if readonly?
237 result = new_record? ? create : update
238 result != false
239 end
240
241 # Updates the associated record with values matching those of the instance attributes.
242 # Returns the number of affected rows.
243 def update(attribute_names = @attributes.keys)
244 attributes_with_values = arel_attributes_values(false, false, attribute_names)
245 return 0 if attributes_with_values.empty?
246 self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.update(attributes_with_values)
247 end
248
249 # Creates a record with values matching those of the instance attributes
250 # and returns its id.
251 def create
252 if self.id.nil? && connection.prefetch_primary_key?(self.class.table_name)
253 self.id = connection.next_sequence_value(self.class.sequence_name)
254 end
255
256 attributes_values = arel_attributes_values
257
258 new_id = if attributes_values.empty?
259 self.class.unscoped.insert connection.empty_insert_statement_value
260 else
261 self.class.unscoped.insert attributes_values
262 end
263
264 self.id ||= new_id
265
266 @new_record = false
267 id
268 end
269
270 # Initializes the attributes array with keys matching the columns from the linked table and
271 # the values matching the corresponding default value of that column, so
272 # that a new instance, or one populated from a passed-in Hash, still has all the attributes
273 # that instances loaded from the database would.
274 def attributes_from_column_definition
275 self.class.columns.inject({}) do |attributes, column|
276 attributes[column.name] = column.default unless column.name == self.class.primary_key
277 attributes
278 end
279 end
280 end
01629d1 @neerajdotname This patch changes update_attribute implementatino so:
neerajdotname authored
281 end
Something went wrong with that request. Please try again.