activerecord 3.1.10 → 4.2.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (237) hide show
  1. checksums.yaml +6 -6
  2. data/CHANGELOG.md +1837 -338
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +39 -43
  5. data/examples/performance.rb +51 -20
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +57 -43
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -39
  10. data/lib/active_record/associations/association.rb +71 -85
  11. data/lib/active_record/associations/association_scope.rb +138 -89
  12. data/lib/active_record/associations/belongs_to_association.rb +65 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -3
  14. data/lib/active_record/associations/builder/association.rb +125 -29
  15. data/lib/active_record/associations/builder/belongs_to.rb +91 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +69 -49
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
  18. data/lib/active_record/associations/builder/has_many.rb +8 -64
  19. data/lib/active_record/associations/builder/has_one.rb +12 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +22 -29
  21. data/lib/active_record/associations/collection_association.rb +294 -187
  22. data/lib/active_record/associations/collection_proxy.rb +961 -94
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +118 -23
  25. data/lib/active_record/associations/has_many_through_association.rb +115 -45
  26. data/lib/active_record/associations/has_one_association.rb +57 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +76 -102
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +230 -156
  32. data/lib/active_record/associations/preloader/association.rb +96 -55
  33. data/lib/active_record/associations/preloader/collection_association.rb +3 -3
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +1 -1
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +61 -32
  38. data/lib/active_record/associations/preloader.rb +113 -87
  39. data/lib/active_record/associations/singular_association.rb +29 -13
  40. data/lib/active_record/associations/through_association.rb +37 -19
  41. data/lib/active_record/associations.rb +505 -371
  42. data/lib/active_record/attribute.rb +163 -0
  43. data/lib/active_record/attribute_assignment.rb +212 -0
  44. data/lib/active_record/attribute_decorators.rb +66 -0
  45. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  46. data/lib/active_record/attribute_methods/dirty.rb +141 -51
  47. data/lib/active_record/attribute_methods/primary_key.rb +87 -36
  48. data/lib/active_record/attribute_methods/query.rb +5 -4
  49. data/lib/active_record/attribute_methods/read.rb +74 -117
  50. data/lib/active_record/attribute_methods/serialization.rb +70 -0
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -47
  52. data/lib/active_record/attribute_methods/write.rb +60 -21
  53. data/lib/active_record/attribute_methods.rb +409 -48
  54. data/lib/active_record/attribute_set/builder.rb +106 -0
  55. data/lib/active_record/attribute_set.rb +81 -0
  56. data/lib/active_record/attributes.rb +147 -0
  57. data/lib/active_record/autosave_association.rb +279 -232
  58. data/lib/active_record/base.rb +84 -1969
  59. data/lib/active_record/callbacks.rb +66 -28
  60. data/lib/active_record/coders/json.rb +13 -0
  61. data/lib/active_record/coders/yaml_column.rb +18 -21
  62. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +422 -243
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +170 -194
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -19
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -57
  67. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  68. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
  69. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +273 -170
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +731 -254
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +339 -95
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +946 -0
  75. data/lib/active_record/connection_adapters/column.rb +33 -221
  76. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +140 -602
  78. data/lib/active_record/connection_adapters/mysql_adapter.rb +254 -756
  79. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
  80. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  81. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
  112. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  113. data/lib/active_record/connection_adapters/postgresql_adapter.rb +445 -902
  114. data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +578 -25
  116. data/lib/active_record/connection_handling.rb +132 -0
  117. data/lib/active_record/core.rb +579 -0
  118. data/lib/active_record/counter_cache.rb +159 -102
  119. data/lib/active_record/dynamic_matchers.rb +140 -0
  120. data/lib/active_record/enum.rb +197 -0
  121. data/lib/active_record/errors.rb +102 -34
  122. data/lib/active_record/explain.rb +38 -0
  123. data/lib/active_record/explain_registry.rb +30 -0
  124. data/lib/active_record/explain_subscriber.rb +29 -0
  125. data/lib/active_record/fixture_set/file.rb +56 -0
  126. data/lib/active_record/fixtures.rb +318 -260
  127. data/lib/active_record/gem_version.rb +15 -0
  128. data/lib/active_record/inheritance.rb +247 -0
  129. data/lib/active_record/integration.rb +113 -0
  130. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  131. data/lib/active_record/locale/en.yml +8 -1
  132. data/lib/active_record/locking/optimistic.rb +80 -52
  133. data/lib/active_record/locking/pessimistic.rb +27 -5
  134. data/lib/active_record/log_subscriber.rb +25 -18
  135. data/lib/active_record/migration/command_recorder.rb +130 -38
  136. data/lib/active_record/migration/join_table.rb +15 -0
  137. data/lib/active_record/migration.rb +532 -201
  138. data/lib/active_record/model_schema.rb +342 -0
  139. data/lib/active_record/nested_attributes.rb +229 -139
  140. data/lib/active_record/no_touching.rb +52 -0
  141. data/lib/active_record/null_relation.rb +81 -0
  142. data/lib/active_record/persistence.rb +304 -99
  143. data/lib/active_record/query_cache.rb +25 -43
  144. data/lib/active_record/querying.rb +68 -0
  145. data/lib/active_record/railtie.rb +86 -45
  146. data/lib/active_record/railties/console_sandbox.rb +3 -4
  147. data/lib/active_record/railties/controller_runtime.rb +7 -4
  148. data/lib/active_record/railties/databases.rake +198 -377
  149. data/lib/active_record/railties/jdbcmysql_error.rb +2 -2
  150. data/lib/active_record/readonly_attributes.rb +23 -0
  151. data/lib/active_record/reflection.rb +516 -165
  152. data/lib/active_record/relation/batches.rb +96 -45
  153. data/lib/active_record/relation/calculations.rb +221 -144
  154. data/lib/active_record/relation/delegation.rb +140 -0
  155. data/lib/active_record/relation/finder_methods.rb +362 -243
  156. data/lib/active_record/relation/merger.rb +193 -0
  157. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  158. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  159. data/lib/active_record/relation/predicate_builder.rb +135 -41
  160. data/lib/active_record/relation/query_methods.rb +982 -155
  161. data/lib/active_record/relation/spawn_methods.rb +50 -110
  162. data/lib/active_record/relation.rb +371 -180
  163. data/lib/active_record/result.rb +109 -12
  164. data/lib/active_record/runtime_registry.rb +22 -0
  165. data/lib/active_record/sanitization.rb +191 -0
  166. data/lib/active_record/schema.rb +19 -13
  167. data/lib/active_record/schema_dumper.rb +111 -61
  168. data/lib/active_record/schema_migration.rb +53 -0
  169. data/lib/active_record/scoping/default.rb +135 -0
  170. data/lib/active_record/scoping/named.rb +164 -0
  171. data/lib/active_record/scoping.rb +87 -0
  172. data/lib/active_record/serialization.rb +7 -45
  173. data/lib/active_record/serializers/xml_serializer.rb +14 -65
  174. data/lib/active_record/statement_cache.rb +111 -0
  175. data/lib/active_record/store.rb +205 -0
  176. data/lib/active_record/tasks/database_tasks.rb +299 -0
  177. data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
  178. data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
  179. data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
  180. data/lib/active_record/timestamp.rb +35 -14
  181. data/lib/active_record/transactions.rb +141 -74
  182. data/lib/active_record/translation.rb +22 -0
  183. data/lib/active_record/type/big_integer.rb +13 -0
  184. data/lib/active_record/type/binary.rb +50 -0
  185. data/lib/active_record/type/boolean.rb +31 -0
  186. data/lib/active_record/type/date.rb +50 -0
  187. data/lib/active_record/type/date_time.rb +54 -0
  188. data/lib/active_record/type/decimal.rb +64 -0
  189. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  190. data/lib/active_record/type/decorator.rb +14 -0
  191. data/lib/active_record/type/float.rb +19 -0
  192. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  193. data/lib/active_record/type/integer.rb +59 -0
  194. data/lib/active_record/type/mutable.rb +16 -0
  195. data/lib/active_record/type/numeric.rb +36 -0
  196. data/lib/active_record/type/serialized.rb +62 -0
  197. data/lib/active_record/type/string.rb +40 -0
  198. data/lib/active_record/type/text.rb +11 -0
  199. data/lib/active_record/type/time.rb +26 -0
  200. data/lib/active_record/type/time_value.rb +38 -0
  201. data/lib/active_record/type/type_map.rb +64 -0
  202. data/lib/active_record/type/unsigned_integer.rb +15 -0
  203. data/lib/active_record/type/value.rb +110 -0
  204. data/lib/active_record/type.rb +23 -0
  205. data/lib/active_record/validations/associated.rb +27 -18
  206. data/lib/active_record/validations/presence.rb +67 -0
  207. data/lib/active_record/validations/uniqueness.rb +125 -66
  208. data/lib/active_record/validations.rb +37 -30
  209. data/lib/active_record/version.rb +5 -7
  210. data/lib/active_record.rb +80 -25
  211. data/lib/rails/generators/active_record/migration/migration_generator.rb +54 -9
  212. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  213. data/lib/rails/generators/active_record/migration/templates/migration.rb +25 -11
  214. data/lib/rails/generators/active_record/migration.rb +11 -8
  215. data/lib/rails/generators/active_record/model/model_generator.rb +17 -4
  216. data/lib/rails/generators/active_record/model/templates/model.rb +5 -2
  217. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  218. data/lib/rails/generators/active_record.rb +3 -11
  219. metadata +132 -53
  220. data/examples/associations.png +0 -0
  221. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -62
  222. data/lib/active_record/associations/join_helper.rb +0 -55
  223. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  224. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -135
  225. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -556
  226. data/lib/active_record/dynamic_finder_match.rb +0 -56
  227. data/lib/active_record/dynamic_scope_match.rb +0 -23
  228. data/lib/active_record/identity_map.rb +0 -163
  229. data/lib/active_record/named_scope.rb +0 -200
  230. data/lib/active_record/observer.rb +0 -121
  231. data/lib/active_record/session_store.rb +0 -358
  232. data/lib/active_record/test_case.rb +0 -69
  233. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -17
  234. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  235. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  236. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  237. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -16
@@ -1,15 +1,27 @@
1
- require 'thread'
2
-
3
1
  module ActiveRecord
4
2
  # See ActiveRecord::Transactions::ClassMethods for documentation.
5
3
  module Transactions
6
4
  extend ActiveSupport::Concern
7
-
8
- class TransactionError < ActiveRecordError # :nodoc:
9
- end
5
+ #:nodoc:
6
+ ACTIONS = [:create, :destroy, :update]
7
+ #:nodoc:
8
+ CALLBACK_WARN_MESSAGE = "Currently, Active Record suppresses errors raised " \
9
+ "within `after_rollback`/`after_commit` callbacks and only print them to " \
10
+ "the logs. In the next version, these errors will no longer be suppressed. " \
11
+ "Instead, the errors will propagate normally just like in other Active " \
12
+ "Record callbacks.\n" \
13
+ "\n" \
14
+ "You can opt into the new behavior and remove this warning by setting:\n" \
15
+ "\n" \
16
+ " config.active_record.raise_in_transactional_callbacks = true\n\n"
10
17
 
11
18
  included do
12
- define_callbacks :commit, :rollback, :terminator => "result == false", :scope => [:kind, :name]
19
+ define_callbacks :commit, :rollback,
20
+ terminator: ->(_, result) { result == false },
21
+ scope: [:kind, :name]
22
+
23
+ mattr_accessor :raise_in_transactional_callbacks, instance_writer: false
24
+ self.raise_in_transactional_callbacks = false
13
25
  end
14
26
 
15
27
  # = Active Record Transactions
@@ -108,10 +120,10 @@ module ActiveRecord
108
120
  #
109
121
  # # Suppose that we have a Number model with a unique column called 'i'.
110
122
  # Number.transaction do
111
- # Number.create(:i => 0)
123
+ # Number.create(i: 0)
112
124
  # begin
113
125
  # # This will raise a unique constraint error...
114
- # Number.create(:i => 0)
126
+ # Number.create(i: 0)
115
127
  # rescue ActiveRecord::StatementInvalid
116
128
  # # ...which we ignore.
117
129
  # end
@@ -119,7 +131,7 @@ module ActiveRecord
119
131
  # # On PostgreSQL, the transaction is now unusable. The following
120
132
  # # statement will cause a PostgreSQL error, even though the unique
121
133
  # # constraint is no longer violated:
122
- # Number.create(:i => 1)
134
+ # Number.create(i: 1)
123
135
  # # => "PGError: ERROR: current transaction is aborted, commands
124
136
  # # ignored until end of transaction block"
125
137
  # end
@@ -134,9 +146,9 @@ module ActiveRecord
134
146
  # transaction. For example, the following behavior may be surprising:
135
147
  #
136
148
  # User.transaction do
137
- # User.create(:username => 'Kotori')
149
+ # User.create(username: 'Kotori')
138
150
  # User.transaction do
139
- # User.create(:username => 'Nemu')
151
+ # User.create(username: 'Nemu')
140
152
  # raise ActiveRecord::Rollback
141
153
  # end
142
154
  # end
@@ -147,25 +159,25 @@ module ActiveRecord
147
159
  # real transaction is committed.
148
160
  #
149
161
  # In order to get a ROLLBACK for the nested transaction you may ask for a real
150
- # sub-transaction by passing <tt>:requires_new => true</tt>. If anything goes wrong,
162
+ # sub-transaction by passing <tt>requires_new: true</tt>. If anything goes wrong,
151
163
  # the database rolls back to the beginning of the sub-transaction without rolling
152
164
  # back the parent transaction. If we add it to the previous example:
153
165
  #
154
166
  # User.transaction do
155
- # User.create(:username => 'Kotori')
156
- # User.transaction(:requires_new => true) do
157
- # User.create(:username => 'Nemu')
167
+ # User.create(username: 'Kotori')
168
+ # User.transaction(requires_new: true) do
169
+ # User.create(username: 'Nemu')
158
170
  # raise ActiveRecord::Rollback
159
171
  # end
160
172
  # end
161
173
  #
162
- # only "Kotori" is created. (This works on MySQL and PostgreSQL, but not on SQLite3.)
174
+ # only "Kotori" is created. This works on MySQL and PostgreSQL. SQLite3 version >= '3.6.8' also supports it.
163
175
  #
164
176
  # Most databases don't support true nested transactions. At the time of
165
177
  # writing, the only database that we're aware of that supports true nested
166
178
  # transactions, is MS-SQL. Because of this, Active Record emulates nested
167
179
  # transactions by using savepoints on MySQL and PostgreSQL. See
168
- # http://dev.mysql.com/doc/refman/5.0/en/savepoint.html
180
+ # http://dev.mysql.com/doc/refman/5.6/en/savepoint.html
169
181
  # for more information about savepoints.
170
182
  #
171
183
  # === Callbacks
@@ -194,7 +206,7 @@ module ActiveRecord
194
206
  # automatically released. The following example demonstrates the problem:
195
207
  #
196
208
  # Model.connection.transaction do # BEGIN
197
- # Model.connection.transaction(:requires_new => true) do # CREATE SAVEPOINT active_record_1
209
+ # Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
198
210
  # Model.connection.create_table(...) # active_record_1 now automatically released
199
211
  # end # RELEASE savepoint active_record_1
200
212
  # # ^^^^ BOOM! database error!
@@ -208,22 +220,55 @@ module ActiveRecord
208
220
  connection.transaction(options, &block)
209
221
  end
210
222
 
223
+ # This callback is called after a record has been created, updated, or destroyed.
224
+ #
225
+ # You can specify that the callback should only be fired by a certain action with
226
+ # the +:on+ option:
227
+ #
228
+ # after_commit :do_foo, on: :create
229
+ # after_commit :do_bar, on: :update
230
+ # after_commit :do_baz, on: :destroy
231
+ #
232
+ # after_commit :do_foo_bar, on: [:create, :update]
233
+ # after_commit :do_bar_baz, on: [:update, :destroy]
234
+ #
235
+ # Note that transactional fixtures do not play well with this feature. Please
236
+ # use the +test_after_commit+ gem to have these hooks fired in tests.
211
237
  def after_commit(*args, &block)
212
- options = args.last
213
- if options.is_a?(Hash) && options[:on]
214
- options[:if] = Array.wrap(options[:if])
215
- options[:if] << "transaction_include_action?(:#{options[:on]})"
216
- end
238
+ set_options_for_callbacks!(args)
217
239
  set_callback(:commit, :after, *args, &block)
240
+ unless ActiveRecord::Base.raise_in_transactional_callbacks
241
+ ActiveSupport::Deprecation.warn(CALLBACK_WARN_MESSAGE)
242
+ end
218
243
  end
219
244
 
245
+ # This callback is called after a create, update, or destroy are rolled back.
246
+ #
247
+ # Please check the documentation of +after_commit+ for options.
220
248
  def after_rollback(*args, &block)
249
+ set_options_for_callbacks!(args)
250
+ set_callback(:rollback, :after, *args, &block)
251
+ unless ActiveRecord::Base.raise_in_transactional_callbacks
252
+ ActiveSupport::Deprecation.warn(CALLBACK_WARN_MESSAGE)
253
+ end
254
+ end
255
+
256
+ private
257
+
258
+ def set_options_for_callbacks!(args)
221
259
  options = args.last
222
260
  if options.is_a?(Hash) && options[:on]
223
- options[:if] = Array.wrap(options[:if])
224
- options[:if] << "transaction_include_action?(:#{options[:on]})"
261
+ fire_on = Array(options[:on])
262
+ assert_valid_transaction_action(fire_on)
263
+ options[:if] = Array(options[:if])
264
+ options[:if] << "transaction_include_any_action?(#{fire_on})"
265
+ end
266
+ end
267
+
268
+ def assert_valid_transaction_action(actions)
269
+ if (actions - ACTIONS).any?
270
+ raise ArgumentError, ":on conditions for after_commit and after_rollback callbacks have to be one of #{ACTIONS}"
225
271
  end
226
- set_callback(:rollback, :after, *args, &block)
227
272
  end
228
273
  end
229
274
 
@@ -246,40 +291,50 @@ module ActiveRecord
246
291
  with_transaction_returning_status { super }
247
292
  end
248
293
 
294
+ def touch(*) #:nodoc:
295
+ with_transaction_returning_status { super }
296
+ end
297
+
249
298
  # Reset id and @new_record if the transaction rolls back.
250
299
  def rollback_active_record_state!
251
300
  remember_transaction_record_state
252
301
  yield
253
302
  rescue Exception
254
- IdentityMap.remove(self) if IdentityMap.enabled?
255
303
  restore_transaction_record_state
256
304
  raise
257
305
  ensure
258
306
  clear_transaction_record_state
259
307
  end
260
308
 
261
- # Call the after_commit callbacks
262
- def committed! #:nodoc:
263
- run_callbacks :commit
309
+ # Call the +after_commit+ callbacks.
310
+ #
311
+ # Ensure that it is not called if the object was never persisted (failed create),
312
+ # but call it after the commit of a destroyed object.
313
+ def committed!(should_run_callbacks = true) #:nodoc:
314
+ _run_commit_callbacks if should_run_callbacks && destroyed? || persisted?
264
315
  ensure
265
- clear_transaction_record_state
316
+ force_clear_transaction_record_state
266
317
  end
267
318
 
268
- # Call the after rollback callbacks. The restore_state argument indicates if the record
319
+ # Call the +after_rollback+ callbacks. The +force_restore_state+ argument indicates if the record
269
320
  # state should be rolled back to the beginning or just to the last savepoint.
270
- def rolledback!(force_restore_state = false) #:nodoc:
271
- run_callbacks :rollback
321
+ def rolledback!(force_restore_state = false, should_run_callbacks = true) #:nodoc:
322
+ _run_rollback_callbacks if should_run_callbacks
272
323
  ensure
273
- IdentityMap.remove(self) if IdentityMap.enabled?
274
324
  restore_transaction_record_state(force_restore_state)
325
+ clear_transaction_record_state
275
326
  end
276
327
 
277
- # Add the record to the current transaction so that the :after_rollback and :after_commit callbacks
328
+ # Add the record to the current transaction so that the +after_rollback+ and +after_commit+ callbacks
278
329
  # can be called.
279
330
  def add_to_transaction
280
- if self.class.connection.add_transaction_record(self)
281
- remember_transaction_record_state
331
+ if has_transactional_callbacks?
332
+ self.class.connection.add_transaction_record(self)
333
+ else
334
+ sync_with_transaction_state
335
+ set_transaction_state(self.class.connection.transaction_state)
282
336
  end
337
+ remember_transaction_record_state
283
338
  end
284
339
 
285
340
  # Executes +method+ within a transaction and captures its return value as a
@@ -292,68 +347,80 @@ module ActiveRecord
292
347
  status = nil
293
348
  self.class.transaction do
294
349
  add_to_transaction
295
- status = yield
350
+ begin
351
+ status = yield
352
+ rescue ActiveRecord::Rollback
353
+ clear_transaction_record_state
354
+ status = nil
355
+ end
356
+
296
357
  raise ActiveRecord::Rollback unless status
297
358
  end
298
359
  status
360
+ ensure
361
+ if @transaction_state && @transaction_state.committed?
362
+ clear_transaction_record_state
363
+ end
299
364
  end
300
365
 
301
366
  protected
302
367
 
303
368
  # Save the new record state and id of a record so it can be restored later if a transaction fails.
304
- def remember_transaction_record_state #:nodoc
305
- @_start_transaction_state ||= {}
306
- @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
307
- unless @_start_transaction_state.include?(:new_record)
308
- @_start_transaction_state[:new_record] = @new_record
309
- end
310
- unless @_start_transaction_state.include?(:destroyed)
311
- @_start_transaction_state[:destroyed] = @destroyed
312
- end
369
+ def remember_transaction_record_state #:nodoc:
370
+ @_start_transaction_state[:id] = id
371
+ @_start_transaction_state.reverse_merge!(
372
+ new_record: @new_record,
373
+ destroyed: @destroyed,
374
+ frozen?: frozen?,
375
+ )
313
376
  @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
314
377
  end
315
378
 
316
379
  # Clear the new record state and id of a record.
317
- def clear_transaction_record_state #:nodoc
318
- if defined?(@_start_transaction_state)
319
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
320
- remove_instance_variable(:@_start_transaction_state) if @_start_transaction_state[:level] < 1
321
- end
380
+ def clear_transaction_record_state #:nodoc:
381
+ @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
382
+ force_clear_transaction_record_state if @_start_transaction_state[:level] < 1
383
+ end
384
+
385
+ # Force to clear the transaction record state.
386
+ def force_clear_transaction_record_state #:nodoc:
387
+ @_start_transaction_state.clear
322
388
  end
323
389
 
324
390
  # Restore the new record state and id of a record that was previously saved by a call to save_record_state.
325
- def restore_transaction_record_state(force = false) #:nodoc
326
- if defined?(@_start_transaction_state)
327
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
328
- if @_start_transaction_state[:level] < 1
329
- restore_state = remove_instance_variable(:@_start_transaction_state)
330
- @attributes = @attributes.dup if @attributes.frozen?
391
+ def restore_transaction_record_state(force = false) #:nodoc:
392
+ unless @_start_transaction_state.empty?
393
+ transaction_level = (@_start_transaction_state[:level] || 0) - 1
394
+ if transaction_level < 1 || force
395
+ restore_state = @_start_transaction_state
396
+ thaw
331
397
  @new_record = restore_state[:new_record]
332
398
  @destroyed = restore_state[:destroyed]
333
- if restore_state.has_key?(:id)
334
- self.id = restore_state[:id]
335
- else
336
- @attributes.delete(self.class.primary_key)
337
- @attributes_cache.delete(self.class.primary_key)
399
+ pk = self.class.primary_key
400
+ if pk && read_attribute(pk) != restore_state[:id]
401
+ write_attribute(pk, restore_state[:id])
338
402
  end
403
+ freeze if restore_state[:frozen?]
339
404
  end
340
405
  end
341
406
  end
342
407
 
343
408
  # Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
344
- def transaction_record_state(state) #:nodoc
345
- @_start_transaction_state[state] if defined?(@_start_transaction_state)
409
+ def transaction_record_state(state) #:nodoc:
410
+ @_start_transaction_state[state]
346
411
  end
347
412
 
348
413
  # Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
349
- def transaction_include_action?(action) #:nodoc
350
- case action
351
- when :create
352
- transaction_record_state(:new_record)
353
- when :destroy
354
- destroyed?
355
- when :update
356
- !(transaction_record_state(:new_record) || destroyed?)
414
+ def transaction_include_any_action?(actions) #:nodoc:
415
+ actions.any? do |action|
416
+ case action
417
+ when :create
418
+ transaction_record_state(:new_record)
419
+ when :destroy
420
+ destroyed?
421
+ when :update
422
+ !(transaction_record_state(:new_record) || destroyed?)
423
+ end
357
424
  end
358
425
  end
359
426
  end
@@ -0,0 +1,22 @@
1
+ module ActiveRecord
2
+ module Translation
3
+ include ActiveModel::Translation
4
+
5
+ # Set the lookup ancestors for ActiveModel.
6
+ def lookup_ancestors #:nodoc:
7
+ klass = self
8
+ classes = [klass]
9
+ return classes if klass == ActiveRecord::Base
10
+
11
+ while klass != klass.base_class
12
+ classes << klass = klass.superclass
13
+ end
14
+ classes
15
+ end
16
+
17
+ # Set the i18n scope to overwrite ActiveModel.
18
+ def i18n_scope #:nodoc:
19
+ :activerecord
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,13 @@
1
+ require 'active_record/type/integer'
2
+
3
+ module ActiveRecord
4
+ module Type
5
+ class BigInteger < Integer # :nodoc:
6
+ private
7
+
8
+ def max_value
9
+ ::Float::INFINITY
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,50 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class Binary < Value # :nodoc:
4
+ def type
5
+ :binary
6
+ end
7
+
8
+ def binary?
9
+ true
10
+ end
11
+
12
+ def type_cast(value)
13
+ if value.is_a?(Data)
14
+ value.to_s
15
+ else
16
+ super
17
+ end
18
+ end
19
+
20
+ def type_cast_for_database(value)
21
+ return if value.nil?
22
+ Data.new(super)
23
+ end
24
+
25
+ def changed_in_place?(raw_old_value, value)
26
+ old_value = type_cast_from_database(raw_old_value)
27
+ old_value != value
28
+ end
29
+
30
+ class Data # :nodoc:
31
+ def initialize(value)
32
+ @value = value.to_s
33
+ end
34
+
35
+ def to_s
36
+ @value
37
+ end
38
+ alias_method :to_str, :to_s
39
+
40
+ def hex
41
+ @value.unpack('H*')[0]
42
+ end
43
+
44
+ def ==(other)
45
+ other == to_s || super
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,31 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class Boolean < Value # :nodoc:
4
+ def type
5
+ :boolean
6
+ end
7
+
8
+ private
9
+
10
+ def cast_value(value)
11
+ if value == ''
12
+ nil
13
+ elsif ConnectionAdapters::Column::TRUE_VALUES.include?(value)
14
+ true
15
+ else
16
+ if !ConnectionAdapters::Column::FALSE_VALUES.include?(value)
17
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
18
+ You attempted to assign a value which is not explicitly `true` or `false`
19
+ (#{value.inspect})
20
+ to a boolean column. Currently this value casts to `false`. This will
21
+ change to match Ruby's semantics, and will cast to `true` in Rails 5.
22
+ If you would like to maintain the current behavior, you should
23
+ explicitly handle the values you would like cast to `false`.
24
+ MSG
25
+ end
26
+ false
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,50 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class Date < Value # :nodoc:
4
+ def type
5
+ :date
6
+ end
7
+
8
+ def klass
9
+ ::Date
10
+ end
11
+
12
+ def type_cast_for_database(value)
13
+ type_cast(value)
14
+ end
15
+
16
+ def type_cast_for_schema(value)
17
+ "'#{value.to_s(:db)}'"
18
+ end
19
+
20
+ private
21
+
22
+ def cast_value(value)
23
+ if value.is_a?(::String)
24
+ return if value.empty?
25
+ fast_string_to_date(value) || fallback_string_to_date(value)
26
+ elsif value.respond_to?(:to_date)
27
+ value.to_date
28
+ else
29
+ value
30
+ end
31
+ end
32
+
33
+ def fast_string_to_date(string)
34
+ if string =~ ConnectionAdapters::Column::Format::ISO_DATE
35
+ new_date $1.to_i, $2.to_i, $3.to_i
36
+ end
37
+ end
38
+
39
+ def fallback_string_to_date(string)
40
+ new_date(*::Date._parse(string, false).values_at(:year, :mon, :mday))
41
+ end
42
+
43
+ def new_date(year, mon, mday)
44
+ if year && year != 0
45
+ ::Date.new(year, mon, mday) rescue nil
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,54 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class DateTime < Value # :nodoc:
4
+ include TimeValue
5
+
6
+ def type
7
+ :datetime
8
+ end
9
+
10
+ def type_cast_for_database(value)
11
+ return super unless value.acts_like?(:time)
12
+
13
+ zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
14
+
15
+ if value.respond_to?(zone_conversion_method)
16
+ value = value.send(zone_conversion_method)
17
+ end
18
+
19
+ return value unless has_precision?
20
+
21
+ result = value.to_s(:db)
22
+ if value.respond_to?(:usec) && (1..6).cover?(precision)
23
+ "#{result}.#{sprintf("%0#{precision}d", value.usec / 10 ** (6 - precision))}"
24
+ else
25
+ result
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ alias has_precision? precision
32
+
33
+ def cast_value(string)
34
+ return string unless string.is_a?(::String)
35
+ return if string.empty?
36
+
37
+ fast_string_to_time(string) || fallback_string_to_time(string)
38
+ end
39
+
40
+ # '0.123456' -> 123456
41
+ # '1.123456' -> 123456
42
+ def microseconds(time)
43
+ time[:sec_fraction] ? (time[:sec_fraction] * 1_000_000).to_i : 0
44
+ end
45
+
46
+ def fallback_string_to_time(string)
47
+ time_hash = ::Date._parse(string)
48
+ time_hash[:sec_fraction] = microseconds(time_hash)
49
+
50
+ new_time(*time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset))
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,64 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class Decimal < Value # :nodoc:
4
+ include Numeric
5
+
6
+ def type
7
+ :decimal
8
+ end
9
+
10
+ def type_cast_for_schema(value)
11
+ value.to_s
12
+ end
13
+
14
+ private
15
+
16
+ def cast_value(value)
17
+ casted_value = case value
18
+ when ::Float
19
+ convert_float_to_big_decimal(value)
20
+ when ::Numeric
21
+ BigDecimal(value, precision.to_i)
22
+ when ::String
23
+ begin
24
+ value.to_d
25
+ rescue ArgumentError
26
+ BigDecimal(0)
27
+ end
28
+ else
29
+ if value.respond_to?(:to_d)
30
+ value.to_d
31
+ else
32
+ cast_value(value.to_s)
33
+ end
34
+ end
35
+
36
+ apply_scale(casted_value)
37
+ end
38
+
39
+ def convert_float_to_big_decimal(value)
40
+ if precision
41
+ BigDecimal(apply_scale(value), float_precision)
42
+ else
43
+ value.to_d
44
+ end
45
+ end
46
+
47
+ def float_precision
48
+ if precision.to_i > ::Float::DIG + 1
49
+ ::Float::DIG + 1
50
+ else
51
+ precision.to_i
52
+ end
53
+ end
54
+
55
+ def apply_scale(value)
56
+ if scale
57
+ value.round(scale)
58
+ else
59
+ value
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,11 @@
1
+ require 'active_record/type/big_integer'
2
+
3
+ module ActiveRecord
4
+ module Type
5
+ class DecimalWithoutScale < BigInteger # :nodoc:
6
+ def type
7
+ :decimal
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,14 @@
1
+ module ActiveRecord
2
+ module Type
3
+ module Decorator # :nodoc:
4
+ def init_with(coder)
5
+ @subtype = coder['subtype']
6
+ __setobj__(@subtype)
7
+ end
8
+
9
+ def encode_with(coder)
10
+ coder['subtype'] = __getobj__
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,19 @@
1
+ module ActiveRecord
2
+ module Type
3
+ class Float < Value # :nodoc:
4
+ include Numeric
5
+
6
+ def type
7
+ :float
8
+ end
9
+
10
+ alias type_cast_for_database type_cast
11
+
12
+ private
13
+
14
+ def cast_value(value)
15
+ value.to_f
16
+ end
17
+ end
18
+ end
19
+ end