activerecord 1.0.0 → 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 (178) hide show
  1. data/CHANGELOG +5518 -76
  2. data/README.rdoc +222 -0
  3. data/examples/performance.rb +162 -0
  4. data/examples/simple.rb +14 -0
  5. data/lib/active_record/aggregations.rb +192 -80
  6. data/lib/active_record/association_preload.rb +403 -0
  7. data/lib/active_record/associations/association_collection.rb +545 -53
  8. data/lib/active_record/associations/association_proxy.rb +295 -0
  9. data/lib/active_record/associations/belongs_to_association.rb +91 -0
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +78 -0
  11. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +127 -36
  12. data/lib/active_record/associations/has_many_association.rb +108 -84
  13. data/lib/active_record/associations/has_many_through_association.rb +116 -0
  14. data/lib/active_record/associations/has_one_association.rb +143 -0
  15. data/lib/active_record/associations/has_one_through_association.rb +40 -0
  16. data/lib/active_record/associations/through_association_scope.rb +154 -0
  17. data/lib/active_record/associations.rb +2086 -368
  18. data/lib/active_record/attribute_methods/before_type_cast.rb +33 -0
  19. data/lib/active_record/attribute_methods/dirty.rb +95 -0
  20. data/lib/active_record/attribute_methods/primary_key.rb +50 -0
  21. data/lib/active_record/attribute_methods/query.rb +39 -0
  22. data/lib/active_record/attribute_methods/read.rb +116 -0
  23. data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -0
  24. data/lib/active_record/attribute_methods/write.rb +37 -0
  25. data/lib/active_record/attribute_methods.rb +60 -0
  26. data/lib/active_record/autosave_association.rb +369 -0
  27. data/lib/active_record/base.rb +1603 -721
  28. data/lib/active_record/callbacks.rb +176 -225
  29. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +365 -0
  30. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +113 -0
  31. data/lib/active_record/connection_adapters/abstract/database_limits.rb +57 -0
  32. data/lib/active_record/connection_adapters/abstract/database_statements.rb +329 -0
  33. data/lib/active_record/connection_adapters/abstract/query_cache.rb +81 -0
  34. data/lib/active_record/connection_adapters/abstract/quoting.rb +72 -0
  35. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +739 -0
  36. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +543 -0
  37. data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -279
  38. data/lib/active_record/connection_adapters/mysql_adapter.rb +594 -82
  39. data/lib/active_record/connection_adapters/postgresql_adapter.rb +988 -135
  40. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +53 -0
  41. data/lib/active_record/connection_adapters/sqlite_adapter.rb +365 -71
  42. data/lib/active_record/counter_cache.rb +115 -0
  43. data/lib/active_record/dynamic_finder_match.rb +53 -0
  44. data/lib/active_record/dynamic_scope_match.rb +32 -0
  45. data/lib/active_record/errors.rb +172 -0
  46. data/lib/active_record/fixtures.rb +941 -105
  47. data/lib/active_record/locale/en.yml +40 -0
  48. data/lib/active_record/locking/optimistic.rb +172 -0
  49. data/lib/active_record/locking/pessimistic.rb +55 -0
  50. data/lib/active_record/log_subscriber.rb +48 -0
  51. data/lib/active_record/migration.rb +617 -0
  52. data/lib/active_record/named_scope.rb +138 -0
  53. data/lib/active_record/nested_attributes.rb +417 -0
  54. data/lib/active_record/observer.rb +105 -36
  55. data/lib/active_record/persistence.rb +291 -0
  56. data/lib/active_record/query_cache.rb +36 -0
  57. data/lib/active_record/railtie.rb +91 -0
  58. data/lib/active_record/railties/controller_runtime.rb +38 -0
  59. data/lib/active_record/railties/databases.rake +512 -0
  60. data/lib/active_record/reflection.rb +364 -87
  61. data/lib/active_record/relation/batches.rb +89 -0
  62. data/lib/active_record/relation/calculations.rb +286 -0
  63. data/lib/active_record/relation/finder_methods.rb +355 -0
  64. data/lib/active_record/relation/predicate_builder.rb +41 -0
  65. data/lib/active_record/relation/query_methods.rb +261 -0
  66. data/lib/active_record/relation/spawn_methods.rb +112 -0
  67. data/lib/active_record/relation.rb +393 -0
  68. data/lib/active_record/schema.rb +59 -0
  69. data/lib/active_record/schema_dumper.rb +195 -0
  70. data/lib/active_record/serialization.rb +60 -0
  71. data/lib/active_record/serializers/xml_serializer.rb +244 -0
  72. data/lib/active_record/session_store.rb +340 -0
  73. data/lib/active_record/test_case.rb +67 -0
  74. data/lib/active_record/timestamp.rb +88 -0
  75. data/lib/active_record/transactions.rb +329 -75
  76. data/lib/active_record/validations/associated.rb +48 -0
  77. data/lib/active_record/validations/uniqueness.rb +185 -0
  78. data/lib/active_record/validations.rb +58 -179
  79. data/lib/active_record/version.rb +9 -0
  80. data/lib/active_record.rb +100 -24
  81. data/lib/rails/generators/active_record/migration/migration_generator.rb +25 -0
  82. data/lib/rails/generators/active_record/migration/templates/migration.rb +17 -0
  83. data/lib/rails/generators/active_record/model/model_generator.rb +38 -0
  84. data/lib/rails/generators/active_record/model/templates/migration.rb +16 -0
  85. data/lib/rails/generators/active_record/model/templates/model.rb +5 -0
  86. data/lib/rails/generators/active_record/model/templates/module.rb +5 -0
  87. data/lib/rails/generators/active_record/observer/observer_generator.rb +15 -0
  88. data/lib/rails/generators/active_record/observer/templates/observer.rb +2 -0
  89. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +24 -0
  90. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +16 -0
  91. data/lib/rails/generators/active_record.rb +27 -0
  92. metadata +216 -158
  93. data/README +0 -361
  94. data/RUNNING_UNIT_TESTS +0 -36
  95. data/dev-utils/eval_debugger.rb +0 -9
  96. data/examples/associations.rb +0 -87
  97. data/examples/shared_setup.rb +0 -15
  98. data/examples/validation.rb +0 -88
  99. data/install.rb +0 -60
  100. data/lib/active_record/deprecated_associations.rb +0 -70
  101. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  102. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  103. data/lib/active_record/support/clean_logger.rb +0 -10
  104. data/lib/active_record/support/inflector.rb +0 -70
  105. data/lib/active_record/vendor/mysql.rb +0 -1117
  106. data/lib/active_record/vendor/simple.rb +0 -702
  107. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  108. data/lib/active_record/wrappings.rb +0 -59
  109. data/rakefile +0 -122
  110. data/test/abstract_unit.rb +0 -16
  111. data/test/aggregations_test.rb +0 -34
  112. data/test/all.sh +0 -8
  113. data/test/associations_test.rb +0 -477
  114. data/test/base_test.rb +0 -513
  115. data/test/class_inheritable_attributes_test.rb +0 -33
  116. data/test/connections/native_mysql/connection.rb +0 -24
  117. data/test/connections/native_postgresql/connection.rb +0 -24
  118. data/test/connections/native_sqlite/connection.rb +0 -24
  119. data/test/deprecated_associations_test.rb +0 -336
  120. data/test/finder_test.rb +0 -67
  121. data/test/fixtures/accounts/signals37 +0 -3
  122. data/test/fixtures/accounts/unknown +0 -2
  123. data/test/fixtures/auto_id.rb +0 -4
  124. data/test/fixtures/column_name.rb +0 -3
  125. data/test/fixtures/companies/first_client +0 -6
  126. data/test/fixtures/companies/first_firm +0 -4
  127. data/test/fixtures/companies/second_client +0 -6
  128. data/test/fixtures/company.rb +0 -37
  129. data/test/fixtures/company_in_module.rb +0 -33
  130. data/test/fixtures/course.rb +0 -3
  131. data/test/fixtures/courses/java +0 -2
  132. data/test/fixtures/courses/ruby +0 -2
  133. data/test/fixtures/customer.rb +0 -30
  134. data/test/fixtures/customers/david +0 -6
  135. data/test/fixtures/db_definitions/mysql.sql +0 -96
  136. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  137. data/test/fixtures/db_definitions/postgresql.sql +0 -113
  138. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  139. data/test/fixtures/db_definitions/sqlite.sql +0 -85
  140. data/test/fixtures/db_definitions/sqlite2.sql +0 -4
  141. data/test/fixtures/default.rb +0 -2
  142. data/test/fixtures/developer.rb +0 -8
  143. data/test/fixtures/developers/david +0 -2
  144. data/test/fixtures/developers/jamis +0 -2
  145. data/test/fixtures/developers_projects/david_action_controller +0 -2
  146. data/test/fixtures/developers_projects/david_active_record +0 -2
  147. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  148. data/test/fixtures/entrant.rb +0 -3
  149. data/test/fixtures/entrants/first +0 -3
  150. data/test/fixtures/entrants/second +0 -3
  151. data/test/fixtures/entrants/third +0 -3
  152. data/test/fixtures/fixture_database.sqlite +0 -0
  153. data/test/fixtures/fixture_database_2.sqlite +0 -0
  154. data/test/fixtures/movie.rb +0 -5
  155. data/test/fixtures/movies/first +0 -2
  156. data/test/fixtures/movies/second +0 -2
  157. data/test/fixtures/project.rb +0 -3
  158. data/test/fixtures/projects/action_controller +0 -2
  159. data/test/fixtures/projects/active_record +0 -2
  160. data/test/fixtures/reply.rb +0 -21
  161. data/test/fixtures/subscriber.rb +0 -5
  162. data/test/fixtures/subscribers/first +0 -2
  163. data/test/fixtures/subscribers/second +0 -2
  164. data/test/fixtures/topic.rb +0 -20
  165. data/test/fixtures/topics/first +0 -9
  166. data/test/fixtures/topics/second +0 -8
  167. data/test/fixtures_test.rb +0 -20
  168. data/test/inflector_test.rb +0 -104
  169. data/test/inheritance_test.rb +0 -125
  170. data/test/lifecycle_test.rb +0 -110
  171. data/test/modules_test.rb +0 -21
  172. data/test/multiple_db_test.rb +0 -46
  173. data/test/pk_test.rb +0 -57
  174. data/test/reflection_test.rb +0 -78
  175. data/test/thread_safety_test.rb +0 -33
  176. data/test/transactions_test.rb +0 -83
  177. data/test/unconnected_test.rb +0 -24
  178. data/test/validations_test.rb +0 -126
@@ -1,38 +1,54 @@
1
- require 'singleton'
1
+ require 'active_support/core_ext/class/attribute'
2
2
 
3
3
  module ActiveRecord
4
- # Observers can be programmed to react to lifecycle callbacks in another class to implement
5
- # trigger-like behavior outside the original class. This is a great way to reduce the clutter that
6
- # normally comes when the model class is burdened with excess responsibility that doesn't pertain to
7
- # the core and nature of the class. Example:
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:
8
11
  #
9
12
  # class CommentObserver < ActiveRecord::Observer
10
13
  # def after_save(comment)
11
- # NotificationServer.send_email("admin@do.com", "New comment was posted", 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!")
12
27
  # end
13
28
  # end
14
29
  #
15
- # This Observer is triggered when a Comment#save is finished and sends a notification about it to the administrator.
30
+ # This Observer uses logger to log when specific callbacks are triggered.
16
31
  #
17
- # == Observing a class that can't be infered
32
+ # == Observing a class that can't be inferred
18
33
  #
19
34
  # Observers will by default be mapped to the class with which they share a name. So CommentObserver will
20
35
  # be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer
21
- # something else than the class you're interested in observing, you can implement the observed_class class method. Like this:
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):
22
38
  #
23
39
  # class AuditObserver < ActiveRecord::Observer
24
- # def self.observed_class() Account end
40
+ # observe :account
41
+ #
25
42
  # def after_update(account)
26
43
  # AuditTrail.new(account, "UPDATED")
27
44
  # end
28
45
  # end
29
46
  #
30
- # == Observing multiple classes at once
31
- #
32
- # If the audit observer needs to watch more than one kind of object, this can be specified in an array, like this:
47
+ # If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
33
48
  #
34
49
  # class AuditObserver < ActiveRecord::Observer
35
- # def self.observed_class() [ Account, Balance ] end
50
+ # observe :account, :balance
51
+ #
36
52
  # def after_update(record)
37
53
  # AuditTrail.new(record, "UPDATED")
38
54
  # end
@@ -40,32 +56,85 @@ module ActiveRecord
40
56
  #
41
57
  # The AuditObserver will now act on both updates to Account and Balance by treating them both as records.
42
58
  #
59
+ # == Available callback methods
60
+ #
43
61
  # The observer can implement callback methods for each of the methods described in the Callbacks module.
44
- class Observer
45
- include Singleton
46
-
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
+
47
95
  def initialize
48
- [ observed_class ].flatten.each do |klass|
49
- klass.add_observer(self)
50
- klass.send(:define_method, :after_find) unless klass.respond_to?(:after_find)
51
- end
96
+ super
97
+ observed_descendants.each { |klass| add_observer!(klass) }
52
98
  end
53
-
54
- def update(callback_method, object)
55
- send(callback_method, object) if respond_to?(callback_method)
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
56
107
  end
57
-
58
- private
59
- def observed_class
60
- if self.class.respond_to? "observed_class"
61
- self.class.observed_class
62
- else
63
- Object.const_get(infer_observed_class_name)
64
- end
108
+
109
+ protected
110
+
111
+ def observed_descendants
112
+ observed_classes.sum([]) { |klass| klass.descendants }
65
113
  end
66
-
67
- def infer_observed_class_name
68
- self.class.name.scan(/(.*)Observer/)[0][0]
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
69
138
  end
70
139
  end
71
- 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
@@ -0,0 +1,91 @@
1
+ require "active_record"
2
+ require "rails"
3
+ require "active_model/railtie"
4
+
5
+ # For now, action_controller must always be present with
6
+ # rails, so let's make sure that it gets required before
7
+ # here. This is needed for correctly setting up the middleware.
8
+ # In the future, this might become an optional require.
9
+ require "action_controller/railtie"
10
+
11
+ module ActiveRecord
12
+ # = Active Record Railtie
13
+ class Railtie < Rails::Railtie
14
+ config.active_record = ActiveSupport::OrderedOptions.new
15
+
16
+ config.generators.orm :active_record, :migration => true,
17
+ :timestamps => true
18
+
19
+ config.app_middleware.insert_after "::ActionDispatch::Callbacks",
20
+ "ActiveRecord::QueryCache"
21
+
22
+ config.app_middleware.insert_after "::ActionDispatch::Callbacks",
23
+ "ActiveRecord::ConnectionAdapters::ConnectionManagement"
24
+
25
+ rake_tasks do
26
+ load "active_record/railties/databases.rake"
27
+ end
28
+
29
+ # When loading console, force ActiveRecord to be loaded to avoid cross
30
+ # references when loading a constant for the first time.
31
+ console do
32
+ ActiveRecord::Base
33
+ end
34
+
35
+ initializer "active_record.initialize_timezone" do
36
+ ActiveSupport.on_load(:active_record) do
37
+ self.time_zone_aware_attributes = true
38
+ self.default_timezone = :utc
39
+ end
40
+ end
41
+
42
+ initializer "active_record.logger" do
43
+ ActiveSupport.on_load(:active_record) { self.logger ||= ::Rails.logger }
44
+ end
45
+
46
+ initializer "active_record.set_configs" do |app|
47
+ ActiveSupport.on_load(:active_record) do
48
+ app.config.active_record.each do |k,v|
49
+ send "#{k}=", v
50
+ end
51
+ end
52
+ end
53
+
54
+ # This sets the database configuration from Configuration#database_configuration
55
+ # and then establishes the connection.
56
+ initializer "active_record.initialize_database" do |app|
57
+ ActiveSupport.on_load(:active_record) do
58
+ self.configurations = app.config.database_configuration
59
+ establish_connection
60
+ end
61
+ end
62
+
63
+ # Expose database runtime to controller for logging.
64
+ initializer "active_record.log_runtime" do |app|
65
+ require "active_record/railties/controller_runtime"
66
+ ActiveSupport.on_load(:action_controller) do
67
+ include ActiveRecord::Railties::ControllerRuntime
68
+ end
69
+ end
70
+
71
+ initializer "active_record.set_dispatch_hooks", :before => :set_clear_dependencies_hook do |app|
72
+ unless app.config.cache_classes
73
+ ActiveSupport.on_load(:active_record) do
74
+ ActionDispatch::Callbacks.after do
75
+ ActiveRecord::Base.clear_reloadable_connections!
76
+ end
77
+ end
78
+ end
79
+ end
80
+
81
+ config.after_initialize do
82
+ ActiveSupport.on_load(:active_record) do
83
+ instantiate_observers
84
+
85
+ ActionDispatch::Callbacks.to_prepare(:activerecord_instantiate_observers) do
86
+ ActiveRecord::Base.instantiate_observers
87
+ end
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,38 @@
1
+ require 'active_support/core_ext/module/attr_internal'
2
+
3
+ module ActiveRecord
4
+ module Railties
5
+ module ControllerRuntime
6
+ extend ActiveSupport::Concern
7
+
8
+ protected
9
+
10
+ attr_internal :db_runtime
11
+
12
+ def cleanup_view_runtime
13
+ if ActiveRecord::Base.connected?
14
+ db_rt_before_render = ActiveRecord::LogSubscriber.reset_runtime
15
+ runtime = super
16
+ db_rt_after_render = ActiveRecord::LogSubscriber.reset_runtime
17
+ self.db_runtime = db_rt_before_render + db_rt_after_render
18
+ runtime - db_rt_after_render
19
+ else
20
+ super
21
+ end
22
+ end
23
+
24
+ def append_info_to_payload(payload)
25
+ super
26
+ payload[:db_runtime] = db_runtime
27
+ end
28
+
29
+ module ClassMethods
30
+ def log_process_action(payload)
31
+ messages, db_runtime = super, payload[:db_runtime]
32
+ messages << ("ActiveRecord: %.1fms" % db_runtime.to_f) if db_runtime
33
+ messages
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end