activerecord 3.2.22.5 → 4.2.11.3

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 (236) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1632 -609
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +37 -41
  5. data/examples/performance.rb +31 -19
  6. data/examples/simple.rb +4 -4
  7. data/lib/active_record/aggregations.rb +56 -42
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -36
  10. data/lib/active_record/associations/association.rb +73 -55
  11. data/lib/active_record/associations/association_scope.rb +143 -82
  12. data/lib/active_record/associations/belongs_to_association.rb +65 -25
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +125 -31
  15. data/lib/active_record/associations/builder/belongs_to.rb +89 -61
  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 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +23 -17
  21. data/lib/active_record/associations/collection_association.rb +251 -177
  22. data/lib/active_record/associations/collection_proxy.rb +963 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +113 -22
  25. data/lib/active_record/associations/has_many_through_association.rb +99 -39
  26. data/lib/active_record/associations/has_one_association.rb +43 -20
  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 -107
  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 +62 -33
  38. data/lib/active_record/associations/preloader.rb +101 -79
  39. data/lib/active_record/associations/singular_association.rb +29 -13
  40. data/lib/active_record/associations/through_association.rb +30 -16
  41. data/lib/active_record/associations.rb +463 -345
  42. data/lib/active_record/attribute.rb +163 -0
  43. data/lib/active_record/attribute_assignment.rb +142 -151
  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 +137 -57
  47. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  48. data/lib/active_record/attribute_methods/query.rb +5 -4
  49. data/lib/active_record/attribute_methods/read.rb +73 -106
  50. data/lib/active_record/attribute_methods/serialization.rb +44 -94
  51. data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -45
  52. data/lib/active_record/attribute_methods/write.rb +57 -44
  53. data/lib/active_record/attribute_methods.rb +301 -141
  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 +246 -217
  58. data/lib/active_record/base.rb +70 -474
  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 +396 -219
  63. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  64. data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -164
  65. data/lib/active_record/connection_adapters/abstract/query_cache.rb +29 -24
  66. data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -55
  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 +261 -169
  70. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +707 -259
  72. data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
  73. data/lib/active_record/connection_adapters/abstract_adapter.rb +298 -89
  74. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +466 -196
  75. data/lib/active_record/connection_adapters/column.rb +31 -245
  76. data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +45 -57
  78. data/lib/active_record/connection_adapters/mysql_adapter.rb +180 -123
  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 +430 -999
  114. data/lib/active_record/connection_adapters/schema_cache.rb +52 -27
  115. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +579 -22
  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 +157 -105
  119. data/lib/active_record/dynamic_matchers.rb +119 -63
  120. data/lib/active_record/enum.rb +197 -0
  121. data/lib/active_record/errors.rb +94 -36
  122. data/lib/active_record/explain.rb +15 -63
  123. data/lib/active_record/explain_registry.rb +30 -0
  124. data/lib/active_record/explain_subscriber.rb +9 -5
  125. data/lib/active_record/fixture_set/file.rb +56 -0
  126. data/lib/active_record/fixtures.rb +302 -215
  127. data/lib/active_record/gem_version.rb +15 -0
  128. data/lib/active_record/inheritance.rb +143 -70
  129. data/lib/active_record/integration.rb +65 -12
  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 +73 -52
  133. data/lib/active_record/locking/pessimistic.rb +5 -5
  134. data/lib/active_record/log_subscriber.rb +24 -21
  135. data/lib/active_record/migration/command_recorder.rb +124 -32
  136. data/lib/active_record/migration/join_table.rb +15 -0
  137. data/lib/active_record/migration.rb +511 -213
  138. data/lib/active_record/model_schema.rb +91 -117
  139. data/lib/active_record/nested_attributes.rb +184 -130
  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 +276 -117
  143. data/lib/active_record/query_cache.rb +19 -37
  144. data/lib/active_record/querying.rb +28 -18
  145. data/lib/active_record/railtie.rb +73 -40
  146. data/lib/active_record/railties/console_sandbox.rb +3 -4
  147. data/lib/active_record/railties/controller_runtime.rb +4 -3
  148. data/lib/active_record/railties/databases.rake +141 -416
  149. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  150. data/lib/active_record/readonly_attributes.rb +1 -4
  151. data/lib/active_record/reflection.rb +513 -154
  152. data/lib/active_record/relation/batches.rb +91 -43
  153. data/lib/active_record/relation/calculations.rb +199 -161
  154. data/lib/active_record/relation/delegation.rb +116 -25
  155. data/lib/active_record/relation/finder_methods.rb +362 -248
  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 -43
  160. data/lib/active_record/relation/query_methods.rb +928 -167
  161. data/lib/active_record/relation/spawn_methods.rb +48 -149
  162. data/lib/active_record/relation.rb +352 -207
  163. data/lib/active_record/result.rb +101 -10
  164. data/lib/active_record/runtime_registry.rb +22 -0
  165. data/lib/active_record/sanitization.rb +56 -59
  166. data/lib/active_record/schema.rb +19 -13
  167. data/lib/active_record/schema_dumper.rb +106 -63
  168. data/lib/active_record/schema_migration.rb +53 -0
  169. data/lib/active_record/scoping/default.rb +50 -57
  170. data/lib/active_record/scoping/named.rb +73 -109
  171. data/lib/active_record/scoping.rb +58 -123
  172. data/lib/active_record/serialization.rb +6 -2
  173. data/lib/active_record/serializers/xml_serializer.rb +12 -22
  174. data/lib/active_record/statement_cache.rb +111 -0
  175. data/lib/active_record/store.rb +168 -15
  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 +23 -16
  181. data/lib/active_record/transactions.rb +125 -79
  182. data/lib/active_record/type/big_integer.rb +13 -0
  183. data/lib/active_record/type/binary.rb +50 -0
  184. data/lib/active_record/type/boolean.rb +31 -0
  185. data/lib/active_record/type/date.rb +50 -0
  186. data/lib/active_record/type/date_time.rb +54 -0
  187. data/lib/active_record/type/decimal.rb +64 -0
  188. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  189. data/lib/active_record/type/decorator.rb +14 -0
  190. data/lib/active_record/type/float.rb +19 -0
  191. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  192. data/lib/active_record/type/integer.rb +59 -0
  193. data/lib/active_record/type/mutable.rb +16 -0
  194. data/lib/active_record/type/numeric.rb +36 -0
  195. data/lib/active_record/type/serialized.rb +62 -0
  196. data/lib/active_record/type/string.rb +40 -0
  197. data/lib/active_record/type/text.rb +11 -0
  198. data/lib/active_record/type/time.rb +26 -0
  199. data/lib/active_record/type/time_value.rb +38 -0
  200. data/lib/active_record/type/type_map.rb +64 -0
  201. data/lib/active_record/type/unsigned_integer.rb +15 -0
  202. data/lib/active_record/type/value.rb +110 -0
  203. data/lib/active_record/type.rb +23 -0
  204. data/lib/active_record/validations/associated.rb +24 -16
  205. data/lib/active_record/validations/presence.rb +67 -0
  206. data/lib/active_record/validations/uniqueness.rb +123 -64
  207. data/lib/active_record/validations.rb +36 -29
  208. data/lib/active_record/version.rb +5 -7
  209. data/lib/active_record.rb +66 -46
  210. data/lib/rails/generators/active_record/migration/migration_generator.rb +53 -8
  211. data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +5 -1
  212. data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
  213. data/lib/rails/generators/active_record/migration.rb +11 -8
  214. data/lib/rails/generators/active_record/model/model_generator.rb +9 -4
  215. data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
  216. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  217. data/lib/rails/generators/active_record.rb +3 -11
  218. metadata +101 -45
  219. data/examples/associations.png +0 -0
  220. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  221. data/lib/active_record/associations/join_helper.rb +0 -55
  222. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  223. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  224. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  225. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  226. data/lib/active_record/dynamic_finder_match.rb +0 -68
  227. data/lib/active_record/dynamic_scope_match.rb +0 -23
  228. data/lib/active_record/fixtures/file.rb +0 -65
  229. data/lib/active_record/identity_map.rb +0 -162
  230. data/lib/active_record/observer.rb +0 -121
  231. data/lib/active_record/session_store.rb +0 -360
  232. data/lib/active_record/test_case.rb +0 -73
  233. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  234. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  235. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  236. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -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!
@@ -213,35 +225,50 @@ module ActiveRecord
213
225
  # You can specify that the callback should only be fired by a certain action with
214
226
  # the +:on+ option:
215
227
  #
216
- # after_commit :do_foo, :on => :create
217
- # after_commit :do_bar, :on => :update
218
- # after_commit :do_baz, :on => :destroy
219
- #
220
- # Also, to have the callback fired on create and update, but not on destroy:
228
+ # after_commit :do_foo, on: :create
229
+ # after_commit :do_bar, on: :update
230
+ # after_commit :do_baz, on: :destroy
221
231
  #
222
- # after_commit :do_zoo, :if => :persisted?
232
+ # after_commit :do_foo_bar, on: [:create, :update]
233
+ # after_commit :do_bar_baz, on: [:update, :destroy]
223
234
  #
224
235
  # Note that transactional fixtures do not play well with this feature. Please
225
236
  # use the +test_after_commit+ gem to have these hooks fired in tests.
226
237
  def after_commit(*args, &block)
227
- options = args.last
228
- if options.is_a?(Hash) && options[:on]
229
- options[:if] = Array.wrap(options[:if])
230
- options[:if] << "transaction_include_action?(:#{options[:on]})"
231
- end
238
+ set_options_for_callbacks!(args)
232
239
  set_callback(:commit, :after, *args, &block)
240
+ unless ActiveRecord::Base.raise_in_transactional_callbacks
241
+ ActiveSupport::Deprecation.warn(CALLBACK_WARN_MESSAGE)
242
+ end
233
243
  end
234
244
 
235
245
  # This callback is called after a create, update, or destroy are rolled back.
236
246
  #
237
247
  # Please check the documentation of +after_commit+ for options.
238
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)
239
259
  options = args.last
240
260
  if options.is_a?(Hash) && options[:on]
241
- options[:if] = Array.wrap(options[:if])
242
- 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}"
243
271
  end
244
- set_callback(:rollback, :after, *args, &block)
245
272
  end
246
273
  end
247
274
 
@@ -264,40 +291,50 @@ module ActiveRecord
264
291
  with_transaction_returning_status { super }
265
292
  end
266
293
 
294
+ def touch(*) #:nodoc:
295
+ with_transaction_returning_status { super }
296
+ end
297
+
267
298
  # Reset id and @new_record if the transaction rolls back.
268
299
  def rollback_active_record_state!
269
300
  remember_transaction_record_state
270
301
  yield
271
302
  rescue Exception
272
- IdentityMap.remove(self) if IdentityMap.enabled?
273
303
  restore_transaction_record_state
274
304
  raise
275
305
  ensure
276
306
  clear_transaction_record_state
277
307
  end
278
308
 
279
- # Call the after_commit callbacks
280
- def committed! #:nodoc:
281
- 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?
282
315
  ensure
283
- clear_transaction_record_state
316
+ force_clear_transaction_record_state
284
317
  end
285
318
 
286
- # 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
287
320
  # state should be rolled back to the beginning or just to the last savepoint.
288
- def rolledback!(force_restore_state = false) #:nodoc:
289
- run_callbacks :rollback
321
+ def rolledback!(force_restore_state = false, should_run_callbacks = true) #:nodoc:
322
+ _run_rollback_callbacks if should_run_callbacks
290
323
  ensure
291
- IdentityMap.remove(self) if IdentityMap.enabled?
292
324
  restore_transaction_record_state(force_restore_state)
325
+ clear_transaction_record_state
293
326
  end
294
327
 
295
- # 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
296
329
  # can be called.
297
330
  def add_to_transaction
298
- if self.class.connection.add_transaction_record(self)
299
- 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)
300
336
  end
337
+ remember_transaction_record_state
301
338
  end
302
339
 
303
340
  # Executes +method+ within a transaction and captures its return value as a
@@ -310,71 +347,80 @@ module ActiveRecord
310
347
  status = nil
311
348
  self.class.transaction do
312
349
  add_to_transaction
313
- status = yield
350
+ begin
351
+ status = yield
352
+ rescue ActiveRecord::Rollback
353
+ clear_transaction_record_state
354
+ status = nil
355
+ end
356
+
314
357
  raise ActiveRecord::Rollback unless status
315
358
  end
316
359
  status
360
+ ensure
361
+ if @transaction_state && @transaction_state.committed?
362
+ clear_transaction_record_state
363
+ end
317
364
  end
318
365
 
319
366
  protected
320
367
 
321
368
  # Save the new record state and id of a record so it can be restored later if a transaction fails.
322
369
  def remember_transaction_record_state #:nodoc:
323
- @_start_transaction_state ||= {}
324
- @_start_transaction_state[:id] = id if has_attribute?(self.class.primary_key)
325
- unless @_start_transaction_state.include?(:new_record)
326
- @_start_transaction_state[:new_record] = @new_record
327
- end
328
- unless @_start_transaction_state.include?(:destroyed)
329
- @_start_transaction_state[:destroyed] = @destroyed
330
- end
370
+ @_start_transaction_state[:id] = id
371
+ @_start_transaction_state.reverse_merge!(
372
+ new_record: @new_record,
373
+ destroyed: @destroyed,
374
+ frozen?: frozen?,
375
+ )
331
376
  @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) + 1
332
- @_start_transaction_state[:frozen?] = @attributes.frozen?
333
377
  end
334
378
 
335
379
  # Clear the new record state and id of a record.
336
380
  def clear_transaction_record_state #:nodoc:
337
- if defined?(@_start_transaction_state)
338
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
339
- remove_instance_variable(:@_start_transaction_state) if @_start_transaction_state[:level] < 1
340
- end
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
341
388
  end
342
389
 
343
390
  # Restore the new record state and id of a record that was previously saved by a call to save_record_state.
344
391
  def restore_transaction_record_state(force = false) #:nodoc:
345
- if defined?(@_start_transaction_state)
346
- @_start_transaction_state[:level] = (@_start_transaction_state[:level] || 0) - 1
347
- if @_start_transaction_state[:level] < 1 || force
348
- restore_state = remove_instance_variable(:@_start_transaction_state)
349
- was_frozen = restore_state[:frozen?]
350
- @attributes = @attributes.dup if @attributes.frozen?
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
351
397
  @new_record = restore_state[:new_record]
352
398
  @destroyed = restore_state[:destroyed]
353
- if restore_state.has_key?(:id)
354
- self.id = restore_state[:id]
355
- else
356
- @attributes.delete(self.class.primary_key)
357
- @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])
358
402
  end
359
- @attributes.freeze if was_frozen
403
+ freeze if restore_state[:frozen?]
360
404
  end
361
405
  end
362
406
  end
363
407
 
364
408
  # Determine if a record was created or destroyed in a transaction. State should be one of :new_record or :destroyed.
365
409
  def transaction_record_state(state) #:nodoc:
366
- @_start_transaction_state[state] if defined?(@_start_transaction_state)
410
+ @_start_transaction_state[state]
367
411
  end
368
412
 
369
413
  # Determine if a transaction included an action for :create, :update, or :destroy. Used in filtering callbacks.
370
- def transaction_include_action?(action) #:nodoc:
371
- case action
372
- when :create
373
- transaction_record_state(:new_record)
374
- when :destroy
375
- destroyed?
376
- when :update
377
- !(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
378
424
  end
379
425
  end
380
426
  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