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.
- checksums.yaml +6 -6
- data/CHANGELOG.md +1837 -338
- data/MIT-LICENSE +1 -1
- data/README.rdoc +39 -43
- data/examples/performance.rb +51 -20
- data/examples/simple.rb +4 -4
- data/lib/active_record/aggregations.rb +57 -43
- data/lib/active_record/association_relation.rb +35 -0
- data/lib/active_record/associations/alias_tracker.rb +47 -39
- data/lib/active_record/associations/association.rb +71 -85
- data/lib/active_record/associations/association_scope.rb +138 -89
- data/lib/active_record/associations/belongs_to_association.rb +65 -25
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -3
- data/lib/active_record/associations/builder/association.rb +125 -29
- data/lib/active_record/associations/builder/belongs_to.rb +91 -60
- 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 -52
- data/lib/active_record/associations/builder/singular_association.rb +22 -29
- data/lib/active_record/associations/collection_association.rb +294 -187
- data/lib/active_record/associations/collection_proxy.rb +961 -94
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +118 -23
- data/lib/active_record/associations/has_many_through_association.rb +115 -45
- data/lib/active_record/associations/has_one_association.rb +57 -24
- data/lib/active_record/associations/has_one_through_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +76 -102
- 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 +61 -32
- data/lib/active_record/associations/preloader.rb +113 -87
- data/lib/active_record/associations/singular_association.rb +29 -13
- data/lib/active_record/associations/through_association.rb +37 -19
- data/lib/active_record/associations.rb +505 -371
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +212 -0
- 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 +141 -51
- data/lib/active_record/attribute_methods/primary_key.rb +87 -36
- data/lib/active_record/attribute_methods/query.rb +5 -4
- data/lib/active_record/attribute_methods/read.rb +74 -117
- data/lib/active_record/attribute_methods/serialization.rb +70 -0
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +49 -47
- data/lib/active_record/attribute_methods/write.rb +60 -21
- data/lib/active_record/attribute_methods.rb +409 -48
- 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 +279 -232
- data/lib/active_record/base.rb +84 -1969
- 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 +422 -243
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +170 -194
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -19
- data/lib/active_record/connection_adapters/abstract/quoting.rb +79 -57
- 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 +273 -170
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +731 -254
- data/lib/active_record/connection_adapters/abstract/transaction.rb +215 -0
- data/lib/active_record/connection_adapters/abstract_adapter.rb +339 -95
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +946 -0
- data/lib/active_record/connection_adapters/column.rb +33 -221
- data/lib/active_record/connection_adapters/connection_specification.rb +275 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +140 -602
- data/lib/active_record/connection_adapters/mysql_adapter.rb +254 -756
- 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 +445 -902
- data/lib/active_record/connection_adapters/schema_cache.rb +94 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +578 -25
- data/lib/active_record/connection_handling.rb +132 -0
- data/lib/active_record/core.rb +579 -0
- data/lib/active_record/counter_cache.rb +159 -102
- data/lib/active_record/dynamic_matchers.rb +140 -0
- data/lib/active_record/enum.rb +197 -0
- data/lib/active_record/errors.rb +102 -34
- data/lib/active_record/explain.rb +38 -0
- data/lib/active_record/explain_registry.rb +30 -0
- data/lib/active_record/explain_subscriber.rb +29 -0
- data/lib/active_record/fixture_set/file.rb +56 -0
- data/lib/active_record/fixtures.rb +318 -260
- data/lib/active_record/gem_version.rb +15 -0
- data/lib/active_record/inheritance.rb +247 -0
- data/lib/active_record/integration.rb +113 -0
- 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 +80 -52
- data/lib/active_record/locking/pessimistic.rb +27 -5
- data/lib/active_record/log_subscriber.rb +25 -18
- data/lib/active_record/migration/command_recorder.rb +130 -38
- data/lib/active_record/migration/join_table.rb +15 -0
- data/lib/active_record/migration.rb +532 -201
- data/lib/active_record/model_schema.rb +342 -0
- data/lib/active_record/nested_attributes.rb +229 -139
- data/lib/active_record/no_touching.rb +52 -0
- data/lib/active_record/null_relation.rb +81 -0
- data/lib/active_record/persistence.rb +304 -99
- data/lib/active_record/query_cache.rb +25 -43
- data/lib/active_record/querying.rb +68 -0
- data/lib/active_record/railtie.rb +86 -45
- data/lib/active_record/railties/console_sandbox.rb +3 -4
- data/lib/active_record/railties/controller_runtime.rb +7 -4
- data/lib/active_record/railties/databases.rake +198 -377
- data/lib/active_record/railties/jdbcmysql_error.rb +2 -2
- data/lib/active_record/readonly_attributes.rb +23 -0
- data/lib/active_record/reflection.rb +516 -165
- data/lib/active_record/relation/batches.rb +96 -45
- data/lib/active_record/relation/calculations.rb +221 -144
- data/lib/active_record/relation/delegation.rb +140 -0
- data/lib/active_record/relation/finder_methods.rb +362 -243
- 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 -41
- data/lib/active_record/relation/query_methods.rb +982 -155
- data/lib/active_record/relation/spawn_methods.rb +50 -110
- data/lib/active_record/relation.rb +371 -180
- data/lib/active_record/result.rb +109 -12
- data/lib/active_record/runtime_registry.rb +22 -0
- data/lib/active_record/sanitization.rb +191 -0
- data/lib/active_record/schema.rb +19 -13
- data/lib/active_record/schema_dumper.rb +111 -61
- data/lib/active_record/schema_migration.rb +53 -0
- data/lib/active_record/scoping/default.rb +135 -0
- data/lib/active_record/scoping/named.rb +164 -0
- data/lib/active_record/scoping.rb +87 -0
- data/lib/active_record/serialization.rb +7 -45
- data/lib/active_record/serializers/xml_serializer.rb +14 -65
- data/lib/active_record/statement_cache.rb +111 -0
- data/lib/active_record/store.rb +205 -0
- 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 +35 -14
- data/lib/active_record/transactions.rb +141 -74
- data/lib/active_record/translation.rb +22 -0
- 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 +27 -18
- data/lib/active_record/validations/presence.rb +67 -0
- data/lib/active_record/validations/uniqueness.rb +125 -66
- data/lib/active_record/validations.rb +37 -30
- data/lib/active_record/version.rb +5 -7
- data/lib/active_record.rb +80 -25
- data/lib/rails/generators/active_record/migration/migration_generator.rb +54 -9
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb +25 -11
- data/lib/rails/generators/active_record/migration.rb +11 -8
- data/lib/rails/generators/active_record/model/model_generator.rb +17 -4
- data/lib/rails/generators/active_record/model/templates/model.rb +5 -2
- data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
- data/lib/rails/generators/active_record.rb +3 -11
- metadata +132 -53
- data/examples/associations.png +0 -0
- data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -62
- 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/connection_adapters/abstract/connection_specification.rb +0 -135
- data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -556
- data/lib/active_record/dynamic_finder_match.rb +0 -56
- data/lib/active_record/dynamic_scope_match.rb +0 -23
- data/lib/active_record/identity_map.rb +0 -163
- data/lib/active_record/named_scope.rb +0 -200
- data/lib/active_record/observer.rb +0 -121
- data/lib/active_record/session_store.rb +0 -358
- data/lib/active_record/test_case.rb +0 -69
- data/lib/rails/generators/active_record/model/templates/migration.rb +0 -17
- 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 -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
|
-
|
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!
|
@@ -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
|
-
|
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
|
-
|
224
|
-
|
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
|
-
|
263
|
-
|
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
|
-
|
316
|
+
force_clear_transaction_record_state
|
266
317
|
end
|
267
318
|
|
268
|
-
# Call the
|
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
|
-
|
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
|
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
|
281
|
-
|
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
|
-
|
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
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
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
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
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
|
-
|
327
|
-
|
328
|
-
if
|
329
|
-
restore_state =
|
330
|
-
|
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
|
-
|
334
|
-
|
335
|
-
|
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]
|
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
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
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,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
|