activerecord 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- data/CHANGELOG +6023 -0
- data/README.rdoc +222 -0
- data/examples/associations.png +0 -0
- data/examples/performance.rb +162 -0
- data/examples/simple.rb +14 -0
- data/lib/active_record.rb +124 -0
- data/lib/active_record/aggregations.rb +277 -0
- data/lib/active_record/association_preload.rb +403 -0
- data/lib/active_record/associations.rb +2254 -0
- data/lib/active_record/associations/association_collection.rb +562 -0
- data/lib/active_record/associations/association_proxy.rb +295 -0
- data/lib/active_record/associations/belongs_to_association.rb +91 -0
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +78 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +137 -0
- data/lib/active_record/associations/has_many_association.rb +128 -0
- data/lib/active_record/associations/has_many_through_association.rb +116 -0
- data/lib/active_record/associations/has_one_association.rb +143 -0
- data/lib/active_record/associations/has_one_through_association.rb +40 -0
- data/lib/active_record/associations/through_association_scope.rb +154 -0
- data/lib/active_record/attribute_methods.rb +60 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
- data/lib/active_record/attribute_methods/dirty.rb +95 -0
- data/lib/active_record/attribute_methods/primary_key.rb +50 -0
- data/lib/active_record/attribute_methods/query.rb +39 -0
- data/lib/active_record/attribute_methods/read.rb +116 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -0
- data/lib/active_record/attribute_methods/write.rb +37 -0
- data/lib/active_record/autosave_association.rb +369 -0
- data/lib/active_record/base.rb +1867 -0
- data/lib/active_record/callbacks.rb +288 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +365 -0
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +329 -0
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +543 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +212 -0
- data/lib/active_record/connection_adapters/mysql_adapter.rb +643 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +1030 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -0
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +401 -0
- data/lib/active_record/counter_cache.rb +115 -0
- data/lib/active_record/dynamic_finder_match.rb +53 -0
- data/lib/active_record/dynamic_scope_match.rb +32 -0
- data/lib/active_record/errors.rb +172 -0
- data/lib/active_record/fixtures.rb +1008 -0
- data/lib/active_record/locale/en.yml +40 -0
- data/lib/active_record/locking/optimistic.rb +172 -0
- data/lib/active_record/locking/pessimistic.rb +55 -0
- data/lib/active_record/log_subscriber.rb +48 -0
- data/lib/active_record/migration.rb +617 -0
- data/lib/active_record/named_scope.rb +138 -0
- data/lib/active_record/nested_attributes.rb +417 -0
- data/lib/active_record/observer.rb +140 -0
- data/lib/active_record/persistence.rb +291 -0
- data/lib/active_record/query_cache.rb +36 -0
- data/lib/active_record/railtie.rb +91 -0
- data/lib/active_record/railties/controller_runtime.rb +38 -0
- data/lib/active_record/railties/databases.rake +512 -0
- data/lib/active_record/reflection.rb +403 -0
- data/lib/active_record/relation.rb +393 -0
- data/lib/active_record/relation/batches.rb +89 -0
- data/lib/active_record/relation/calculations.rb +286 -0
- data/lib/active_record/relation/finder_methods.rb +355 -0
- data/lib/active_record/relation/predicate_builder.rb +41 -0
- data/lib/active_record/relation/query_methods.rb +261 -0
- data/lib/active_record/relation/spawn_methods.rb +112 -0
- data/lib/active_record/schema.rb +59 -0
- data/lib/active_record/schema_dumper.rb +195 -0
- data/lib/active_record/serialization.rb +60 -0
- data/lib/active_record/serializers/xml_serializer.rb +244 -0
- data/lib/active_record/session_store.rb +340 -0
- data/lib/active_record/test_case.rb +67 -0
- data/lib/active_record/timestamp.rb +88 -0
- data/lib/active_record/transactions.rb +356 -0
- data/lib/active_record/validations.rb +84 -0
- data/lib/active_record/validations/associated.rb +48 -0
- data/lib/active_record/validations/uniqueness.rb +185 -0
- data/lib/active_record/version.rb +9 -0
- data/lib/rails/generators/active_record.rb +27 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
- data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
- data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
- data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
- data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
- data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
- metadata +224 -0
@@ -0,0 +1,140 @@
|
|
1
|
+
require 'active_support/core_ext/class/attribute'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
# = Active Record Observer
|
5
|
+
#
|
6
|
+
# Observer classes respond to life cycle callbacks to implement trigger-like
|
7
|
+
# behavior outside the original class. This is a great way to reduce the
|
8
|
+
# clutter that normally comes when the model class is burdened with
|
9
|
+
# functionality that doesn't pertain to the core responsibility of the
|
10
|
+
# class. Example:
|
11
|
+
#
|
12
|
+
# class CommentObserver < ActiveRecord::Observer
|
13
|
+
# def after_save(comment)
|
14
|
+
# Notifications.deliver_comment("admin@do.com", "New comment was posted", comment)
|
15
|
+
# end
|
16
|
+
# end
|
17
|
+
#
|
18
|
+
# This Observer sends an email when a Comment#save is finished.
|
19
|
+
#
|
20
|
+
# class ContactObserver < ActiveRecord::Observer
|
21
|
+
# def after_create(contact)
|
22
|
+
# contact.logger.info('New contact added!')
|
23
|
+
# end
|
24
|
+
#
|
25
|
+
# def after_destroy(contact)
|
26
|
+
# contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
#
|
30
|
+
# This Observer uses logger to log when specific callbacks are triggered.
|
31
|
+
#
|
32
|
+
# == Observing a class that can't be inferred
|
33
|
+
#
|
34
|
+
# Observers will by default be mapped to the class with which they share a name. So CommentObserver will
|
35
|
+
# be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer
|
36
|
+
# differently than the class you're interested in observing, you can use the Observer.observe class method which takes
|
37
|
+
# either the concrete class (Product) or a symbol for that class (:product):
|
38
|
+
#
|
39
|
+
# class AuditObserver < ActiveRecord::Observer
|
40
|
+
# observe :account
|
41
|
+
#
|
42
|
+
# def after_update(account)
|
43
|
+
# AuditTrail.new(account, "UPDATED")
|
44
|
+
# end
|
45
|
+
# end
|
46
|
+
#
|
47
|
+
# If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
|
48
|
+
#
|
49
|
+
# class AuditObserver < ActiveRecord::Observer
|
50
|
+
# observe :account, :balance
|
51
|
+
#
|
52
|
+
# def after_update(record)
|
53
|
+
# AuditTrail.new(record, "UPDATED")
|
54
|
+
# end
|
55
|
+
# end
|
56
|
+
#
|
57
|
+
# The AuditObserver will now act on both updates to Account and Balance by treating them both as records.
|
58
|
+
#
|
59
|
+
# == Available callback methods
|
60
|
+
#
|
61
|
+
# The observer can implement callback methods for each of the methods described in the Callbacks module.
|
62
|
+
#
|
63
|
+
# == Storing Observers in Rails
|
64
|
+
#
|
65
|
+
# If you're using Active Record within Rails, observer classes are usually stored in app/models with the
|
66
|
+
# naming convention of app/models/audit_observer.rb.
|
67
|
+
#
|
68
|
+
# == Configuration
|
69
|
+
#
|
70
|
+
# In order to activate an observer, list it in the <tt>config.active_record.observers</tt> configuration
|
71
|
+
# setting in your <tt>config/application.rb</tt> file.
|
72
|
+
#
|
73
|
+
# config.active_record.observers = :comment_observer, :signup_observer
|
74
|
+
#
|
75
|
+
# Observers will not be invoked unless you define these in your application configuration.
|
76
|
+
#
|
77
|
+
# == Loading
|
78
|
+
#
|
79
|
+
# Observers register themselves in the model class they observe, since it is the class that
|
80
|
+
# notifies them of events when they occur. As a side-effect, when an observer is loaded its
|
81
|
+
# corresponding model class is loaded.
|
82
|
+
#
|
83
|
+
# Up to (and including) Rails 2.0.2 observers were instantiated between plugins and
|
84
|
+
# application initializers. Now observers are loaded after application initializers,
|
85
|
+
# so observed models can make use of extensions.
|
86
|
+
#
|
87
|
+
# If by any chance you are using observed models in the initialization you can still
|
88
|
+
# load their observers by calling <tt>ModelObserver.instance</tt> before. Observers are
|
89
|
+
# singletons and that call instantiates and registers them.
|
90
|
+
#
|
91
|
+
class Observer < ActiveModel::Observer
|
92
|
+
class_attribute :observed_methods
|
93
|
+
self.observed_methods = [].freeze
|
94
|
+
|
95
|
+
def initialize
|
96
|
+
super
|
97
|
+
observed_descendants.each { |klass| add_observer!(klass) }
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.method_added(method)
|
101
|
+
method = method.to_sym
|
102
|
+
|
103
|
+
if ActiveRecord::Callbacks::CALLBACKS.include?(method)
|
104
|
+
self.observed_methods += [method]
|
105
|
+
self.observed_methods.freeze
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
protected
|
110
|
+
|
111
|
+
def observed_descendants
|
112
|
+
observed_classes.sum([]) { |klass| klass.descendants }
|
113
|
+
end
|
114
|
+
|
115
|
+
def observe_callbacks?
|
116
|
+
self.class.observed_methods.any?
|
117
|
+
end
|
118
|
+
|
119
|
+
def add_observer!(klass)
|
120
|
+
super
|
121
|
+
define_callbacks klass if observe_callbacks?
|
122
|
+
end
|
123
|
+
|
124
|
+
def define_callbacks(klass)
|
125
|
+
existing_methods = klass.instance_methods.map { |m| m.to_sym }
|
126
|
+
observer = self
|
127
|
+
observer_name = observer.class.name.underscore.gsub('/', '__')
|
128
|
+
|
129
|
+
self.class.observed_methods.each do |method|
|
130
|
+
callback = :"_notify_#{observer_name}_for_#{method}"
|
131
|
+
unless existing_methods.include? callback
|
132
|
+
klass.send(:define_method, callback) do # def _notify_user_observer_for_before_save
|
133
|
+
observer.update(method, self) # observer.update(:before_save, self)
|
134
|
+
end # end
|
135
|
+
klass.send(method, callback) # before_save :_notify_user_observer_for_before_save
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
@@ -0,0 +1,291 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
# = Active Record Persistence
|
3
|
+
module Persistence
|
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.
|
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
|
+
|
15
|
+
# Returns if the record is persisted, i.e. it's not a new record and it was
|
16
|
+
# not destroyed.
|
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 an 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
|
+
|
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).
|
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
|
+
|
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.
|
93
|
+
#
|
94
|
+
# Note: The new instance will share a link to the same attributes as the original class.
|
95
|
+
# So any change to the attributes in either 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.
|
106
|
+
# This is especially useful for boolean flags on existing records. Also note that
|
107
|
+
#
|
108
|
+
# * Validation is skipped.
|
109
|
+
# * Callbacks are invoked.
|
110
|
+
# * updated_at/updated_on column is updated if that column is available.
|
111
|
+
# * Updates all the attributes that are dirty in this object.
|
112
|
+
#
|
113
|
+
def update_attribute(name, value)
|
114
|
+
name = name.to_s
|
115
|
+
raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
|
116
|
+
send("#{name}=", value)
|
117
|
+
save(:validate => false)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Updates the attributes of the model from the passed-in hash and saves the
|
121
|
+
# record, all wrapped in a transaction. If the object is invalid, the saving
|
122
|
+
# will fail and false will be returned.
|
123
|
+
def update_attributes(attributes)
|
124
|
+
# The following transaction covers any possible database side-effects of the
|
125
|
+
# attributes assignment. For example, setting the IDs of a child collection.
|
126
|
+
with_transaction_returning_status do
|
127
|
+
self.attributes = attributes
|
128
|
+
save
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Updates its receiver just like +update_attributes+ but calls <tt>save!</tt> instead
|
133
|
+
# of +save+, so an exception is raised if the record is invalid.
|
134
|
+
def update_attributes!(attributes)
|
135
|
+
# The following transaction covers any possible database side-effects of the
|
136
|
+
# attributes assignment. For example, setting the IDs of a child collection.
|
137
|
+
with_transaction_returning_status do
|
138
|
+
self.attributes = attributes
|
139
|
+
save!
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
# Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
|
144
|
+
# The increment is performed directly on the underlying attribute, no setter is invoked.
|
145
|
+
# Only makes sense for number-based attributes. Returns +self+.
|
146
|
+
def increment(attribute, by = 1)
|
147
|
+
self[attribute] ||= 0
|
148
|
+
self[attribute] += by
|
149
|
+
self
|
150
|
+
end
|
151
|
+
|
152
|
+
# Wrapper around +increment+ that saves the record. This method differs from
|
153
|
+
# its non-bang version in that it passes through the attribute setter.
|
154
|
+
# Saving is not subjected to validation checks. Returns +true+ if the
|
155
|
+
# record could be saved.
|
156
|
+
def increment!(attribute, by = 1)
|
157
|
+
increment(attribute, by).update_attribute(attribute, self[attribute])
|
158
|
+
end
|
159
|
+
|
160
|
+
# Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
|
161
|
+
# The decrement is performed directly on the underlying attribute, no setter is invoked.
|
162
|
+
# Only makes sense for number-based attributes. Returns +self+.
|
163
|
+
def decrement(attribute, by = 1)
|
164
|
+
self[attribute] ||= 0
|
165
|
+
self[attribute] -= by
|
166
|
+
self
|
167
|
+
end
|
168
|
+
|
169
|
+
# Wrapper around +decrement+ that saves the record. This method differs from
|
170
|
+
# its non-bang version in that it passes through the attribute setter.
|
171
|
+
# Saving is not subjected to validation checks. Returns +true+ if the
|
172
|
+
# record could be saved.
|
173
|
+
def decrement!(attribute, by = 1)
|
174
|
+
decrement(attribute, by).update_attribute(attribute, self[attribute])
|
175
|
+
end
|
176
|
+
|
177
|
+
# Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
|
178
|
+
# if the predicate returns +true+ the attribute will become +false+. This
|
179
|
+
# method toggles directly the underlying value without calling any setter.
|
180
|
+
# Returns +self+.
|
181
|
+
def toggle(attribute)
|
182
|
+
self[attribute] = !send("#{attribute}?")
|
183
|
+
self
|
184
|
+
end
|
185
|
+
|
186
|
+
# Wrapper around +toggle+ that saves the record. This method differs from
|
187
|
+
# its non-bang version in that it passes through the attribute setter.
|
188
|
+
# Saving is not subjected to validation checks. Returns +true+ if the
|
189
|
+
# record could be saved.
|
190
|
+
def toggle!(attribute)
|
191
|
+
toggle(attribute).update_attribute(attribute, self[attribute])
|
192
|
+
end
|
193
|
+
|
194
|
+
# Reloads the attributes of this object from the database.
|
195
|
+
# The optional options argument is passed to find when reloading so you
|
196
|
+
# may do e.g. record.reload(:lock => true) to reload the same record with
|
197
|
+
# an exclusive row lock.
|
198
|
+
def reload(options = nil)
|
199
|
+
clear_aggregation_cache
|
200
|
+
clear_association_cache
|
201
|
+
@attributes.update(self.class.unscoped { self.class.find(self.id, options) }.instance_variable_get('@attributes'))
|
202
|
+
@attributes_cache = {}
|
203
|
+
self
|
204
|
+
end
|
205
|
+
|
206
|
+
# Saves the record with the updated_at/on attributes set to the current time.
|
207
|
+
# Please note that no validation is performed and no callbacks are executed.
|
208
|
+
# If an attribute name is passed, that attribute is updated along with
|
209
|
+
# updated_at/on attributes.
|
210
|
+
#
|
211
|
+
# product.touch # updates updated_at/on
|
212
|
+
# product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
|
213
|
+
#
|
214
|
+
# If used along with +belongs_to+ then +touch+ will invoke +touch+ method on associated object.
|
215
|
+
#
|
216
|
+
# class Brake < ActiveRecord::Base
|
217
|
+
# belongs_to :car, :touch => true
|
218
|
+
# end
|
219
|
+
#
|
220
|
+
# class Car < ActiveRecord::Base
|
221
|
+
# belongs_to :corporation, :touch => true
|
222
|
+
# end
|
223
|
+
#
|
224
|
+
# # triggers @brake.car.touch and @brake.car.corporation.touch
|
225
|
+
# @brake.touch
|
226
|
+
def touch(name = nil)
|
227
|
+
attributes = timestamp_attributes_for_update_in_model
|
228
|
+
unless attributes.blank?
|
229
|
+
attributes << name if name
|
230
|
+
|
231
|
+
current_time = current_time_from_proper_timezone
|
232
|
+
changes = {}
|
233
|
+
|
234
|
+
attributes.each do |column|
|
235
|
+
changes[column.to_s] = write_attribute(column.to_s, current_time)
|
236
|
+
end
|
237
|
+
|
238
|
+
@changed_attributes.except!(*changes.keys)
|
239
|
+
primary_key = self.class.primary_key
|
240
|
+
self.class.update_all(changes, { primary_key => self[primary_key] }) == 1
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
private
|
245
|
+
def create_or_update
|
246
|
+
raise ReadOnlyRecord if readonly?
|
247
|
+
result = new_record? ? create : update
|
248
|
+
result != false
|
249
|
+
end
|
250
|
+
|
251
|
+
# Updates the associated record with values matching those of the instance attributes.
|
252
|
+
# Returns the number of affected rows.
|
253
|
+
def update(attribute_names = @attributes.keys)
|
254
|
+
attributes_with_values = arel_attributes_values(false, false, attribute_names)
|
255
|
+
return 0 if attributes_with_values.empty?
|
256
|
+
self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.update(attributes_with_values)
|
257
|
+
end
|
258
|
+
|
259
|
+
# Creates a record with values matching those of the instance attributes
|
260
|
+
# and returns its id.
|
261
|
+
def create
|
262
|
+
if self.id.nil? && connection.prefetch_primary_key?(self.class.table_name)
|
263
|
+
self.id = connection.next_sequence_value(self.class.sequence_name)
|
264
|
+
end
|
265
|
+
|
266
|
+
attributes_values = arel_attributes_values
|
267
|
+
|
268
|
+
new_id = if attributes_values.empty?
|
269
|
+
self.class.unscoped.insert connection.empty_insert_statement_value
|
270
|
+
else
|
271
|
+
self.class.unscoped.insert attributes_values
|
272
|
+
end
|
273
|
+
|
274
|
+
self.id ||= new_id
|
275
|
+
|
276
|
+
@new_record = false
|
277
|
+
id
|
278
|
+
end
|
279
|
+
|
280
|
+
# Initializes the attributes array with keys matching the columns from the linked table and
|
281
|
+
# the values matching the corresponding default value of that column, so
|
282
|
+
# that a new instance, or one populated from a passed-in Hash, still has all the attributes
|
283
|
+
# that instances loaded from the database would.
|
284
|
+
def attributes_from_column_definition
|
285
|
+
self.class.columns.inject({}) do |attributes, column|
|
286
|
+
attributes[column.name] = column.default unless column.name == self.class.primary_key
|
287
|
+
attributes
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
2
|
+
|
3
|
+
module ActiveRecord
|
4
|
+
# = Active Record Query Cache
|
5
|
+
class QueryCache
|
6
|
+
module ClassMethods
|
7
|
+
# Enable the query cache within the block if Active Record is configured.
|
8
|
+
def cache(&block)
|
9
|
+
if ActiveRecord::Base.configurations.blank?
|
10
|
+
yield
|
11
|
+
else
|
12
|
+
connection.cache(&block)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Disable the query cache within the block if Active Record is configured.
|
17
|
+
def uncached(&block)
|
18
|
+
if ActiveRecord::Base.configurations.blank?
|
19
|
+
yield
|
20
|
+
else
|
21
|
+
connection.uncached(&block)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def initialize(app)
|
27
|
+
@app = app
|
28
|
+
end
|
29
|
+
|
30
|
+
def call(env)
|
31
|
+
ActiveRecord::Base.cache do
|
32
|
+
@app.call(env)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|