Skip to content
Newer
Older
100644 237 lines (211 sloc) 9.2 KB
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored May 9, 2010
1 module ActiveRecord
d827780 @rizwanreza Adds title and minor changes.
rizwanreza authored Jun 16, 2010
2 # = Active Record Persistence
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored May 8, 2010
3 module Persistence
d827780 @rizwanreza Adds title and minor changes.
rizwanreza authored Jun 16, 2010
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 May 8, 2010
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 Jun 16, 2010
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 May 8, 2010
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 Jun 16, 2010
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 May 8, 2010
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 Jun 16, 2010
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 May 8, 2010
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
105 # Updates a single attribute and saves the record without going through the normal validation procedure.
106 # This is especially useful for boolean flags on existing records. The regular +update_attribute+ method
107 # in Base is replaced with this when the validations module is mixed in, which it is by default.
108 def update_attribute(name, value)
109 send("#{name}=", value)
110 save(:validate => false)
111 end
112
d827780 @rizwanreza Adds title and minor changes.
rizwanreza authored Jun 16, 2010
113 # Updates all the attributes from the passed-in Hash and saves the record.
114 # If the object is invalid, the saving will fail and false will be returned.
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored May 8, 2010
115 def update_attributes(attributes)
116 self.attributes = attributes
117 save
118 end
119
d827780 @rizwanreza Adds title and minor changes.
rizwanreza authored Jun 16, 2010
120 # Updates an object just like Base.update_attributes but calls save! instead
121 # of save so an exception is raised if the record is invalid.
d916c62 @wycats eliminate alias_method_chain from ActiveRecord
wycats authored May 8, 2010
122 def update_attributes!(attributes)
123 self.attributes = attributes
124 save!
125 end
126
127 # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
128 # The increment is performed directly on the underlying attribute, no setter is invoked.
129 # Only makes sense for number-based attributes. Returns +self+.
130 def increment(attribute, by = 1)
131 self[attribute] ||= 0
132 self[attribute] += by
133 self
134 end
135
136 # Wrapper around +increment+ that saves the record. This method differs from
137 # its non-bang version in that it passes through the attribute setter.
138 # Saving is not subjected to validation checks. Returns +true+ if the
139 # record could be saved.
140 def increment!(attribute, by = 1)
141 increment(attribute, by).update_attribute(attribute, self[attribute])
142 end
143
144 # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
145 # The decrement is performed directly on the underlying attribute, no setter is invoked.
146 # Only makes sense for number-based attributes. Returns +self+.
147 def decrement(attribute, by = 1)
148 self[attribute] ||= 0
149 self[attribute] -= by
150 self
151 end
152
153 # Wrapper around +decrement+ that saves the record. This method differs from
154 # its non-bang version in that it passes through the attribute setter.
155 # Saving is not subjected to validation checks. Returns +true+ if the
156 # record could be saved.
157 def decrement!(attribute, by = 1)
158 decrement(attribute, by).update_attribute(attribute, self[attribute])
159 end
160
161 # Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
162 # if the predicate returns +true+ the attribute will become +false+. This
163 # method toggles directly the underlying value without calling any setter.
164 # Returns +self+.
165 def toggle(attribute)
166 self[attribute] = !send("#{attribute}?")
167 self
168 end
169
170 # Wrapper around +toggle+ that saves the record. This method differs from
171 # its non-bang version in that it passes through the attribute setter.
172 # Saving is not subjected to validation checks. Returns +true+ if the
173 # record could be saved.
174 def toggle!(attribute)
175 toggle(attribute).update_attribute(attribute, self[attribute])
176 end
177
178 # Reloads the attributes of this object from the database.
179 # The optional options argument is passed to find when reloading so you
180 # may do e.g. record.reload(:lock => true) to reload the same record with
181 # an exclusive row lock.
182 def reload(options = nil)
183 clear_aggregation_cache
184 clear_association_cache
185 @attributes.update(self.class.send(:with_exclusive_scope) { self.class.find(self.id, options) }.instance_variable_get('@attributes'))
186 @attributes_cache = {}
187 self
188 end
189
190 private
191 def create_or_update
192 raise ReadOnlyRecord if readonly?
193 result = new_record? ? create : update
194 result != false
195 end
196
197 # Updates the associated record with values matching those of the instance attributes.
198 # Returns the number of affected rows.
199 def update(attribute_names = @attributes.keys)
200 attributes_with_values = arel_attributes_values(false, false, attribute_names)
201 return 0 if attributes_with_values.empty?
202 self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.update(attributes_with_values)
203 end
204
205 # Creates a record with values matching those of the instance attributes
206 # and returns its id.
207 def create
208 if self.id.nil? && connection.prefetch_primary_key?(self.class.table_name)
209 self.id = connection.next_sequence_value(self.class.sequence_name)
210 end
211
212 attributes_values = arel_attributes_values
213
214 new_id = if attributes_values.empty?
215 self.class.unscoped.insert connection.empty_insert_statement_value
216 else
217 self.class.unscoped.insert attributes_values
218 end
219
220 self.id ||= new_id
221
222 @new_record = false
223 id
224 end
225
226 # Initializes the attributes array with keys matching the columns from the linked table and
227 # the values matching the corresponding default value of that column, so
228 # that a new instance, or one populated from a passed-in Hash, still has all the attributes
229 # that instances loaded from the database would.
230 def attributes_from_column_definition
231 self.class.columns.inject({}) do |attributes, column|
232 attributes[column.name] = column.default unless column.name == self.class.primary_key
233 attributes
234 end
235 end
236 end
237 end
Something went wrong with that request. Please try again.