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.

Files changed (93) hide show
  1. data/CHANGELOG +6023 -0
  2. data/README.rdoc +222 -0
  3. data/examples/associations.png +0 -0
  4. data/examples/performance.rb +162 -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 +403 -0
  9. data/lib/active_record/associations.rb +2254 -0
  10. data/lib/active_record/associations/association_collection.rb +562 -0
  11. data/lib/active_record/associations/association_proxy.rb +295 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +78 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +137 -0
  15. data/lib/active_record/associations/has_many_association.rb +128 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +116 -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 +33 -0
  22. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  23. data/lib/active_record/attribute_methods/primary_key.rb +50 -0
  24. data/lib/active_record/attribute_methods/query.rb +39 -0
  25. data/lib/active_record/attribute_methods/read.rb +116 -0
  26. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -0
  27. data/lib/active_record/attribute_methods/write.rb +37 -0
  28. data/lib/active_record/autosave_association.rb +369 -0
  29. data/lib/active_record/base.rb +1867 -0
  30. data/lib/active_record/callbacks.rb +288 -0
  31. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +365 -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 +329 -0
  35. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  36. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -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 +543 -0
  39. data/lib/active_record/connection_adapters/abstract_adapter.rb +212 -0
  40. data/lib/active_record/connection_adapters/mysql_adapter.rb +643 -0
  41. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1030 -0
  42. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -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 +53 -0
  46. data/lib/active_record/dynamic_scope_match.rb +32 -0
  47. data/lib/active_record/errors.rb +172 -0
  48. data/lib/active_record/fixtures.rb +1008 -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 +417 -0
  56. data/lib/active_record/observer.rb +140 -0
  57. data/lib/active_record/persistence.rb +291 -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 +403 -0
  63. data/lib/active_record/relation.rb +393 -0
  64. data/lib/active_record/relation/batches.rb +89 -0
  65. data/lib/active_record/relation/calculations.rb +286 -0
  66. data/lib/active_record/relation/finder_methods.rb +355 -0
  67. data/lib/active_record/relation/predicate_builder.rb +41 -0
  68. data/lib/active_record/relation/query_methods.rb +261 -0
  69. data/lib/active_record/relation/spawn_methods.rb +112 -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 +356 -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 +185 -0
  81. data/lib/active_record/version.rb +9 -0
  82. data/lib/rails/generators/active_record.rb +27 -0
  83. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  84. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  85. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  86. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  87. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  88. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  89. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  90. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  91. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  92. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  93. 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