activerecord 4.2.11.3 → 5.0.7.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1638 -1132
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -8
- data/examples/performance.rb +2 -3
- data/examples/simple.rb +0 -1
- data/lib/active_record.rb +7 -2
- data/lib/active_record/aggregations.rb +34 -21
- data/lib/active_record/association_relation.rb +7 -4
- data/lib/active_record/associations.rb +347 -218
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +22 -10
- data/lib/active_record/associations/association_scope.rb +75 -104
- data/lib/active_record/associations/belongs_to_association.rb +21 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +43 -18
- data/lib/active_record/associations/builder/collection_association.rb +7 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +16 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +11 -6
- data/lib/active_record/associations/builder/singular_association.rb +13 -11
- data/lib/active_record/associations/collection_association.rb +85 -69
- data/lib/active_record/associations/collection_proxy.rb +104 -46
- data/lib/active_record/associations/foreign_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +21 -78
- data/lib/active_record/associations/has_many_through_association.rb +6 -47
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +38 -22
- data/lib/active_record/associations/join_dependency/join_association.rb +15 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +14 -4
- data/lib/active_record/associations/preloader/association.rb +52 -71
- data/lib/active_record/associations/preloader/collection_association.rb +0 -7
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/singular_association.rb +0 -1
- data/lib/active_record/associations/preloader/through_association.rb +36 -17
- data/lib/active_record/associations/singular_association.rb +13 -1
- data/lib/active_record/associations/through_association.rb +12 -4
- data/lib/active_record/attribute.rb +69 -19
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute_assignment.rb +19 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods.rb +69 -44
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +46 -86
- data/lib/active_record/attribute_methods/primary_key.rb +16 -3
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
- data/lib/active_record/attribute_methods/write.rb +13 -37
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set.rb +32 -3
- data/lib/active_record/attribute_set/builder.rb +42 -16
- data/lib/active_record/attributes.rb +199 -81
- data/lib/active_record/autosave_association.rb +54 -17
- data/lib/active_record/base.rb +32 -23
- data/lib/active_record/callbacks.rb +39 -43
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +467 -189
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -62
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +86 -13
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -188
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +407 -156
- data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -71
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +433 -399
- data/lib/active_record/connection_adapters/column.rb +28 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +108 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +25 -166
- data/lib/active_record/connection_adapters/postgresql/column.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -72
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +37 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +13 -3
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +56 -19
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +250 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +264 -170
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +151 -194
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +37 -14
- data/lib/active_record/core.rb +92 -108
- data/lib/active_record/counter_cache.rb +13 -24
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +116 -76
- data/lib/active_record/errors.rb +87 -48
- data/lib/active_record/explain.rb +20 -9
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +77 -41
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +17 -14
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +18 -2
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +15 -15
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +48 -24
- data/lib/active_record/migration.rb +362 -111
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/model_schema.rb +270 -73
- data/lib/active_record/nested_attributes.rb +58 -29
- data/lib/active_record/no_touching.rb +4 -0
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +152 -90
- data/lib/active_record/query_cache.rb +18 -23
- data/lib/active_record/querying.rb +12 -11
- data/lib/active_record/railtie.rb +23 -16
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +52 -41
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +302 -115
- data/lib/active_record/relation.rb +187 -120
- data/lib/active_record/relation/batches.rb +141 -36
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +92 -117
- data/lib/active_record/relation/delegation.rb +8 -20
- data/lib/active_record/relation/finder_methods.rb +173 -89
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +16 -42
- data/lib/active_record/relation/predicate_builder.rb +120 -107
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +308 -244
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -7
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/result.rb +11 -4
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +105 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +54 -37
- data/lib/active_record/schema_migration.rb +11 -14
- data/lib/active_record/scoping.rb +34 -16
- data/lib/active_record/scoping/default.rb +28 -10
- data/lib/active_record/scoping/named.rb +59 -26
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +3 -5
- data/lib/active_record/statement_cache.rb +17 -15
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +69 -0
- data/lib/active_record/tasks/database_tasks.rb +66 -49
- data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
- data/lib/active_record/tasks/postgresql_database_tasks.rb +12 -3
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +20 -9
- data/lib/active_record/touch_later.rb +63 -0
- data/lib/active_record/transactions.rb +139 -57
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -45
- data/lib/active_record/type/date_time.rb +2 -49
- data/lib/active_record/type/internal/abstract_json.rb +33 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +15 -14
- data/lib/active_record/type/time.rb +10 -16
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +11 -12
- data/lib/active_record/validations/uniqueness.rb +33 -33
- data/lib/rails/generators/active_record/migration.rb +15 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +33 -16
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +58 -34
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decimal_without_scale.rb +0 -11
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/text.rb +0 -11
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/unsigned_integer.rb +0 -15
- data/lib/active_record/type/value.rb +0 -110
@@ -5,7 +5,7 @@ module ActiveRecord
|
|
5
5
|
class DatabaseAlreadyExists < StandardError; end # :nodoc:
|
6
6
|
class DatabaseNotSupported < StandardError; end # :nodoc:
|
7
7
|
|
8
|
-
#
|
8
|
+
# ActiveRecord::Tasks::DatabaseTasks is a utility class, which encapsulates
|
9
9
|
# logic behind common tasks used to manage database and migrations.
|
10
10
|
#
|
11
11
|
# The tasks defined here are used with Rake tasks provided by Active Record.
|
@@ -18,15 +18,15 @@ module ActiveRecord
|
|
18
18
|
#
|
19
19
|
# The possible config values are:
|
20
20
|
#
|
21
|
-
#
|
22
|
-
#
|
23
|
-
#
|
24
|
-
#
|
25
|
-
#
|
26
|
-
#
|
27
|
-
#
|
21
|
+
# * +env+: current environment (like Rails.env).
|
22
|
+
# * +database_configuration+: configuration of your databases (as in +config/database.yml+).
|
23
|
+
# * +db_dir+: your +db+ directory.
|
24
|
+
# * +fixtures_path+: a path to fixtures directory.
|
25
|
+
# * +migrations_paths+: a list of paths to directories with migrations.
|
26
|
+
# * +seed_loader+: an object which will load seeds, it needs to respond to the +load_seed+ method.
|
27
|
+
# * +root+: a path to the root of the application.
|
28
28
|
#
|
29
|
-
# Example usage of
|
29
|
+
# Example usage of DatabaseTasks outside Rails could look as such:
|
30
30
|
#
|
31
31
|
# include ActiveRecord::Tasks
|
32
32
|
# DatabaseTasks.database_configuration = YAML.load_file('my_database_config.yml')
|
@@ -42,6 +42,22 @@ module ActiveRecord
|
|
42
42
|
|
43
43
|
LOCAL_HOSTS = ['127.0.0.1', 'localhost']
|
44
44
|
|
45
|
+
def check_protected_environments!
|
46
|
+
unless ENV['DISABLE_DATABASE_ENVIRONMENT_CHECK']
|
47
|
+
current = ActiveRecord::Migrator.current_environment
|
48
|
+
stored = ActiveRecord::Migrator.last_stored_environment
|
49
|
+
|
50
|
+
if ActiveRecord::Migrator.protected_environment?
|
51
|
+
raise ActiveRecord::ProtectedEnvironmentError.new(stored)
|
52
|
+
end
|
53
|
+
|
54
|
+
if stored && stored != current
|
55
|
+
raise ActiveRecord::EnvironmentMismatchError.new(current: current, stored: stored)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
rescue ActiveRecord::NoDatabaseError
|
59
|
+
end
|
60
|
+
|
45
61
|
def register_task(pattern, task)
|
46
62
|
@tasks ||= {}
|
47
63
|
@tasks[pattern] = task
|
@@ -91,15 +107,21 @@ module ActiveRecord
|
|
91
107
|
def create(*arguments)
|
92
108
|
configuration = arguments.first
|
93
109
|
class_for_adapter(configuration['adapter']).new(*arguments).create
|
110
|
+
$stdout.puts "Created database '#{configuration['database']}'"
|
94
111
|
rescue DatabaseAlreadyExists
|
95
|
-
$stderr.puts "#{configuration['database']} already exists"
|
112
|
+
$stderr.puts "Database '#{configuration['database']}' already exists"
|
96
113
|
rescue Exception => error
|
97
|
-
$stderr.puts error
|
114
|
+
$stderr.puts error
|
98
115
|
$stderr.puts "Couldn't create database for #{configuration.inspect}"
|
116
|
+
raise
|
99
117
|
end
|
100
118
|
|
101
119
|
def create_all
|
120
|
+
old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
|
102
121
|
each_local_configuration { |configuration| create configuration }
|
122
|
+
if old_pool
|
123
|
+
ActiveRecord::Base.connection_handler.establish_connection(old_pool.spec)
|
124
|
+
end
|
103
125
|
end
|
104
126
|
|
105
127
|
def create_current(environment = env)
|
@@ -112,11 +134,13 @@ module ActiveRecord
|
|
112
134
|
def drop(*arguments)
|
113
135
|
configuration = arguments.first
|
114
136
|
class_for_adapter(configuration['adapter']).new(*arguments).drop
|
137
|
+
$stdout.puts "Dropped database '#{configuration['database']}'"
|
115
138
|
rescue ActiveRecord::NoDatabaseError
|
116
139
|
$stderr.puts "Database '#{configuration['database']}' does not exist"
|
117
140
|
rescue Exception => error
|
118
|
-
$stderr.puts error
|
119
|
-
$stderr.puts "Couldn't drop #{configuration['database']}"
|
141
|
+
$stderr.puts error
|
142
|
+
$stderr.puts "Couldn't drop database '#{configuration['database']}'"
|
143
|
+
raise
|
120
144
|
end
|
121
145
|
|
122
146
|
def drop_all
|
@@ -191,59 +215,52 @@ module ActiveRecord
|
|
191
215
|
class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename)
|
192
216
|
end
|
193
217
|
|
194
|
-
def load_schema(format = ActiveRecord::Base.schema_format, file = nil)
|
195
|
-
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
196
|
-
This method will act on a specific connection in the future.
|
197
|
-
To act on the current connection, use `load_schema_current` instead.
|
198
|
-
MSG
|
199
|
-
|
200
|
-
load_schema_current(format, file)
|
201
|
-
end
|
202
|
-
|
203
|
-
def schema_file(format = ActiveRecord::Base.schema_format)
|
204
|
-
case format
|
205
|
-
when :ruby
|
206
|
-
File.join(db_dir, "schema.rb")
|
207
|
-
when :sql
|
208
|
-
File.join(db_dir, "structure.sql")
|
209
|
-
end
|
210
|
-
end
|
211
|
-
|
212
|
-
# This method is the successor of +load_schema+. We should rename it
|
213
|
-
# after +load_schema+ went through a deprecation cycle. (Rails > 4.2)
|
214
|
-
def load_schema_for(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
|
218
|
+
def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env) # :nodoc:
|
215
219
|
file ||= schema_file(format)
|
216
220
|
|
221
|
+
check_schema_file(file)
|
222
|
+
ActiveRecord::Base.establish_connection(configuration)
|
223
|
+
|
217
224
|
case format
|
218
225
|
when :ruby
|
219
|
-
check_schema_file(file)
|
220
|
-
ActiveRecord::Base.establish_connection(configuration)
|
221
226
|
load(file)
|
222
227
|
when :sql
|
223
|
-
check_schema_file(file)
|
224
228
|
structure_load(configuration, file)
|
225
229
|
else
|
226
230
|
raise ArgumentError, "unknown format #{format.inspect}"
|
227
231
|
end
|
232
|
+
ActiveRecord::InternalMetadata.create_table
|
233
|
+
ActiveRecord::InternalMetadata[:environment] = environment
|
234
|
+
end
|
235
|
+
|
236
|
+
def load_schema_for(*args)
|
237
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
238
|
+
This method was renamed to `#load_schema` and will be removed in the future.
|
239
|
+
Use `#load_schema` instead.
|
240
|
+
MSG
|
241
|
+
load_schema(*args)
|
228
242
|
end
|
229
243
|
|
230
|
-
def
|
231
|
-
|
232
|
-
|
244
|
+
def schema_file(format = ActiveRecord::Base.schema_format)
|
245
|
+
case format
|
246
|
+
when :ruby
|
247
|
+
File.join(db_dir, "schema.rb")
|
248
|
+
when :sql
|
249
|
+
File.join(db_dir, "structure.sql")
|
233
250
|
end
|
234
251
|
end
|
235
252
|
|
236
253
|
def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
|
237
|
-
each_current_configuration(environment) { |configuration|
|
238
|
-
|
254
|
+
each_current_configuration(environment) { |configuration, configuration_environment|
|
255
|
+
load_schema configuration, format, file, configuration_environment
|
239
256
|
}
|
240
257
|
ActiveRecord::Base.establish_connection(environment.to_sym)
|
241
258
|
end
|
242
259
|
|
243
260
|
def check_schema_file(filename)
|
244
261
|
unless File.exist?(filename)
|
245
|
-
message = %{#{filename} doesn't exist yet. Run `
|
246
|
-
message << %{ If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.} if defined?(::Rails)
|
262
|
+
message = %{#{filename} doesn't exist yet. Run `rails db:migrate` to create it, then try again.}
|
263
|
+
message << %{ If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.} if defined?(::Rails.root)
|
247
264
|
Kernel.abort message
|
248
265
|
end
|
249
266
|
end
|
@@ -270,12 +287,12 @@ module ActiveRecord
|
|
270
287
|
|
271
288
|
def each_current_configuration(environment)
|
272
289
|
environments = [environment]
|
273
|
-
|
274
|
-
|
290
|
+
environments << 'test' if environment == 'development'
|
291
|
+
|
292
|
+
ActiveRecord::Base.configurations.slice(*environments).each do |configuration_environment, configuration|
|
293
|
+
next unless configuration["database"]
|
275
294
|
|
276
|
-
|
277
|
-
configurations.compact.each do |configuration|
|
278
|
-
yield configuration unless configuration['database'].blank?
|
295
|
+
yield configuration, configuration_environment
|
279
296
|
end
|
280
297
|
end
|
281
298
|
|
@@ -1,8 +1,6 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Tasks # :nodoc:
|
3
3
|
class MySQLDatabaseTasks # :nodoc:
|
4
|
-
DEFAULT_CHARSET = ENV['CHARSET'] || 'utf8'
|
5
|
-
DEFAULT_COLLATION = ENV['COLLATION'] || 'utf8_unicode_ci'
|
6
4
|
ACCESS_DENIED_ERROR = 1045
|
7
5
|
|
8
6
|
delegate :connection, :establish_connection, to: ActiveRecord::Base
|
@@ -23,7 +21,7 @@ module ActiveRecord
|
|
23
21
|
end
|
24
22
|
rescue error_class => error
|
25
23
|
if error.respond_to?(:errno) && error.errno == ACCESS_DENIED_ERROR
|
26
|
-
$stdout.print error.
|
24
|
+
$stdout.print error.message
|
27
25
|
establish_connection root_configuration_without_database
|
28
26
|
connection.create_database configuration['database'], creation_options
|
29
27
|
if configuration['username'] != 'root'
|
@@ -59,6 +57,7 @@ module ActiveRecord
|
|
59
57
|
args = prepare_command_options
|
60
58
|
args.concat(["--result-file", "#{filename}"])
|
61
59
|
args.concat(["--no-data"])
|
60
|
+
args.concat(["--routines"])
|
62
61
|
args.concat(["#{configuration['database']}"])
|
63
62
|
|
64
63
|
run_cmd('mysqldump', args, 'dumping')
|
@@ -86,12 +85,6 @@ module ActiveRecord
|
|
86
85
|
Hash.new.tap do |options|
|
87
86
|
options[:charset] = configuration['encoding'] if configuration.include? 'encoding'
|
88
87
|
options[:collation] = configuration['collation'] if configuration.include? 'collation'
|
89
|
-
|
90
|
-
# Set default charset only when collation isn't set.
|
91
|
-
options[:charset] ||= DEFAULT_CHARSET unless options[:collation]
|
92
|
-
|
93
|
-
# Set default collation only when charset is also default.
|
94
|
-
options[:collation] ||= DEFAULT_COLLATION if options[:charset] == DEFAULT_CHARSET
|
95
88
|
end
|
96
89
|
end
|
97
90
|
|
@@ -101,8 +94,6 @@ module ActiveRecord
|
|
101
94
|
ArJdbcMySQL::Error
|
102
95
|
elsif defined?(Mysql2)
|
103
96
|
Mysql2::Error
|
104
|
-
elsif defined?(Mysql)
|
105
|
-
Mysql::Error
|
106
97
|
else
|
107
98
|
StandardError
|
108
99
|
end
|
@@ -129,7 +120,7 @@ IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
|
|
129
120
|
end
|
130
121
|
|
131
122
|
def prepare_command_options
|
132
|
-
{
|
123
|
+
args = {
|
133
124
|
'host' => '--host',
|
134
125
|
'port' => '--port',
|
135
126
|
'socket' => '--socket',
|
@@ -142,6 +133,8 @@ IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
|
|
142
133
|
'sslcipher' => '--ssl-cipher',
|
143
134
|
'sslkey' => '--ssl-key'
|
144
135
|
}.map { |opt, arg| "#{arg}=#{configuration[opt]}" if configuration[opt] }.compact
|
136
|
+
|
137
|
+
args
|
145
138
|
end
|
146
139
|
|
147
140
|
def run_cmd(cmd, args, action)
|
@@ -149,8 +142,7 @@ IDENTIFIED BY '#{configuration['password']}' WITH GRANT OPTION;
|
|
149
142
|
end
|
150
143
|
|
151
144
|
def run_cmd_error(cmd, args, action)
|
152
|
-
msg = "failed to execute
|
153
|
-
msg << "#{cmd}"
|
145
|
+
msg = "failed to execute: `#{cmd}`\n"
|
154
146
|
msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
|
155
147
|
msg
|
156
148
|
end
|
@@ -16,7 +16,7 @@ module ActiveRecord
|
|
16
16
|
configuration.merge('encoding' => encoding)
|
17
17
|
establish_connection configuration
|
18
18
|
rescue ActiveRecord::StatementInvalid => error
|
19
|
-
if
|
19
|
+
if error.cause.is_a?(PG::DuplicateDatabase)
|
20
20
|
raise DatabaseAlreadyExists
|
21
21
|
else
|
22
22
|
raise
|
@@ -44,8 +44,17 @@ module ActiveRecord
|
|
44
44
|
|
45
45
|
def structure_dump(filename)
|
46
46
|
set_psql_env
|
47
|
+
|
48
|
+
search_path = case ActiveRecord::Base.dump_schemas
|
49
|
+
when :schema_search_path
|
50
|
+
configuration['schema_search_path']
|
51
|
+
when :all
|
52
|
+
nil
|
53
|
+
when String
|
54
|
+
ActiveRecord::Base.dump_schemas
|
55
|
+
end
|
56
|
+
|
47
57
|
args = ['-s', '-x', '-O', '-f', filename]
|
48
|
-
search_path = configuration['schema_search_path']
|
49
58
|
unless search_path.blank?
|
50
59
|
args += search_path.split(',').map do |part|
|
51
60
|
"--schema=#{part.strip}"
|
@@ -59,7 +68,7 @@ module ActiveRecord
|
|
59
68
|
def structure_load(filename)
|
60
69
|
set_psql_env
|
61
70
|
args = [ '-q', '-f', filename, configuration['database'] ]
|
62
|
-
run_cmd('psql', args, 'loading')
|
71
|
+
run_cmd('psql', args, 'loading' )
|
63
72
|
end
|
64
73
|
|
65
74
|
private
|
@@ -19,11 +19,15 @@ module ActiveRecord
|
|
19
19
|
path = Pathname.new configuration['database']
|
20
20
|
file = path.absolute? ? path.to_s : File.join(root, path)
|
21
21
|
|
22
|
-
FileUtils.rm(file)
|
22
|
+
FileUtils.rm(file)
|
23
|
+
rescue Errno::ENOENT => error
|
24
|
+
raise NoDatabaseError.new(error.message, error)
|
23
25
|
end
|
24
26
|
|
25
27
|
def purge
|
26
28
|
drop
|
29
|
+
rescue NoDatabaseError
|
30
|
+
ensure
|
27
31
|
create
|
28
32
|
end
|
29
33
|
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module ActiveRecord
|
2
|
-
# = Active Record Timestamp
|
2
|
+
# = Active Record \Timestamp
|
3
3
|
#
|
4
4
|
# Active Record automatically timestamps create and update operations if the
|
5
5
|
# table has fields named <tt>created_at/created_on</tt> or
|
@@ -15,14 +15,25 @@ module ActiveRecord
|
|
15
15
|
#
|
16
16
|
# == Time Zone aware attributes
|
17
17
|
#
|
18
|
-
#
|
18
|
+
# Active Record keeps all the <tt>datetime</tt> and <tt>time</tt> columns
|
19
|
+
# timezone aware. By default, these values are stored in the database as UTC
|
20
|
+
# and converted back to the current <tt>Time.zone</tt> when pulled from the database.
|
19
21
|
#
|
20
|
-
#
|
22
|
+
# This feature can be turned off completely by setting:
|
21
23
|
#
|
22
|
-
#
|
24
|
+
# config.active_record.time_zone_aware_attributes = false
|
23
25
|
#
|
24
|
-
#
|
25
|
-
#
|
26
|
+
# You can also specify that only <tt>datetime</tt> columns should be time-zone
|
27
|
+
# aware (while <tt>time</tt> should not) by setting:
|
28
|
+
#
|
29
|
+
# ActiveRecord::Base.time_zone_aware_types = [:datetime]
|
30
|
+
#
|
31
|
+
# You can also add database specific timezone aware types. For example, for PostgreSQL:
|
32
|
+
#
|
33
|
+
# ActiveRecord::Base.time_zone_aware_types += [:tsrange, :tstzrange]
|
34
|
+
#
|
35
|
+
# Finally, you can indicate specific attributes of a model for which time zone
|
36
|
+
# conversion should not applied, for instance by setting:
|
26
37
|
#
|
27
38
|
# class Topic < ActiveRecord::Base
|
28
39
|
# self.skip_time_zone_conversion_for_attributes = [:written_on]
|
@@ -57,8 +68,8 @@ module ActiveRecord
|
|
57
68
|
super
|
58
69
|
end
|
59
70
|
|
60
|
-
def _update_record(*args)
|
61
|
-
if should_record_timestamps?
|
71
|
+
def _update_record(*args, touch: true, **options)
|
72
|
+
if touch && should_record_timestamps?
|
62
73
|
current_time = current_time_from_proper_timezone
|
63
74
|
|
64
75
|
timestamp_attributes_for_update_in_model.each do |column|
|
@@ -67,7 +78,7 @@ module ActiveRecord
|
|
67
78
|
write_attribute(column, current_time)
|
68
79
|
end
|
69
80
|
end
|
70
|
-
super
|
81
|
+
super(*args)
|
71
82
|
end
|
72
83
|
|
73
84
|
def should_record_timestamps?
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
# = Active Record Touch Later
|
3
|
+
module TouchLater
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
included do
|
7
|
+
before_commit_without_transaction_enrollment :touch_deferred_attributes
|
8
|
+
end
|
9
|
+
|
10
|
+
def touch_later(*names) # :nodoc:
|
11
|
+
unless persisted?
|
12
|
+
raise ActiveRecordError, <<-MSG.squish
|
13
|
+
cannot touch on a new or destroyed record object. Consider using
|
14
|
+
persisted?, new_record?, or destroyed? before touching
|
15
|
+
MSG
|
16
|
+
end
|
17
|
+
|
18
|
+
@_defer_touch_attrs ||= timestamp_attributes_for_update_in_model
|
19
|
+
@_defer_touch_attrs |= names
|
20
|
+
@_touch_time = current_time_from_proper_timezone
|
21
|
+
|
22
|
+
surreptitiously_touch @_defer_touch_attrs
|
23
|
+
self.class.connection.add_transaction_record self
|
24
|
+
|
25
|
+
# touch the parents as we are not calling the after_save callbacks
|
26
|
+
self.class.reflect_on_all_associations(:belongs_to).each do |r|
|
27
|
+
if touch = r.options[:touch]
|
28
|
+
ActiveRecord::Associations::Builder::BelongsTo.touch_record(self, r.foreign_key, r.name, touch, :touch_later)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def touch(*names, time: nil) # :nodoc:
|
34
|
+
if has_defer_touch_attrs?
|
35
|
+
names |= @_defer_touch_attrs
|
36
|
+
end
|
37
|
+
super(*names, time: time)
|
38
|
+
end
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def surreptitiously_touch(attrs)
|
43
|
+
attrs.each { |attr| write_attribute attr, @_touch_time }
|
44
|
+
clear_attribute_changes attrs
|
45
|
+
end
|
46
|
+
|
47
|
+
def touch_deferred_attributes
|
48
|
+
if has_defer_touch_attrs? && persisted?
|
49
|
+
touch(*@_defer_touch_attrs, time: @_touch_time)
|
50
|
+
@_defer_touch_attrs, @_touch_time = nil, nil
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def has_defer_touch_attrs?
|
55
|
+
defined?(@_defer_touch_attrs) && @_defer_touch_attrs.present?
|
56
|
+
end
|
57
|
+
|
58
|
+
def belongs_to_touch_method
|
59
|
+
:touch_later
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
@@ -4,32 +4,23 @@ module ActiveRecord
|
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
#:nodoc:
|
6
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"
|
17
7
|
|
18
8
|
included do
|
19
9
|
define_callbacks :commit, :rollback,
|
20
|
-
|
10
|
+
:before_commit,
|
11
|
+
:before_commit_without_transaction_enrollment,
|
12
|
+
:commit_without_transaction_enrollment,
|
13
|
+
:rollback_without_transaction_enrollment,
|
14
|
+
terminator: deprecated_false_terminator,
|
21
15
|
scope: [:kind, :name]
|
22
|
-
|
23
|
-
mattr_accessor :raise_in_transactional_callbacks, instance_writer: false
|
24
|
-
self.raise_in_transactional_callbacks = false
|
25
16
|
end
|
26
17
|
|
27
18
|
# = Active Record Transactions
|
28
19
|
#
|
29
|
-
# Transactions are protective blocks where SQL statements are only permanent
|
20
|
+
# \Transactions are protective blocks where SQL statements are only permanent
|
30
21
|
# if they can all succeed as one atomic action. The classic example is a
|
31
22
|
# transfer between two accounts where you can only have a deposit if the
|
32
|
-
# withdrawal succeeded and vice versa. Transactions enforce the integrity of
|
23
|
+
# withdrawal succeeded and vice versa. \Transactions enforce the integrity of
|
33
24
|
# the database and guard the data against program errors or database
|
34
25
|
# break-downs. So basically you should use transaction blocks whenever you
|
35
26
|
# have a number of statements that must be executed together or not at all.
|
@@ -49,20 +40,20 @@ module ActiveRecord
|
|
49
40
|
#
|
50
41
|
# == Different Active Record classes in a single transaction
|
51
42
|
#
|
52
|
-
# Though the transaction class method is called on some Active Record class,
|
43
|
+
# Though the #transaction class method is called on some Active Record class,
|
53
44
|
# the objects within the transaction block need not all be instances of
|
54
45
|
# that class. This is because transactions are per-database connection, not
|
55
46
|
# per-model.
|
56
47
|
#
|
57
48
|
# In this example a +balance+ record is transactionally saved even
|
58
|
-
# though
|
49
|
+
# though #transaction is called on the +Account+ class:
|
59
50
|
#
|
60
51
|
# Account.transaction do
|
61
52
|
# balance.save!
|
62
53
|
# account.save!
|
63
54
|
# end
|
64
55
|
#
|
65
|
-
# The
|
56
|
+
# The #transaction method is also available as a model instance method.
|
66
57
|
# For example, you can also do this:
|
67
58
|
#
|
68
59
|
# balance.transaction do
|
@@ -89,7 +80,8 @@ module ActiveRecord
|
|
89
80
|
#
|
90
81
|
# == +save+ and +destroy+ are automatically wrapped in a transaction
|
91
82
|
#
|
92
|
-
# Both
|
83
|
+
# Both {#save}[rdoc-ref:Persistence#save] and
|
84
|
+
# {#destroy}[rdoc-ref:Persistence#destroy] come wrapped in a transaction that ensures
|
93
85
|
# that whatever you do in validations or callbacks will happen under its
|
94
86
|
# protected cover. So you can use validations to check for values that
|
95
87
|
# the transaction depends on or you can raise exceptions in the callbacks
|
@@ -98,7 +90,7 @@ module ActiveRecord
|
|
98
90
|
# As a consequence changes to the database are not seen outside your connection
|
99
91
|
# until the operation is complete. For example, if you try to update the index
|
100
92
|
# of a search engine in +after_save+ the indexer won't see the updated record.
|
101
|
-
# The
|
93
|
+
# The #after_commit callback is the only one that is triggered once the update
|
102
94
|
# is committed. See below.
|
103
95
|
#
|
104
96
|
# == Exception handling and rolling back
|
@@ -107,11 +99,11 @@ module ActiveRecord
|
|
107
99
|
# be propagated (after triggering the ROLLBACK), so you should be ready to
|
108
100
|
# catch those in your application code.
|
109
101
|
#
|
110
|
-
# One exception is the
|
102
|
+
# One exception is the ActiveRecord::Rollback exception, which will trigger
|
111
103
|
# a ROLLBACK when raised, but not be re-raised by the transaction block.
|
112
104
|
#
|
113
|
-
# *Warning*: one should not catch
|
114
|
-
# inside a transaction block.
|
105
|
+
# *Warning*: one should not catch ActiveRecord::StatementInvalid exceptions
|
106
|
+
# inside a transaction block. ActiveRecord::StatementInvalid exceptions indicate that an
|
115
107
|
# error occurred at the database level, for example when a unique constraint
|
116
108
|
# is violated. On some database systems, such as PostgreSQL, database errors
|
117
109
|
# inside a transaction cause the entire transaction to become unusable
|
@@ -132,16 +124,16 @@ module ActiveRecord
|
|
132
124
|
# # statement will cause a PostgreSQL error, even though the unique
|
133
125
|
# # constraint is no longer violated:
|
134
126
|
# Number.create(i: 1)
|
135
|
-
# # => "
|
127
|
+
# # => "PG::Error: ERROR: current transaction is aborted, commands
|
136
128
|
# # ignored until end of transaction block"
|
137
129
|
# end
|
138
130
|
#
|
139
131
|
# One should restart the entire transaction if an
|
140
|
-
#
|
132
|
+
# ActiveRecord::StatementInvalid occurred.
|
141
133
|
#
|
142
134
|
# == Nested transactions
|
143
135
|
#
|
144
|
-
#
|
136
|
+
# #transaction calls can be nested. By default, this makes all database
|
145
137
|
# statements in the nested transaction block become part of the parent
|
146
138
|
# transaction. For example, the following behavior may be surprising:
|
147
139
|
#
|
@@ -153,7 +145,7 @@ module ActiveRecord
|
|
153
145
|
# end
|
154
146
|
# end
|
155
147
|
#
|
156
|
-
# creates both "Kotori" and "Nemu". Reason is the
|
148
|
+
# creates both "Kotori" and "Nemu". Reason is the ActiveRecord::Rollback
|
157
149
|
# exception in the nested block does not issue a ROLLBACK. Since these exceptions
|
158
150
|
# are captured in transaction blocks, the parent block does not see it and the
|
159
151
|
# real transaction is committed.
|
@@ -177,22 +169,22 @@ module ActiveRecord
|
|
177
169
|
# writing, the only database that we're aware of that supports true nested
|
178
170
|
# transactions, is MS-SQL. Because of this, Active Record emulates nested
|
179
171
|
# transactions by using savepoints on MySQL and PostgreSQL. See
|
180
|
-
# http://dev.mysql.com/doc/refman/5.
|
172
|
+
# http://dev.mysql.com/doc/refman/5.7/en/savepoint.html
|
181
173
|
# for more information about savepoints.
|
182
174
|
#
|
183
|
-
# === Callbacks
|
175
|
+
# === \Callbacks
|
184
176
|
#
|
185
177
|
# There are two types of callbacks associated with committing and rolling back transactions:
|
186
|
-
#
|
178
|
+
# #after_commit and #after_rollback.
|
187
179
|
#
|
188
|
-
#
|
189
|
-
# transaction immediately after the transaction is committed.
|
180
|
+
# #after_commit callbacks are called on every record saved or destroyed within a
|
181
|
+
# transaction immediately after the transaction is committed. #after_rollback callbacks
|
190
182
|
# are called on every record saved or destroyed within a transaction immediately after the
|
191
183
|
# transaction or savepoint is rolled back.
|
192
184
|
#
|
193
185
|
# These callbacks are useful for interacting with other systems since you will be guaranteed
|
194
186
|
# that the callback is only executed when the database is in a permanent state. For example,
|
195
|
-
#
|
187
|
+
# #after_commit is a good spot to put in a hook to clearing a cache since clearing it from
|
196
188
|
# within a transaction could trigger the cache to be regenerated before the database is updated.
|
197
189
|
#
|
198
190
|
# === Caveats
|
@@ -206,20 +198,24 @@ module ActiveRecord
|
|
206
198
|
# automatically released. The following example demonstrates the problem:
|
207
199
|
#
|
208
200
|
# Model.connection.transaction do # BEGIN
|
209
|
-
# Model.connection.transaction(requires_new: true) do
|
201
|
+
# Model.connection.transaction(requires_new: true) do # CREATE SAVEPOINT active_record_1
|
210
202
|
# Model.connection.create_table(...) # active_record_1 now automatically released
|
211
|
-
# end # RELEASE
|
203
|
+
# end # RELEASE SAVEPOINT active_record_1
|
212
204
|
# # ^^^^ BOOM! database error!
|
213
205
|
# end
|
214
206
|
#
|
215
207
|
# Note that "TRUNCATE" is also a MySQL DDL statement!
|
216
208
|
module ClassMethods
|
217
|
-
# See
|
209
|
+
# See the ConnectionAdapters::DatabaseStatements#transaction API docs.
|
218
210
|
def transaction(options = {}, &block)
|
219
|
-
# See the ConnectionAdapters::DatabaseStatements#transaction API docs.
|
220
211
|
connection.transaction(options, &block)
|
221
212
|
end
|
222
213
|
|
214
|
+
def before_commit(*args, &block) # :nodoc:
|
215
|
+
set_options_for_callbacks!(args)
|
216
|
+
set_callback(:before_commit, :before, *args, &block)
|
217
|
+
end
|
218
|
+
|
223
219
|
# This callback is called after a record has been created, updated, or destroyed.
|
224
220
|
#
|
225
221
|
# You can specify that the callback should only be fired by a certain action with
|
@@ -232,32 +228,69 @@ module ActiveRecord
|
|
232
228
|
# after_commit :do_foo_bar, on: [:create, :update]
|
233
229
|
# after_commit :do_bar_baz, on: [:update, :destroy]
|
234
230
|
#
|
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.
|
237
231
|
def after_commit(*args, &block)
|
238
232
|
set_options_for_callbacks!(args)
|
239
233
|
set_callback(:commit, :after, *args, &block)
|
240
|
-
|
241
|
-
|
242
|
-
|
234
|
+
end
|
235
|
+
|
236
|
+
# Shortcut for <tt>after_commit :hook, on: :create</tt>.
|
237
|
+
def after_create_commit(*args, &block)
|
238
|
+
set_options_for_callbacks!(args, on: :create)
|
239
|
+
set_callback(:commit, :after, *args, &block)
|
240
|
+
end
|
241
|
+
|
242
|
+
# Shortcut for <tt>after_commit :hook, on: :update</tt>.
|
243
|
+
def after_update_commit(*args, &block)
|
244
|
+
set_options_for_callbacks!(args, on: :update)
|
245
|
+
set_callback(:commit, :after, *args, &block)
|
246
|
+
end
|
247
|
+
|
248
|
+
# Shortcut for <tt>after_commit :hook, on: :destroy</tt>.
|
249
|
+
def after_destroy_commit(*args, &block)
|
250
|
+
set_options_for_callbacks!(args, on: :destroy)
|
251
|
+
set_callback(:commit, :after, *args, &block)
|
243
252
|
end
|
244
253
|
|
245
254
|
# This callback is called after a create, update, or destroy are rolled back.
|
246
255
|
#
|
247
|
-
# Please check the documentation of
|
256
|
+
# Please check the documentation of #after_commit for options.
|
248
257
|
def after_rollback(*args, &block)
|
249
258
|
set_options_for_callbacks!(args)
|
250
259
|
set_callback(:rollback, :after, *args, &block)
|
251
|
-
|
252
|
-
|
253
|
-
|
260
|
+
end
|
261
|
+
|
262
|
+
def before_commit_without_transaction_enrollment(*args, &block) # :nodoc:
|
263
|
+
set_options_for_callbacks!(args)
|
264
|
+
set_callback(:before_commit_without_transaction_enrollment, :before, *args, &block)
|
265
|
+
end
|
266
|
+
|
267
|
+
def after_commit_without_transaction_enrollment(*args, &block) # :nodoc:
|
268
|
+
set_options_for_callbacks!(args)
|
269
|
+
set_callback(:commit_without_transaction_enrollment, :after, *args, &block)
|
270
|
+
end
|
271
|
+
|
272
|
+
def after_rollback_without_transaction_enrollment(*args, &block) # :nodoc:
|
273
|
+
set_options_for_callbacks!(args)
|
274
|
+
set_callback(:rollback_without_transaction_enrollment, :after, *args, &block)
|
275
|
+
end
|
276
|
+
|
277
|
+
def raise_in_transactional_callbacks
|
278
|
+
ActiveSupport::Deprecation.warn('ActiveRecord::Base.raise_in_transactional_callbacks is deprecated and will be removed without replacement.')
|
279
|
+
true
|
280
|
+
end
|
281
|
+
|
282
|
+
def raise_in_transactional_callbacks=(value)
|
283
|
+
ActiveSupport::Deprecation.warn('ActiveRecord::Base.raise_in_transactional_callbacks= is deprecated, has no effect and will be removed without replacement.')
|
284
|
+
value
|
254
285
|
end
|
255
286
|
|
256
287
|
private
|
257
288
|
|
258
|
-
def set_options_for_callbacks!(args)
|
259
|
-
options = args.
|
260
|
-
|
289
|
+
def set_options_for_callbacks!(args, enforced_options = {})
|
290
|
+
options = args.extract_options!.merge!(enforced_options)
|
291
|
+
args << options
|
292
|
+
|
293
|
+
if options[:on]
|
261
294
|
fire_on = Array(options[:on])
|
262
295
|
assert_valid_transaction_action(fire_on)
|
263
296
|
options[:if] = Array(options[:if])
|
@@ -306,26 +339,37 @@ module ActiveRecord
|
|
306
339
|
clear_transaction_record_state
|
307
340
|
end
|
308
341
|
|
309
|
-
|
342
|
+
def before_committed! # :nodoc:
|
343
|
+
_run_before_commit_without_transaction_enrollment_callbacks
|
344
|
+
_run_before_commit_callbacks
|
345
|
+
end
|
346
|
+
|
347
|
+
# Call the #after_commit callbacks.
|
310
348
|
#
|
311
349
|
# Ensure that it is not called if the object was never persisted (failed create),
|
312
350
|
# but call it after the commit of a destroyed object.
|
313
|
-
def committed!(should_run_callbacks
|
314
|
-
|
351
|
+
def committed!(should_run_callbacks: true) #:nodoc:
|
352
|
+
if should_run_callbacks && destroyed? || persisted?
|
353
|
+
_run_commit_without_transaction_enrollment_callbacks
|
354
|
+
_run_commit_callbacks
|
355
|
+
end
|
315
356
|
ensure
|
316
357
|
force_clear_transaction_record_state
|
317
358
|
end
|
318
359
|
|
319
|
-
# Call the
|
360
|
+
# Call the #after_rollback callbacks. The +force_restore_state+ argument indicates if the record
|
320
361
|
# state should be rolled back to the beginning or just to the last savepoint.
|
321
|
-
def rolledback!(force_restore_state
|
322
|
-
|
362
|
+
def rolledback!(force_restore_state: false, should_run_callbacks: true) #:nodoc:
|
363
|
+
if should_run_callbacks
|
364
|
+
_run_rollback_callbacks
|
365
|
+
_run_rollback_without_transaction_enrollment_callbacks
|
366
|
+
end
|
323
367
|
ensure
|
324
368
|
restore_transaction_record_state(force_restore_state)
|
325
369
|
clear_transaction_record_state
|
326
370
|
end
|
327
371
|
|
328
|
-
# Add the record to the current transaction so that the
|
372
|
+
# Add the record to the current transaction so that the #after_rollback and #after_commit callbacks
|
329
373
|
# can be called.
|
330
374
|
def add_to_transaction
|
331
375
|
if has_transactional_callbacks?
|
@@ -423,5 +467,43 @@ module ActiveRecord
|
|
423
467
|
end
|
424
468
|
end
|
425
469
|
end
|
470
|
+
|
471
|
+
private
|
472
|
+
|
473
|
+
def set_transaction_state(state) # :nodoc:
|
474
|
+
@transaction_state = state
|
475
|
+
end
|
476
|
+
|
477
|
+
def has_transactional_callbacks? # :nodoc:
|
478
|
+
!_rollback_callbacks.empty? || !_commit_callbacks.empty? || !_before_commit_callbacks.empty?
|
479
|
+
end
|
480
|
+
|
481
|
+
# Updates the attributes on this particular Active Record object so that
|
482
|
+
# if it's associated with a transaction, then the state of the Active Record
|
483
|
+
# object will be updated to reflect the current state of the transaction
|
484
|
+
#
|
485
|
+
# The +@transaction_state+ variable stores the states of the associated
|
486
|
+
# transaction. This relies on the fact that a transaction can only be in
|
487
|
+
# one rollback or commit (otherwise a list of states would be required)
|
488
|
+
# Each Active Record object inside of a transaction carries that transaction's
|
489
|
+
# TransactionState.
|
490
|
+
#
|
491
|
+
# This method checks to see if the ActiveRecord object's state reflects
|
492
|
+
# the TransactionState, and rolls back or commits the Active Record object
|
493
|
+
# as appropriate.
|
494
|
+
#
|
495
|
+
# Since Active Record objects can be inside multiple transactions, this
|
496
|
+
# method recursively goes through the parent of the TransactionState and
|
497
|
+
# checks if the Active Record object reflects the state of the object.
|
498
|
+
def sync_with_transaction_state
|
499
|
+
update_attributes_from_transaction_state(@transaction_state)
|
500
|
+
end
|
501
|
+
|
502
|
+
def update_attributes_from_transaction_state(transaction_state)
|
503
|
+
if transaction_state && transaction_state.finalized?
|
504
|
+
restore_transaction_record_state if transaction_state.rolledback?
|
505
|
+
clear_transaction_record_state
|
506
|
+
end
|
507
|
+
end
|
426
508
|
end
|
427
509
|
end
|