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.
- checksums.yaml +5 -5
- data/CHANGELOG.md +1632 -609
- data/MIT-LICENSE +1 -1
- data/README.rdoc +37 -41
- data/examples/performance.rb +31 -19
- data/examples/simple.rb +4 -4
- data/lib/active_record/aggregations.rb +56 -42
- data/lib/active_record/association_relation.rb +35 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -36
- data/lib/active_record/associations/association.rb +73 -55
- data/lib/active_record/associations/association_scope.rb +143 -82
- data/lib/active_record/associations/belongs_to_association.rb +65 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
- data/lib/active_record/associations/builder/association.rb +125 -31
- data/lib/active_record/associations/builder/belongs_to.rb +89 -61
- data/lib/active_record/associations/builder/collection_association.rb +69 -49
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +113 -42
- data/lib/active_record/associations/builder/has_many.rb +8 -64
- data/lib/active_record/associations/builder/has_one.rb +12 -51
- data/lib/active_record/associations/builder/singular_association.rb +23 -17
- data/lib/active_record/associations/collection_association.rb +251 -177
- data/lib/active_record/associations/collection_proxy.rb +963 -63
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +113 -22
- data/lib/active_record/associations/has_many_through_association.rb +99 -39
- data/lib/active_record/associations/has_one_association.rb +43 -20
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +76 -107
- data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
- data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
- data/lib/active_record/associations/join_dependency.rb +230 -156
- data/lib/active_record/associations/preloader/association.rb +96 -55
- data/lib/active_record/associations/preloader/collection_association.rb +3 -3
- data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
- data/lib/active_record/associations/preloader/has_one.rb +1 -1
- data/lib/active_record/associations/preloader/singular_association.rb +3 -3
- data/lib/active_record/associations/preloader/through_association.rb +62 -33
- data/lib/active_record/associations/preloader.rb +101 -79
- data/lib/active_record/associations/singular_association.rb +29 -13
- data/lib/active_record/associations/through_association.rb +30 -16
- data/lib/active_record/associations.rb +463 -345
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +142 -151
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
- data/lib/active_record/attribute_methods/dirty.rb +137 -57
- data/lib/active_record/attribute_methods/primary_key.rb +50 -36
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +73 -106
- data/lib/active_record/attribute_methods/serialization.rb +44 -94
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -45
- data/lib/active_record/attribute_methods/write.rb +57 -44
- data/lib/active_record/attribute_methods.rb +301 -141
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +246 -217
- data/lib/active_record/base.rb +70 -474
- data/lib/active_record/callbacks.rb +66 -28
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/coders/yaml_column.rb +18 -21
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +396 -219
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +167 -164
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +29 -24
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -55
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +125 -0
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +261 -169
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +707 -259
- data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +298 -89
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +466 -196
- data/lib/active_record/connection_adapters/column.rb +31 -245
- data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +45 -57
- data/lib/active_record/connection_adapters/mysql_adapter.rb +180 -123
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +93 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +232 -0
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +36 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +108 -0
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +596 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +430 -999
- data/lib/active_record/connection_adapters/schema_cache.rb +52 -27
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +579 -22
- data/lib/active_record/connection_handling.rb +132 -0
- data/lib/active_record/core.rb +579 -0
- data/lib/active_record/counter_cache.rb +157 -105
- data/lib/active_record/dynamic_matchers.rb +119 -63
- data/lib/active_record/enum.rb +197 -0
- data/lib/active_record/errors.rb +94 -36
- data/lib/active_record/explain.rb +15 -63
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +9 -5
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +302 -215
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +143 -70
- data/lib/active_record/integration.rb +65 -12
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locale/en.yml +8 -1
- data/lib/active_record/locking/optimistic.rb +73 -52
- data/lib/active_record/locking/pessimistic.rb +5 -5
- data/lib/active_record/log_subscriber.rb +24 -21
- data/lib/active_record/migration/command_recorder.rb +124 -32
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +511 -213
- data/lib/active_record/model_schema.rb +91 -117
- data/lib/active_record/nested_attributes.rb +184 -130
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +81 -0
- data/lib/active_record/persistence.rb +276 -117
- data/lib/active_record/query_cache.rb +19 -37
- data/lib/active_record/querying.rb +28 -18
- data/lib/active_record/railtie.rb +73 -40
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +4 -3
- data/lib/active_record/railties/databases.rake +141 -416
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +1 -4
- data/lib/active_record/reflection.rb +513 -154
- data/lib/active_record/relation/batches.rb +91 -43
- data/lib/active_record/relation/calculations.rb +199 -161
- data/lib/active_record/relation/delegation.rb +116 -25
- data/lib/active_record/relation/finder_methods.rb +362 -248
- data/lib/active_record/relation/merger.rb +193 -0
- data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
- data/lib/active_record/relation/predicate_builder.rb +135 -43
- data/lib/active_record/relation/query_methods.rb +928 -167
- data/lib/active_record/relation/spawn_methods.rb +48 -149
- data/lib/active_record/relation.rb +352 -207
- data/lib/active_record/result.rb +101 -10
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +56 -59
- data/lib/active_record/schema.rb +19 -13
- data/lib/active_record/schema_dumper.rb +106 -63
- data/lib/active_record/schema_migration.rb +53 -0
- data/lib/active_record/scoping/default.rb +50 -57
- data/lib/active_record/scoping/named.rb +73 -109
- data/lib/active_record/scoping.rb +58 -123
- data/lib/active_record/serialization.rb +6 -2
- data/lib/active_record/serializers/xml_serializer.rb +12 -22
- data/lib/active_record/statement_cache.rb +111 -0
- data/lib/active_record/store.rb +168 -15
- data/lib/active_record/tasks/database_tasks.rb +299 -0
- data/lib/active_record/tasks/mysql_database_tasks.rb +159 -0
- data/lib/active_record/tasks/postgresql_database_tasks.rb +101 -0
- data/lib/active_record/tasks/sqlite_database_tasks.rb +55 -0
- data/lib/active_record/timestamp.rb +23 -16
- data/lib/active_record/transactions.rb +125 -79
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
- data/lib/active_record/type/integer.rb +59 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +62 -0
- data/lib/active_record/type/string.rb +40 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +110 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +24 -16
- data/lib/active_record/validations/presence.rb +67 -0
- data/lib/active_record/validations/uniqueness.rb +123 -64
- data/lib/active_record/validations.rb +36 -29
- data/lib/active_record/version.rb +5 -7
- data/lib/active_record.rb +66 -46
- data/lib/rails/generators/active_record/migration/migration_generator.rb +53 -8
- data/lib/rails/generators/active_record/{model/templates/migration.rb → migration/templates/create_table_migration.rb} +5 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +20 -15
- data/lib/rails/generators/active_record/migration.rb +11 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +4 -6
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -11
- metadata +101 -45
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
- data/lib/active_record/associations/join_helper.rb +0 -55
- data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
- data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
- data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
- data/lib/active_record/dynamic_finder_match.rb +0 -68
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/fixtures/file.rb +0 -65
- data/lib/active_record/identity_map.rb +0 -162
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -360
- data/lib/active_record/test_case.rb +0 -73
- data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
- data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
- data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
- 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
|
-
|
9
|
-
|
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,
|
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(:
|
123
|
+
# Number.create(i: 0)
|
112
124
|
# begin
|
113
125
|
# # This will raise a unique constraint error...
|
114
|
-
# Number.create(:
|
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(:
|
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(:
|
149
|
+
# User.create(username: 'Kotori')
|
138
150
|
# User.transaction do
|
139
|
-
# User.create(:
|
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
|
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(:
|
156
|
-
# User.transaction(:
|
157
|
-
# User.create(:
|
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.
|
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.
|
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(:
|
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, :
|
217
|
-
# after_commit :do_bar, :
|
218
|
-
# after_commit :do_baz, :
|
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 :
|
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
|
-
|
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
|
-
|
242
|
-
|
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
|
-
|
281
|
-
|
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
|
-
|
316
|
+
force_clear_transaction_record_state
|
284
317
|
end
|
285
318
|
|
286
|
-
# Call the
|
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
|
-
|
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
|
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
|
299
|
-
|
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
|
-
|
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
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
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
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
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
|
-
|
346
|
-
|
347
|
-
if
|
348
|
-
restore_state =
|
349
|
-
|
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
|
-
|
354
|
-
|
355
|
-
|
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
|
-
|
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]
|
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
|
371
|
-
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
|
377
|
-
|
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,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
|