activerecord 1.0.0 → 4.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 (255) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +213 -0
  5. data/examples/performance.rb +172 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record/aggregations.rb +180 -84
  8. data/lib/active_record/associations/alias_tracker.rb +76 -0
  9. data/lib/active_record/associations/association.rb +248 -0
  10. data/lib/active_record/associations/association_scope.rb +135 -0
  11. data/lib/active_record/associations/belongs_to_association.rb +92 -0
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +35 -0
  13. data/lib/active_record/associations/builder/association.rb +108 -0
  14. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  15. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  17. data/lib/active_record/associations/builder/has_many.rb +15 -0
  18. data/lib/active_record/associations/builder/has_one.rb +25 -0
  19. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  20. data/lib/active_record/associations/collection_association.rb +608 -0
  21. data/lib/active_record/associations/collection_proxy.rb +986 -0
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +58 -39
  23. data/lib/active_record/associations/has_many_association.rb +116 -85
  24. data/lib/active_record/associations/has_many_through_association.rb +197 -0
  25. data/lib/active_record/associations/has_one_association.rb +102 -0
  26. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  27. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_dependency.rb +235 -0
  31. data/lib/active_record/associations/join_helper.rb +45 -0
  32. data/lib/active_record/associations/preloader/association.rb +121 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  42. data/lib/active_record/associations/preloader.rb +178 -0
  43. data/lib/active_record/associations/singular_association.rb +64 -0
  44. data/lib/active_record/associations/through_association.rb +87 -0
  45. data/lib/active_record/associations.rb +1437 -431
  46. data/lib/active_record/attribute_assignment.rb +201 -0
  47. data/lib/active_record/attribute_methods/before_type_cast.rb +70 -0
  48. data/lib/active_record/attribute_methods/dirty.rb +118 -0
  49. data/lib/active_record/attribute_methods/primary_key.rb +122 -0
  50. data/lib/active_record/attribute_methods/query.rb +40 -0
  51. data/lib/active_record/attribute_methods/read.rb +107 -0
  52. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  53. data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -0
  54. data/lib/active_record/attribute_methods/write.rb +63 -0
  55. data/lib/active_record/attribute_methods.rb +393 -0
  56. data/lib/active_record/autosave_association.rb +426 -0
  57. data/lib/active_record/base.rb +268 -930
  58. data/lib/active_record/callbacks.rb +203 -230
  59. data/lib/active_record/coders/yaml_column.rb +38 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +638 -0
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +390 -0
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +129 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +501 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +873 -0
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +389 -275
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  71. data/lib/active_record/connection_adapters/column.rb +318 -0
  72. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  73. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  74. data/lib/active_record/connection_adapters/mysql_adapter.rb +517 -90
  75. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  76. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  77. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  79. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  80. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  81. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  82. data/lib/active_record/connection_adapters/postgresql_adapter.rb +911 -138
  83. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  84. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +624 -0
  85. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  86. data/lib/active_record/connection_handling.rb +98 -0
  87. data/lib/active_record/core.rb +463 -0
  88. data/lib/active_record/counter_cache.rb +122 -0
  89. data/lib/active_record/dynamic_matchers.rb +131 -0
  90. data/lib/active_record/errors.rb +213 -0
  91. data/lib/active_record/explain.rb +38 -0
  92. data/lib/active_record/explain_registry.rb +30 -0
  93. data/lib/active_record/explain_subscriber.rb +29 -0
  94. data/lib/active_record/fixture_set/file.rb +55 -0
  95. data/lib/active_record/fixtures.rb +892 -138
  96. data/lib/active_record/inheritance.rb +200 -0
  97. data/lib/active_record/integration.rb +60 -0
  98. data/lib/active_record/locale/en.yml +47 -0
  99. data/lib/active_record/locking/optimistic.rb +181 -0
  100. data/lib/active_record/locking/pessimistic.rb +77 -0
  101. data/lib/active_record/log_subscriber.rb +82 -0
  102. data/lib/active_record/migration/command_recorder.rb +164 -0
  103. data/lib/active_record/migration/join_table.rb +15 -0
  104. data/lib/active_record/migration.rb +1015 -0
  105. data/lib/active_record/model_schema.rb +345 -0
  106. data/lib/active_record/nested_attributes.rb +546 -0
  107. data/lib/active_record/null_relation.rb +65 -0
  108. data/lib/active_record/persistence.rb +509 -0
  109. data/lib/active_record/query_cache.rb +56 -0
  110. data/lib/active_record/querying.rb +62 -0
  111. data/lib/active_record/railtie.rb +205 -0
  112. data/lib/active_record/railties/console_sandbox.rb +5 -0
  113. data/lib/active_record/railties/controller_runtime.rb +50 -0
  114. data/lib/active_record/railties/databases.rake +402 -0
  115. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  116. data/lib/active_record/readonly_attributes.rb +30 -0
  117. data/lib/active_record/reflection.rb +544 -87
  118. data/lib/active_record/relation/batches.rb +93 -0
  119. data/lib/active_record/relation/calculations.rb +399 -0
  120. data/lib/active_record/relation/delegation.rb +125 -0
  121. data/lib/active_record/relation/finder_methods.rb +349 -0
  122. data/lib/active_record/relation/merger.rb +161 -0
  123. data/lib/active_record/relation/predicate_builder.rb +106 -0
  124. data/lib/active_record/relation/query_methods.rb +1044 -0
  125. data/lib/active_record/relation/spawn_methods.rb +73 -0
  126. data/lib/active_record/relation.rb +655 -0
  127. data/lib/active_record/result.rb +67 -0
  128. data/lib/active_record/runtime_registry.rb +17 -0
  129. data/lib/active_record/sanitization.rb +168 -0
  130. data/lib/active_record/schema.rb +65 -0
  131. data/lib/active_record/schema_dumper.rb +204 -0
  132. data/lib/active_record/schema_migration.rb +39 -0
  133. data/lib/active_record/scoping/default.rb +146 -0
  134. data/lib/active_record/scoping/named.rb +175 -0
  135. data/lib/active_record/scoping.rb +82 -0
  136. data/lib/active_record/serialization.rb +22 -0
  137. data/lib/active_record/serializers/xml_serializer.rb +197 -0
  138. data/lib/active_record/statement_cache.rb +26 -0
  139. data/lib/active_record/store.rb +156 -0
  140. data/lib/active_record/tasks/database_tasks.rb +203 -0
  141. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  142. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  143. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  144. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  145. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  146. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  147. data/lib/active_record/test_case.rb +96 -0
  148. data/lib/active_record/timestamp.rb +119 -0
  149. data/lib/active_record/transactions.rb +366 -69
  150. data/lib/active_record/translation.rb +22 -0
  151. data/lib/active_record/validations/associated.rb +49 -0
  152. data/lib/active_record/validations/presence.rb +65 -0
  153. data/lib/active_record/validations/uniqueness.rb +225 -0
  154. data/lib/active_record/validations.rb +64 -185
  155. data/lib/active_record/version.rb +11 -0
  156. data/lib/active_record.rb +149 -24
  157. data/lib/rails/generators/active_record/migration/migration_generator.rb +62 -0
  158. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  159. data/lib/rails/generators/active_record/migration/templates/migration.rb +39 -0
  160. data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
  161. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  162. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  163. data/lib/rails/generators/active_record.rb +23 -0
  164. metadata +261 -161
  165. data/CHANGELOG +0 -581
  166. data/README +0 -361
  167. data/RUNNING_UNIT_TESTS +0 -36
  168. data/dev-utils/eval_debugger.rb +0 -9
  169. data/examples/associations.png +0 -0
  170. data/examples/associations.rb +0 -87
  171. data/examples/shared_setup.rb +0 -15
  172. data/examples/validation.rb +0 -88
  173. data/install.rb +0 -60
  174. data/lib/active_record/associations/association_collection.rb +0 -70
  175. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -107
  176. data/lib/active_record/deprecated_associations.rb +0 -70
  177. data/lib/active_record/observer.rb +0 -71
  178. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  179. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  180. data/lib/active_record/support/clean_logger.rb +0 -10
  181. data/lib/active_record/support/inflector.rb +0 -70
  182. data/lib/active_record/vendor/mysql.rb +0 -1117
  183. data/lib/active_record/vendor/simple.rb +0 -702
  184. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  185. data/lib/active_record/wrappings.rb +0 -59
  186. data/rakefile +0 -122
  187. data/test/abstract_unit.rb +0 -16
  188. data/test/aggregations_test.rb +0 -34
  189. data/test/all.sh +0 -8
  190. data/test/associations_test.rb +0 -477
  191. data/test/base_test.rb +0 -513
  192. data/test/class_inheritable_attributes_test.rb +0 -33
  193. data/test/connections/native_mysql/connection.rb +0 -24
  194. data/test/connections/native_postgresql/connection.rb +0 -24
  195. data/test/connections/native_sqlite/connection.rb +0 -24
  196. data/test/deprecated_associations_test.rb +0 -336
  197. data/test/finder_test.rb +0 -67
  198. data/test/fixtures/accounts/signals37 +0 -3
  199. data/test/fixtures/accounts/unknown +0 -2
  200. data/test/fixtures/auto_id.rb +0 -4
  201. data/test/fixtures/column_name.rb +0 -3
  202. data/test/fixtures/companies/first_client +0 -6
  203. data/test/fixtures/companies/first_firm +0 -4
  204. data/test/fixtures/companies/second_client +0 -6
  205. data/test/fixtures/company.rb +0 -37
  206. data/test/fixtures/company_in_module.rb +0 -33
  207. data/test/fixtures/course.rb +0 -3
  208. data/test/fixtures/courses/java +0 -2
  209. data/test/fixtures/courses/ruby +0 -2
  210. data/test/fixtures/customer.rb +0 -30
  211. data/test/fixtures/customers/david +0 -6
  212. data/test/fixtures/db_definitions/mysql.sql +0 -96
  213. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  214. data/test/fixtures/db_definitions/postgresql.sql +0 -113
  215. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  216. data/test/fixtures/db_definitions/sqlite.sql +0 -85
  217. data/test/fixtures/db_definitions/sqlite2.sql +0 -4
  218. data/test/fixtures/default.rb +0 -2
  219. data/test/fixtures/developer.rb +0 -8
  220. data/test/fixtures/developers/david +0 -2
  221. data/test/fixtures/developers/jamis +0 -2
  222. data/test/fixtures/developers_projects/david_action_controller +0 -2
  223. data/test/fixtures/developers_projects/david_active_record +0 -2
  224. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  225. data/test/fixtures/entrant.rb +0 -3
  226. data/test/fixtures/entrants/first +0 -3
  227. data/test/fixtures/entrants/second +0 -3
  228. data/test/fixtures/entrants/third +0 -3
  229. data/test/fixtures/fixture_database.sqlite +0 -0
  230. data/test/fixtures/fixture_database_2.sqlite +0 -0
  231. data/test/fixtures/movie.rb +0 -5
  232. data/test/fixtures/movies/first +0 -2
  233. data/test/fixtures/movies/second +0 -2
  234. data/test/fixtures/project.rb +0 -3
  235. data/test/fixtures/projects/action_controller +0 -2
  236. data/test/fixtures/projects/active_record +0 -2
  237. data/test/fixtures/reply.rb +0 -21
  238. data/test/fixtures/subscriber.rb +0 -5
  239. data/test/fixtures/subscribers/first +0 -2
  240. data/test/fixtures/subscribers/second +0 -2
  241. data/test/fixtures/topic.rb +0 -20
  242. data/test/fixtures/topics/first +0 -9
  243. data/test/fixtures/topics/second +0 -8
  244. data/test/fixtures_test.rb +0 -20
  245. data/test/inflector_test.rb +0 -104
  246. data/test/inheritance_test.rb +0 -125
  247. data/test/lifecycle_test.rb +0 -110
  248. data/test/modules_test.rb +0 -21
  249. data/test/multiple_db_test.rb +0 -46
  250. data/test/pk_test.rb +0 -57
  251. data/test/reflection_test.rb +0 -78
  252. data/test/thread_safety_test.rb +0 -33
  253. data/test/transactions_test.rb +0 -83
  254. data/test/unconnected_test.rb +0 -24
  255. data/test/validations_test.rb +0 -126
@@ -0,0 +1,509 @@
1
+ module ActiveRecord
2
+ # = Active Record Persistence
3
+ module Persistence
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ # Creates an object (or multiple objects) and saves it to the database, if validations pass.
8
+ # The resulting object is returned whether the object was saved successfully to the database or not.
9
+ #
10
+ # The +attributes+ parameter can be either a Hash or an Array of Hashes. These Hashes describe the
11
+ # attributes on the objects that are to be created.
12
+ #
13
+ # +create+ respects mass-assignment security and accepts either +:as+ or +:without_protection+ options
14
+ # in the +options+ parameter.
15
+ #
16
+ # ==== Examples
17
+ # # Create a single new object
18
+ # User.create(first_name: 'Jamie')
19
+ #
20
+ # # Create an Array of new objects
21
+ # User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }])
22
+ #
23
+ # # Create a single object and pass it into a block to set other attributes.
24
+ # User.create(first_name: 'Jamie') do |u|
25
+ # u.is_admin = false
26
+ # end
27
+ #
28
+ # # Creating an Array of new objects using a block, where the block is executed for each object:
29
+ # User.create([{ first_name: 'Jamie' }, { first_name: 'Jeremy' }]) do |u|
30
+ # u.is_admin = false
31
+ # end
32
+ def create(attributes = nil, &block)
33
+ if attributes.is_a?(Array)
34
+ attributes.collect { |attr| create(attr, &block) }
35
+ else
36
+ object = new(attributes, &block)
37
+ object.save
38
+ object
39
+ end
40
+ end
41
+
42
+ # Given an attributes hash, +instantiate+ returns a new instance of
43
+ # the appropriate class.
44
+ #
45
+ # For example, +Post.all+ may return Comments, Messages, and Emails
46
+ # by storing the record's subclass in a +type+ attribute. By calling
47
+ # +instantiate+ instead of +new+, finder methods ensure they get new
48
+ # instances of the appropriate class for each record.
49
+ #
50
+ # See +ActiveRecord::Inheritance#discriminate_class_for_record+ to see
51
+ # how this "single-table" inheritance mapping is implemented.
52
+ def instantiate(record, column_types = {})
53
+ klass = discriminate_class_for_record(record)
54
+ column_types = klass.decorate_columns(column_types.dup)
55
+ klass.allocate.init_with('attributes' => record, 'column_types' => column_types)
56
+ end
57
+
58
+ private
59
+ # Called by +instantiate+ to decide which class to use for a new
60
+ # record instance.
61
+ #
62
+ # See +ActiveRecord::Inheritance#discriminate_class_for_record+ for
63
+ # the single-table inheritance discriminator.
64
+ def discriminate_class_for_record(record)
65
+ self
66
+ end
67
+ end
68
+
69
+ # Returns true if this object hasn't been saved yet -- that is, a record
70
+ # for the object doesn't exist in the data store yet; otherwise, returns false.
71
+ def new_record?
72
+ sync_with_transaction_state
73
+ @new_record
74
+ end
75
+
76
+ # Returns true if this object has been destroyed, otherwise returns false.
77
+ def destroyed?
78
+ sync_with_transaction_state
79
+ @destroyed
80
+ end
81
+
82
+ # Returns true if the record is persisted, i.e. it's not a new record and it was
83
+ # not destroyed, otherwise returns false.
84
+ def persisted?
85
+ !(new_record? || destroyed?)
86
+ end
87
+
88
+ # Saves the model.
89
+ #
90
+ # If the model is new a record gets created in the database, otherwise
91
+ # the existing record gets updated.
92
+ #
93
+ # By default, save always run validations. If any of them fail the action
94
+ # is cancelled and +save+ returns +false+. However, if you supply
95
+ # validate: false, validations are bypassed altogether. See
96
+ # ActiveRecord::Validations for more information.
97
+ #
98
+ # There's a series of callbacks associated with +save+. If any of the
99
+ # <tt>before_*</tt> callbacks return +false+ the action is cancelled and
100
+ # +save+ returns +false+. See ActiveRecord::Callbacks for further
101
+ # details.
102
+ #
103
+ # Attributes marked as readonly are silently ignored if the record is
104
+ # being updated.
105
+ def save(*)
106
+ create_or_update
107
+ rescue ActiveRecord::RecordInvalid
108
+ false
109
+ end
110
+
111
+ # Saves the model.
112
+ #
113
+ # If the model is new a record gets created in the database, otherwise
114
+ # the existing record gets updated.
115
+ #
116
+ # With <tt>save!</tt> validations always run. If any of them fail
117
+ # ActiveRecord::RecordInvalid gets raised. See ActiveRecord::Validations
118
+ # for more information.
119
+ #
120
+ # There's a series of callbacks associated with <tt>save!</tt>. If any of
121
+ # the <tt>before_*</tt> callbacks return +false+ the action is cancelled
122
+ # and <tt>save!</tt> raises ActiveRecord::RecordNotSaved. See
123
+ # ActiveRecord::Callbacks for further details.
124
+ #
125
+ # Attributes marked as readonly are silently ignored if the record is
126
+ # being updated.
127
+ def save!(*)
128
+ create_or_update || raise(RecordNotSaved)
129
+ end
130
+
131
+ # Deletes the record in the database and freezes this instance to
132
+ # reflect that no changes should be made (since they can't be
133
+ # persisted). Returns the frozen instance.
134
+ #
135
+ # The row is simply removed with an SQL +DELETE+ statement on the
136
+ # record's primary key, and no callbacks are executed.
137
+ #
138
+ # To enforce the object's +before_destroy+ and +after_destroy+
139
+ # callbacks or any <tt>:dependent</tt> association
140
+ # options, use <tt>#destroy</tt>.
141
+ def delete
142
+ self.class.delete(id) if persisted?
143
+ @destroyed = true
144
+ freeze
145
+ end
146
+
147
+ # Deletes the record in the database and freezes this instance to reflect
148
+ # that no changes should be made (since they can't be persisted).
149
+ #
150
+ # There's a series of callbacks associated with <tt>destroy</tt>. If
151
+ # the <tt>before_destroy</tt> callback return +false+ the action is cancelled
152
+ # and <tt>destroy</tt> returns +false+. See
153
+ # ActiveRecord::Callbacks for further details.
154
+ def destroy
155
+ raise ReadOnlyRecord if readonly?
156
+ destroy_associations
157
+ destroy_row if persisted?
158
+ @destroyed = true
159
+ freeze
160
+ end
161
+
162
+ # Deletes the record in the database and freezes this instance to reflect
163
+ # that no changes should be made (since they can't be persisted).
164
+ #
165
+ # There's a series of callbacks associated with <tt>destroy!</tt>. If
166
+ # the <tt>before_destroy</tt> callback return +false+ the action is cancelled
167
+ # and <tt>destroy!</tt> raises ActiveRecord::RecordNotDestroyed. See
168
+ # ActiveRecord::Callbacks for further details.
169
+ def destroy!
170
+ destroy || raise(ActiveRecord::RecordNotDestroyed)
171
+ end
172
+
173
+ # Returns an instance of the specified +klass+ with the attributes of the
174
+ # current record. This is mostly useful in relation to single-table
175
+ # inheritance structures where you want a subclass to appear as the
176
+ # superclass. This can be used along with record identification in
177
+ # Action Pack to allow, say, <tt>Client < Company</tt> to do something
178
+ # like render <tt>partial: @client.becomes(Company)</tt> to render that
179
+ # instance using the companies/company partial instead of clients/client.
180
+ #
181
+ # Note: The new instance will share a link to the same attributes as the original class.
182
+ # So any change to the attributes in either instance will affect the other.
183
+ def becomes(klass)
184
+ became = klass.new
185
+ became.instance_variable_set("@attributes", @attributes)
186
+ became.instance_variable_set("@attributes_cache", @attributes_cache)
187
+ became.instance_variable_set("@new_record", new_record?)
188
+ became.instance_variable_set("@destroyed", destroyed?)
189
+ became.instance_variable_set("@errors", errors)
190
+ became
191
+ end
192
+
193
+ # Wrapper around +becomes+ that also changes the instance's sti column value.
194
+ # This is especially useful if you want to persist the changed class in your
195
+ # database.
196
+ #
197
+ # Note: The old instance's sti column value will be changed too, as both objects
198
+ # share the same set of attributes.
199
+ def becomes!(klass)
200
+ became = becomes(klass)
201
+ became.public_send("#{klass.inheritance_column}=", klass.sti_name) unless self.class.descends_from_active_record?
202
+ became
203
+ end
204
+
205
+ # Updates a single attribute and saves the record.
206
+ # This is especially useful for boolean flags on existing records. Also note that
207
+ #
208
+ # * Validation is skipped.
209
+ # * Callbacks are invoked.
210
+ # * updated_at/updated_on column is updated if that column is available.
211
+ # * Updates all the attributes that are dirty in this object.
212
+ #
213
+ # This method raises an +ActiveRecord::ActiveRecordError+ if the
214
+ # attribute is marked as readonly.
215
+ def update_attribute(name, value)
216
+ name = name.to_s
217
+ verify_readonly_attribute(name)
218
+ send("#{name}=", value)
219
+ save(validate: false)
220
+ end
221
+
222
+ # Updates the attributes of the model from the passed-in hash and saves the
223
+ # record, all wrapped in a transaction. If the object is invalid, the saving
224
+ # will fail and false will be returned.
225
+ def update(attributes)
226
+ # The following transaction covers any possible database side-effects of the
227
+ # attributes assignment. For example, setting the IDs of a child collection.
228
+ with_transaction_returning_status do
229
+ assign_attributes(attributes)
230
+ save
231
+ end
232
+ end
233
+
234
+ alias update_attributes update
235
+
236
+ # Updates its receiver just like +update+ but calls <tt>save!</tt> instead
237
+ # of +save+, so an exception is raised if the record is invalid.
238
+ def update!(attributes)
239
+ # The following transaction covers any possible database side-effects of the
240
+ # attributes assignment. For example, setting the IDs of a child collection.
241
+ with_transaction_returning_status do
242
+ assign_attributes(attributes)
243
+ save!
244
+ end
245
+ end
246
+
247
+ alias update_attributes! update!
248
+
249
+ # Equivalent to <code>update_columns(name => value)</code>.
250
+ def update_column(name, value)
251
+ update_columns(name => value)
252
+ end
253
+
254
+ # Updates the attributes directly in the database issuing an UPDATE SQL
255
+ # statement and sets them in the receiver:
256
+ #
257
+ # user.update_columns(last_request_at: Time.current)
258
+ #
259
+ # This is the fastest way to update attributes because it goes straight to
260
+ # the database, but take into account that in consequence the regular update
261
+ # procedures are totally bypassed. In particular:
262
+ #
263
+ # * Validations are skipped.
264
+ # * Callbacks are skipped.
265
+ # * +updated_at+/+updated_on+ are not updated.
266
+ #
267
+ # This method raises an +ActiveRecord::ActiveRecordError+ when called on new
268
+ # objects, or when at least one of the attributes is marked as readonly.
269
+ def update_columns(attributes)
270
+ raise ActiveRecordError, "can not update on a new record object" unless persisted?
271
+
272
+ attributes.each_key do |key|
273
+ verify_readonly_attribute(key.to_s)
274
+ end
275
+
276
+ updated_count = self.class.unscoped.where(self.class.primary_key => id).update_all(attributes)
277
+
278
+ attributes.each do |k, v|
279
+ raw_write_attribute(k, v)
280
+ end
281
+
282
+ updated_count == 1
283
+ end
284
+
285
+ # Initializes +attribute+ to zero if +nil+ and adds the value passed as +by+ (default is 1).
286
+ # The increment is performed directly on the underlying attribute, no setter is invoked.
287
+ # Only makes sense for number-based attributes. Returns +self+.
288
+ def increment(attribute, by = 1)
289
+ self[attribute] ||= 0
290
+ self[attribute] += by
291
+ self
292
+ end
293
+
294
+ # Wrapper around +increment+ that saves the record. This method differs from
295
+ # its non-bang version in that it passes through the attribute setter.
296
+ # Saving is not subjected to validation checks. Returns +true+ if the
297
+ # record could be saved.
298
+ def increment!(attribute, by = 1)
299
+ increment(attribute, by).update_attribute(attribute, self[attribute])
300
+ end
301
+
302
+ # Initializes +attribute+ to zero if +nil+ and subtracts the value passed as +by+ (default is 1).
303
+ # The decrement is performed directly on the underlying attribute, no setter is invoked.
304
+ # Only makes sense for number-based attributes. Returns +self+.
305
+ def decrement(attribute, by = 1)
306
+ self[attribute] ||= 0
307
+ self[attribute] -= by
308
+ self
309
+ end
310
+
311
+ # Wrapper around +decrement+ that saves the record. This method differs from
312
+ # its non-bang version in that it passes through the attribute setter.
313
+ # Saving is not subjected to validation checks. Returns +true+ if the
314
+ # record could be saved.
315
+ def decrement!(attribute, by = 1)
316
+ decrement(attribute, by).update_attribute(attribute, self[attribute])
317
+ end
318
+
319
+ # Assigns to +attribute+ the boolean opposite of <tt>attribute?</tt>. So
320
+ # if the predicate returns +true+ the attribute will become +false+. This
321
+ # method toggles directly the underlying value without calling any setter.
322
+ # Returns +self+.
323
+ def toggle(attribute)
324
+ self[attribute] = !send("#{attribute}?")
325
+ self
326
+ end
327
+
328
+ # Wrapper around +toggle+ that saves the record. This method differs from
329
+ # its non-bang version in that it passes through the attribute setter.
330
+ # Saving is not subjected to validation checks. Returns +true+ if the
331
+ # record could be saved.
332
+ def toggle!(attribute)
333
+ toggle(attribute).update_attribute(attribute, self[attribute])
334
+ end
335
+
336
+ # Reloads the record from the database.
337
+ #
338
+ # This method modifies the receiver in-place. Attributes are updated, and
339
+ # caches busted, in particular the associations cache.
340
+ #
341
+ # If the record no longer exists in the database <tt>ActiveRecord::RecordNotFound</tt>
342
+ # is raised. Otherwise, in addition to the in-place modification the method
343
+ # returns +self+ for convenience.
344
+ #
345
+ # The optional <tt>:lock</tt> flag option allows you to lock the reloaded record:
346
+ #
347
+ # reload(lock: true) # reload with pessimistic locking
348
+ #
349
+ # Reloading is commonly used in test suites to test something is actually
350
+ # written to the database, or when some action modifies the corresponding
351
+ # row in the database but not the object in memory:
352
+ #
353
+ # assert account.deposit!(25)
354
+ # assert_equal 25, account.credit # check it is updated in memory
355
+ # assert_equal 25, account.reload.credit # check it is also persisted
356
+ #
357
+ # Another commom use case is optimistic locking handling:
358
+ #
359
+ # def with_optimistic_retry
360
+ # begin
361
+ # yield
362
+ # rescue ActiveRecord::StaleObjectError
363
+ # begin
364
+ # # Reload lock_version in particular.
365
+ # reload
366
+ # rescue ActiveRecord::RecordNotFound
367
+ # # If the record is gone there is nothing to do.
368
+ # else
369
+ # retry
370
+ # end
371
+ # end
372
+ # end
373
+ #
374
+ def reload(options = nil)
375
+ clear_aggregation_cache
376
+ clear_association_cache
377
+
378
+ fresh_object =
379
+ if options && options[:lock]
380
+ self.class.unscoped { self.class.lock.find(id) }
381
+ else
382
+ self.class.unscoped { self.class.find(id) }
383
+ end
384
+
385
+ @attributes.update(fresh_object.instance_variable_get('@attributes'))
386
+ @columns_hash = fresh_object.instance_variable_get('@columns_hash')
387
+
388
+ @attributes_cache = {}
389
+ self
390
+ end
391
+
392
+ # Saves the record with the updated_at/on attributes set to the current time.
393
+ # Please note that no validation is performed and no callbacks are executed.
394
+ # If an attribute name is passed, that attribute is updated along with
395
+ # updated_at/on attributes.
396
+ #
397
+ # product.touch # updates updated_at/on
398
+ # product.touch(:designed_at) # updates the designed_at attribute and updated_at/on
399
+ #
400
+ # If used along with +belongs_to+ then +touch+ will invoke +touch+ method on associated object.
401
+ #
402
+ # class Brake < ActiveRecord::Base
403
+ # belongs_to :car, touch: true
404
+ # end
405
+ #
406
+ # class Car < ActiveRecord::Base
407
+ # belongs_to :corporation, touch: true
408
+ # end
409
+ #
410
+ # # triggers @brake.car.touch and @brake.car.corporation.touch
411
+ # @brake.touch
412
+ #
413
+ # Note that +touch+ must be used on a persisted object, or else an
414
+ # ActiveRecordError will be thrown. For example:
415
+ #
416
+ # ball = Ball.new
417
+ # ball.touch(:updated_at) # => raises ActiveRecordError
418
+ #
419
+ def touch(name = nil)
420
+ raise ActiveRecordError, "can not touch on a new record object" unless persisted?
421
+
422
+ attributes = timestamp_attributes_for_update_in_model
423
+ attributes << name if name
424
+
425
+ unless attributes.empty?
426
+ current_time = current_time_from_proper_timezone
427
+ changes = {}
428
+
429
+ attributes.each do |column|
430
+ column = column.to_s
431
+ changes[column] = write_attribute(column, current_time)
432
+ end
433
+
434
+ changes[self.class.locking_column] = increment_lock if locking_enabled?
435
+
436
+ @changed_attributes.except!(*changes.keys)
437
+ primary_key = self.class.primary_key
438
+ self.class.unscoped.where(primary_key => self[primary_key]).update_all(changes) == 1
439
+ end
440
+ end
441
+
442
+ private
443
+
444
+ # A hook to be overridden by association modules.
445
+ def destroy_associations
446
+ end
447
+
448
+ def destroy_row
449
+ relation_for_destroy.delete_all
450
+ end
451
+
452
+ def relation_for_destroy
453
+ pk = self.class.primary_key
454
+ column = self.class.columns_hash[pk]
455
+ substitute = self.class.connection.substitute_at(column, 0)
456
+
457
+ relation = self.class.unscoped.where(
458
+ self.class.arel_table[pk].eq(substitute))
459
+
460
+ relation.bind_values = [[column, id]]
461
+ relation
462
+ end
463
+
464
+ def create_or_update
465
+ raise ReadOnlyRecord if readonly?
466
+ result = new_record? ? create_record : update_record
467
+ result != false
468
+ end
469
+
470
+ # Updates the associated record with values matching those of the instance attributes.
471
+ # Returns the number of affected rows.
472
+ def update_record(attribute_names = @attributes.keys)
473
+ attributes_with_values = arel_attributes_with_values_for_update(attribute_names)
474
+ if attributes_with_values.empty?
475
+ 0
476
+ else
477
+ klass = self.class
478
+ column_hash = klass.connection.schema_cache.columns_hash klass.table_name
479
+ db_columns_with_values = attributes_with_values.map { |attr,value|
480
+ real_column = column_hash[attr.name]
481
+ [real_column, value]
482
+ }
483
+ bind_attrs = attributes_with_values.dup
484
+ bind_attrs.keys.each_with_index do |column, i|
485
+ real_column = db_columns_with_values[i].first
486
+ bind_attrs[column] = klass.connection.substitute_at(real_column, i)
487
+ end
488
+ stmt = klass.unscoped.where(klass.arel_table[klass.primary_key].eq(id_was || id)).arel.compile_update(bind_attrs)
489
+ klass.connection.update stmt, 'SQL', db_columns_with_values
490
+ end
491
+ end
492
+
493
+ # Creates a record with values matching those of the instance attributes
494
+ # and returns its id.
495
+ def create_record(attribute_names = @attributes.keys)
496
+ attributes_values = arel_attributes_with_values_for_create(attribute_names)
497
+
498
+ new_id = self.class.unscoped.insert attributes_values
499
+ self.id ||= new_id if self.class.primary_key
500
+
501
+ @new_record = false
502
+ id
503
+ end
504
+
505
+ def verify_readonly_attribute(name)
506
+ raise ActiveRecordError, "#{name} is marked as readonly" if self.class.readonly_attributes.include?(name)
507
+ end
508
+ end
509
+ end
@@ -0,0 +1,56 @@
1
+
2
+ module ActiveRecord
3
+ # = Active Record Query Cache
4
+ class QueryCache
5
+ module ClassMethods
6
+ # Enable the query cache within the block if Active Record is configured.
7
+ # If it's not, it will execute the given block.
8
+ def cache(&block)
9
+ if ActiveRecord::Base.connected?
10
+ connection.cache(&block)
11
+ else
12
+ yield
13
+ end
14
+ end
15
+
16
+ # Disable the query cache within the block if Active Record is configured.
17
+ # If it's not, it will execute the given block.
18
+ def uncached(&block)
19
+ if ActiveRecord::Base.connected?
20
+ connection.uncached(&block)
21
+ else
22
+ yield
23
+ end
24
+ end
25
+ end
26
+
27
+ def initialize(app)
28
+ @app = app
29
+ end
30
+
31
+ def call(env)
32
+ enabled = ActiveRecord::Base.connection.query_cache_enabled
33
+ connection_id = ActiveRecord::Base.connection_id
34
+ ActiveRecord::Base.connection.enable_query_cache!
35
+
36
+ response = @app.call(env)
37
+ response[2] = Rack::BodyProxy.new(response[2]) do
38
+ restore_query_cache_settings(connection_id, enabled)
39
+ end
40
+
41
+ response
42
+ rescue Exception => e
43
+ restore_query_cache_settings(connection_id, enabled)
44
+ raise e
45
+ end
46
+
47
+ private
48
+
49
+ def restore_query_cache_settings(connection_id, enabled)
50
+ ActiveRecord::Base.connection_id = connection_id
51
+ ActiveRecord::Base.connection.clear_query_cache
52
+ ActiveRecord::Base.connection.disable_query_cache! unless enabled
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,62 @@
1
+ module ActiveRecord
2
+ module Querying
3
+ delegate :find, :take, :take!, :first, :first!, :last, :last!, :exists?, :any?, :many?, :to => :all
4
+ delegate :first_or_create, :first_or_create!, :first_or_initialize, :to => :all
5
+ delegate :find_or_create_by, :find_or_create_by!, :find_or_initialize_by, :to => :all
6
+ delegate :find_by, :find_by!, :to => :all
7
+ delegate :destroy, :destroy_all, :delete, :delete_all, :update, :update_all, :to => :all
8
+ delegate :find_each, :find_in_batches, :to => :all
9
+ delegate :select, :group, :order, :except, :reorder, :limit, :offset, :joins,
10
+ :where, :preload, :eager_load, :includes, :from, :lock, :readonly,
11
+ :having, :create_with, :uniq, :distinct, :references, :none, :unscope, :to => :all
12
+ delegate :count, :average, :minimum, :maximum, :sum, :calculate, :pluck, :ids, :to => :all
13
+
14
+ # Executes a custom SQL query against your database and returns all the results. The results will
15
+ # be returned as an array with columns requested encapsulated as attributes of the model you call
16
+ # this method from. If you call <tt>Product.find_by_sql</tt> then the results will be returned in
17
+ # a Product object with the attributes you specified in the SQL query.
18
+ #
19
+ # If you call a complicated SQL query which spans multiple tables the columns specified by the
20
+ # SELECT will be attributes of the model, whether or not they are columns of the corresponding
21
+ # table.
22
+ #
23
+ # The +sql+ parameter is a full SQL query as a string. It will be called as is, there will be
24
+ # no database agnostic conversions performed. This should be a last resort because using, for example,
25
+ # MySQL specific terms will lock you to using that particular database engine or require you to
26
+ # change your call if you switch engines.
27
+ #
28
+ # # A simple SQL query spanning multiple tables
29
+ # Post.find_by_sql "SELECT p.title, c.author FROM posts p, comments c WHERE p.id = c.post_id"
30
+ # # => [#<Post:0x36bff9c @attributes={"title"=>"Ruby Meetup", "first_name"=>"Quentin"}>, ...]
31
+ #
32
+ # # You can use the same string replacement techniques as you can with ActiveRecord#find
33
+ # Post.find_by_sql ["SELECT title FROM posts WHERE author = ? AND created > ?", author_id, start_date]
34
+ # # => [#<Post:0x36bff9c @attributes={"title"=>"The Cheap Man Buys Twice"}>, ...]
35
+ def find_by_sql(sql, binds = [])
36
+ result_set = connection.select_all(sanitize_sql(sql), "#{name} Load", binds)
37
+ column_types = {}
38
+
39
+ if result_set.respond_to? :column_types
40
+ column_types = result_set.column_types
41
+ else
42
+ ActiveSupport::Deprecation.warn "the object returned from `select_all` must respond to `column_types`"
43
+ end
44
+
45
+ result_set.map { |record| instantiate(record, column_types) }
46
+ end
47
+
48
+ # Returns the result of an SQL statement that should only include a COUNT(*) in the SELECT part.
49
+ # The use of this method should be restricted to complicated SQL queries that can't be executed
50
+ # using the ActiveRecord::Calculations class methods. Look into those before using this.
51
+ #
52
+ # ==== Parameters
53
+ #
54
+ # * +sql+ - An SQL statement which should return a count query from the database, see the example below.
55
+ #
56
+ # Product.count_by_sql "SELECT COUNT(*) FROM sales s, customers c WHERE s.customer_id = c.id"
57
+ def count_by_sql(sql)
58
+ sql = sanitize_conditions(sql)
59
+ connection.select_value(sql, "#{name} Count").to_i
60
+ end
61
+ end
62
+ end