square-activerecord 3.0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (94) hide show
  1. data/CHANGELOG +6140 -0
  2. data/README.rdoc +222 -0
  3. data/examples/associations.png +0 -0
  4. data/examples/performance.rb +179 -0
  5. data/examples/simple.rb +14 -0
  6. data/lib/active_record.rb +124 -0
  7. data/lib/active_record/aggregations.rb +277 -0
  8. data/lib/active_record/association_preload.rb +430 -0
  9. data/lib/active_record/associations.rb +2307 -0
  10. data/lib/active_record/associations/association_collection.rb +572 -0
  11. data/lib/active_record/associations/association_proxy.rb +299 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +82 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +128 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +115 -0
  17. data/lib/active_record/associations/has_one_association.rb +143 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +40 -0
  19. data/lib/active_record/associations/through_association_scope.rb +154 -0
  20. data/lib/active_record/attribute_methods.rb +60 -0
  21. data/lib/active_record/attribute_methods/before_type_cast.rb +30 -0
  22. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  23. data/lib/active_record/attribute_methods/primary_key.rb +56 -0
  24. data/lib/active_record/attribute_methods/query.rb +39 -0
  25. data/lib/active_record/attribute_methods/read.rb +145 -0
  26. data/lib/active_record/attribute_methods/time_zone_conversion.rb +64 -0
  27. data/lib/active_record/attribute_methods/write.rb +43 -0
  28. data/lib/active_record/autosave_association.rb +369 -0
  29. data/lib/active_record/base.rb +1904 -0
  30. data/lib/active_record/callbacks.rb +284 -0
  31. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +364 -0
  32. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
  33. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  34. data/lib/active_record/connection_adapters/abstract/database_statements.rb +333 -0
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +73 -0
  37. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
  38. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +539 -0
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +217 -0
  40. data/lib/active_record/connection_adapters/mysql_adapter.rb +657 -0
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1031 -0
  42. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -0
  43. data/lib/active_record/connection_adapters/sqlite_adapter.rb +401 -0
  44. data/lib/active_record/counter_cache.rb +115 -0
  45. data/lib/active_record/dynamic_finder_match.rb +56 -0
  46. data/lib/active_record/dynamic_scope_match.rb +23 -0
  47. data/lib/active_record/errors.rb +172 -0
  48. data/lib/active_record/fixtures.rb +1006 -0
  49. data/lib/active_record/locale/en.yml +40 -0
  50. data/lib/active_record/locking/optimistic.rb +172 -0
  51. data/lib/active_record/locking/pessimistic.rb +55 -0
  52. data/lib/active_record/log_subscriber.rb +48 -0
  53. data/lib/active_record/migration.rb +617 -0
  54. data/lib/active_record/named_scope.rb +138 -0
  55. data/lib/active_record/nested_attributes.rb +419 -0
  56. data/lib/active_record/observer.rb +125 -0
  57. data/lib/active_record/persistence.rb +290 -0
  58. data/lib/active_record/query_cache.rb +36 -0
  59. data/lib/active_record/railtie.rb +91 -0
  60. data/lib/active_record/railties/controller_runtime.rb +38 -0
  61. data/lib/active_record/railties/databases.rake +512 -0
  62. data/lib/active_record/reflection.rb +411 -0
  63. data/lib/active_record/relation.rb +394 -0
  64. data/lib/active_record/relation/batches.rb +89 -0
  65. data/lib/active_record/relation/calculations.rb +295 -0
  66. data/lib/active_record/relation/finder_methods.rb +363 -0
  67. data/lib/active_record/relation/predicate_builder.rb +48 -0
  68. data/lib/active_record/relation/query_methods.rb +303 -0
  69. data/lib/active_record/relation/spawn_methods.rb +132 -0
  70. data/lib/active_record/schema.rb +59 -0
  71. data/lib/active_record/schema_dumper.rb +195 -0
  72. data/lib/active_record/serialization.rb +60 -0
  73. data/lib/active_record/serializers/xml_serializer.rb +244 -0
  74. data/lib/active_record/session_store.rb +340 -0
  75. data/lib/active_record/test_case.rb +67 -0
  76. data/lib/active_record/timestamp.rb +88 -0
  77. data/lib/active_record/transactions.rb +359 -0
  78. data/lib/active_record/validations.rb +84 -0
  79. data/lib/active_record/validations/associated.rb +48 -0
  80. data/lib/active_record/validations/uniqueness.rb +190 -0
  81. data/lib/active_record/version.rb +10 -0
  82. data/lib/rails/generators/active_record.rb +19 -0
  83. data/lib/rails/generators/active_record/migration.rb +15 -0
  84. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  85. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  86. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  87. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  88. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  89. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  90. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  91. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  92. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  93. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  94. metadata +223 -0
@@ -0,0 +1,125 @@
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
+
93
+ def initialize
94
+ super
95
+ observed_descendants.each { |klass| add_observer!(klass) }
96
+ end
97
+
98
+ protected
99
+
100
+ def observed_descendants
101
+ observed_classes.sum([]) { |klass| klass.descendants }
102
+ end
103
+
104
+ def add_observer!(klass)
105
+ super
106
+ define_callbacks klass
107
+ end
108
+
109
+ def define_callbacks(klass)
110
+ observer = self
111
+ observer_name = observer.class.name.underscore.gsub('/', '__')
112
+
113
+ ActiveRecord::Callbacks::CALLBACKS.each do |callback|
114
+ next unless respond_to?(callback)
115
+ callback_meth = :"_notify_#{observer_name}_for_#{callback}"
116
+ unless klass.respond_to?(callback_meth)
117
+ klass.send(:define_method, callback_meth) do
118
+ observer.send(callback, self)
119
+ end
120
+ klass.send(callback, callback_meth)
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,290 @@
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
+ attributes << name if name
229
+ unless attributes.empty?
230
+ current_time = current_time_from_proper_timezone
231
+ changes = {}
232
+
233
+ attributes.each do |column|
234
+ changes[column.to_s] = write_attribute(column.to_s, current_time)
235
+ end
236
+
237
+ @changed_attributes.except!(*changes.keys)
238
+ primary_key = self.class.primary_key
239
+ self.class.update_all(changes, { primary_key => self[primary_key] }) == 1
240
+ end
241
+ end
242
+
243
+ private
244
+ def create_or_update
245
+ raise ReadOnlyRecord if readonly?
246
+ result = new_record? ? create : update
247
+ result != false
248
+ end
249
+
250
+ # Updates the associated record with values matching those of the instance attributes.
251
+ # Returns the number of affected rows.
252
+ def update(attribute_names = @attributes.keys)
253
+ attributes_with_values = arel_attributes_values(false, false, attribute_names)
254
+ return 0 if attributes_with_values.empty?
255
+ self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.update(attributes_with_values)
256
+ end
257
+
258
+ # Creates a record with values matching those of the instance attributes
259
+ # and returns its id.
260
+ def create
261
+ if self.id.nil? && connection.prefetch_primary_key?(self.class.table_name)
262
+ self.id = connection.next_sequence_value(self.class.sequence_name)
263
+ end
264
+
265
+ attributes_values = arel_attributes_values
266
+
267
+ new_id = if attributes_values.empty?
268
+ self.class.unscoped.insert connection.empty_insert_statement_value
269
+ else
270
+ self.class.unscoped.insert attributes_values
271
+ end
272
+
273
+ self.id ||= new_id
274
+
275
+ @new_record = false
276
+ id
277
+ end
278
+
279
+ # Initializes the attributes array with keys matching the columns from the linked table and
280
+ # the values matching the corresponding default value of that column, so
281
+ # that a new instance, or one populated from a passed-in Hash, still has all the attributes
282
+ # that instances loaded from the database would.
283
+ def attributes_from_column_definition
284
+ self.class.columns.inject({}) do |attributes, column|
285
+ attributes[column.name] = column.default unless column.name == self.class.primary_key
286
+ attributes
287
+ end
288
+ end
289
+ end
290
+ 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