activerecord 5.2.4.rc1 → 6.0.0.rc2
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 +597 -586
- data/MIT-LICENSE +3 -1
- data/README.rdoc +4 -2
- data/examples/performance.rb +1 -1
- data/lib/active_record.rb +9 -2
- data/lib/active_record/aggregations.rb +4 -2
- data/lib/active_record/associations.rb +19 -14
- data/lib/active_record/associations/association.rb +52 -19
- data/lib/active_record/associations/association_scope.rb +4 -6
- data/lib/active_record/associations/belongs_to_association.rb +36 -42
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
- data/lib/active_record/associations/builder/association.rb +14 -18
- data/lib/active_record/associations/builder/belongs_to.rb +19 -52
- data/lib/active_record/associations/builder/collection_association.rb +3 -13
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
- data/lib/active_record/associations/builder/has_many.rb +2 -0
- data/lib/active_record/associations/builder/has_one.rb +35 -1
- data/lib/active_record/associations/builder/singular_association.rb +2 -0
- data/lib/active_record/associations/collection_association.rb +6 -21
- data/lib/active_record/associations/collection_proxy.rb +12 -15
- data/lib/active_record/associations/foreign_association.rb +7 -0
- data/lib/active_record/associations/has_many_association.rb +2 -10
- data/lib/active_record/associations/has_many_through_association.rb +14 -14
- data/lib/active_record/associations/has_one_association.rb +28 -30
- data/lib/active_record/associations/has_one_through_association.rb +5 -5
- data/lib/active_record/associations/join_dependency.rb +24 -28
- data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +40 -32
- data/lib/active_record/associations/preloader/association.rb +38 -36
- data/lib/active_record/associations/preloader/through_association.rb +48 -39
- data/lib/active_record/associations/singular_association.rb +2 -16
- data/lib/active_record/attribute_assignment.rb +7 -10
- data/lib/active_record/attribute_methods.rb +28 -100
- data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
- data/lib/active_record/attribute_methods/dirty.rb +111 -40
- data/lib/active_record/attribute_methods/primary_key.rb +15 -22
- data/lib/active_record/attribute_methods/query.rb +2 -3
- data/lib/active_record/attribute_methods/read.rb +15 -53
- data/lib/active_record/attribute_methods/serialization.rb +1 -1
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
- data/lib/active_record/attribute_methods/write.rb +17 -24
- data/lib/active_record/attributes.rb +13 -0
- data/lib/active_record/autosave_association.rb +5 -9
- data/lib/active_record/base.rb +2 -3
- data/lib/active_record/callbacks.rb +5 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +89 -16
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +95 -123
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +133 -54
- data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
- data/lib/active_record/connection_adapters/abstract_adapter.rb +172 -41
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +128 -194
- data/lib/active_record/connection_adapters/column.rb +17 -13
- data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +73 -13
- data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +24 -7
- data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -1
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +160 -74
- data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +125 -141
- data/lib/active_record/connection_handling.rb +149 -27
- data/lib/active_record/core.rb +100 -60
- data/lib/active_record/counter_cache.rb +4 -29
- data/lib/active_record/database_configurations.rb +204 -0
- data/lib/active_record/database_configurations/database_config.rb +37 -0
- data/lib/active_record/database_configurations/hash_config.rb +50 -0
- data/lib/active_record/database_configurations/url_config.rb +79 -0
- data/lib/active_record/dynamic_matchers.rb +1 -1
- data/lib/active_record/enum.rb +28 -7
- data/lib/active_record/errors.rb +15 -7
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixture_set/model_metadata.rb +33 -0
- data/lib/active_record/fixture_set/render_context.rb +17 -0
- data/lib/active_record/fixture_set/table_row.rb +153 -0
- data/lib/active_record/fixture_set/table_rows.rb +47 -0
- data/lib/active_record/fixtures.rb +140 -472
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +13 -3
- data/lib/active_record/insert_all.rb +180 -0
- data/lib/active_record/integration.rb +68 -16
- data/lib/active_record/internal_metadata.rb +10 -2
- data/lib/active_record/locking/optimistic.rb +5 -6
- data/lib/active_record/locking/pessimistic.rb +3 -3
- data/lib/active_record/log_subscriber.rb +7 -26
- data/lib/active_record/middleware/database_selector.rb +75 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +90 -0
- data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
- data/lib/active_record/migration.rb +87 -76
- data/lib/active_record/migration/command_recorder.rb +50 -6
- data/lib/active_record/migration/compatibility.rb +76 -49
- data/lib/active_record/model_schema.rb +30 -9
- data/lib/active_record/nested_attributes.rb +2 -2
- data/lib/active_record/no_touching.rb +7 -0
- data/lib/active_record/persistence.rb +228 -24
- data/lib/active_record/query_cache.rb +11 -4
- data/lib/active_record/querying.rb +32 -20
- data/lib/active_record/railtie.rb +80 -43
- data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
- data/lib/active_record/railties/controller_runtime.rb +30 -35
- data/lib/active_record/railties/databases.rake +193 -46
- data/lib/active_record/reflection.rb +32 -30
- data/lib/active_record/relation.rb +310 -80
- data/lib/active_record/relation/batches.rb +13 -10
- data/lib/active_record/relation/calculations.rb +53 -45
- data/lib/active_record/relation/delegation.rb +26 -43
- data/lib/active_record/relation/finder_methods.rb +13 -26
- data/lib/active_record/relation/merger.rb +11 -20
- data/lib/active_record/relation/predicate_builder.rb +4 -6
- data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
- data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
- data/lib/active_record/relation/query_attribute.rb +13 -8
- data/lib/active_record/relation/query_methods.rb +165 -53
- data/lib/active_record/relation/spawn_methods.rb +1 -1
- data/lib/active_record/relation/where_clause.rb +14 -10
- data/lib/active_record/relation/where_clause_factory.rb +1 -2
- data/lib/active_record/result.rb +30 -11
- data/lib/active_record/sanitization.rb +32 -40
- data/lib/active_record/schema.rb +2 -11
- data/lib/active_record/schema_dumper.rb +22 -7
- data/lib/active_record/schema_migration.rb +5 -1
- data/lib/active_record/scoping.rb +8 -8
- data/lib/active_record/scoping/default.rb +4 -5
- data/lib/active_record/scoping/named.rb +19 -15
- data/lib/active_record/statement_cache.rb +30 -3
- data/lib/active_record/store.rb +87 -8
- data/lib/active_record/table_metadata.rb +10 -17
- data/lib/active_record/tasks/database_tasks.rb +159 -25
- data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
- data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
- data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
- data/lib/active_record/test_databases.rb +38 -0
- data/lib/active_record/test_fixtures.rb +224 -0
- data/lib/active_record/timestamp.rb +39 -25
- data/lib/active_record/touch_later.rb +4 -2
- data/lib/active_record/transactions.rb +57 -66
- data/lib/active_record/translation.rb +1 -1
- data/lib/active_record/type.rb +3 -4
- data/lib/active_record/type/adapter_specific_registry.rb +1 -8
- data/lib/active_record/type_caster/connection.rb +15 -14
- data/lib/active_record/type_caster/map.rb +1 -4
- data/lib/active_record/validations/uniqueness.rb +15 -27
- data/lib/arel.rb +51 -0
- data/lib/arel/alias_predication.rb +9 -0
- data/lib/arel/attributes.rb +22 -0
- data/lib/arel/attributes/attribute.rb +37 -0
- data/lib/arel/collectors/bind.rb +24 -0
- data/lib/arel/collectors/composite.rb +31 -0
- data/lib/arel/collectors/plain_string.rb +20 -0
- data/lib/arel/collectors/sql_string.rb +20 -0
- data/lib/arel/collectors/substitute_binds.rb +28 -0
- data/lib/arel/crud.rb +42 -0
- data/lib/arel/delete_manager.rb +18 -0
- data/lib/arel/errors.rb +9 -0
- data/lib/arel/expressions.rb +29 -0
- data/lib/arel/factory_methods.rb +49 -0
- data/lib/arel/insert_manager.rb +49 -0
- data/lib/arel/math.rb +45 -0
- data/lib/arel/nodes.rb +68 -0
- data/lib/arel/nodes/and.rb +32 -0
- data/lib/arel/nodes/ascending.rb +23 -0
- data/lib/arel/nodes/binary.rb +52 -0
- data/lib/arel/nodes/bind_param.rb +36 -0
- data/lib/arel/nodes/case.rb +55 -0
- data/lib/arel/nodes/casted.rb +50 -0
- data/lib/arel/nodes/comment.rb +29 -0
- data/lib/arel/nodes/count.rb +12 -0
- data/lib/arel/nodes/delete_statement.rb +45 -0
- data/lib/arel/nodes/descending.rb +23 -0
- data/lib/arel/nodes/equality.rb +18 -0
- data/lib/arel/nodes/extract.rb +24 -0
- data/lib/arel/nodes/false.rb +16 -0
- data/lib/arel/nodes/full_outer_join.rb +8 -0
- data/lib/arel/nodes/function.rb +44 -0
- data/lib/arel/nodes/grouping.rb +8 -0
- data/lib/arel/nodes/in.rb +8 -0
- data/lib/arel/nodes/infix_operation.rb +80 -0
- data/lib/arel/nodes/inner_join.rb +8 -0
- data/lib/arel/nodes/insert_statement.rb +37 -0
- data/lib/arel/nodes/join_source.rb +20 -0
- data/lib/arel/nodes/matches.rb +18 -0
- data/lib/arel/nodes/named_function.rb +23 -0
- data/lib/arel/nodes/node.rb +50 -0
- data/lib/arel/nodes/node_expression.rb +13 -0
- data/lib/arel/nodes/outer_join.rb +8 -0
- data/lib/arel/nodes/over.rb +15 -0
- data/lib/arel/nodes/regexp.rb +16 -0
- data/lib/arel/nodes/right_outer_join.rb +8 -0
- data/lib/arel/nodes/select_core.rb +67 -0
- data/lib/arel/nodes/select_statement.rb +41 -0
- data/lib/arel/nodes/sql_literal.rb +16 -0
- data/lib/arel/nodes/string_join.rb +11 -0
- data/lib/arel/nodes/table_alias.rb +27 -0
- data/lib/arel/nodes/terminal.rb +16 -0
- data/lib/arel/nodes/true.rb +16 -0
- data/lib/arel/nodes/unary.rb +45 -0
- data/lib/arel/nodes/unary_operation.rb +20 -0
- data/lib/arel/nodes/unqualified_column.rb +22 -0
- data/lib/arel/nodes/update_statement.rb +41 -0
- data/lib/arel/nodes/values_list.rb +9 -0
- data/lib/arel/nodes/window.rb +126 -0
- data/lib/arel/nodes/with.rb +11 -0
- data/lib/arel/order_predications.rb +13 -0
- data/lib/arel/predications.rb +257 -0
- data/lib/arel/select_manager.rb +271 -0
- data/lib/arel/table.rb +110 -0
- data/lib/arel/tree_manager.rb +72 -0
- data/lib/arel/update_manager.rb +34 -0
- data/lib/arel/visitors.rb +20 -0
- data/lib/arel/visitors/depth_first.rb +204 -0
- data/lib/arel/visitors/dot.rb +297 -0
- data/lib/arel/visitors/ibm_db.rb +34 -0
- data/lib/arel/visitors/informix.rb +62 -0
- data/lib/arel/visitors/mssql.rb +157 -0
- data/lib/arel/visitors/mysql.rb +83 -0
- data/lib/arel/visitors/oracle.rb +159 -0
- data/lib/arel/visitors/oracle12.rb +66 -0
- data/lib/arel/visitors/postgresql.rb +110 -0
- data/lib/arel/visitors/sqlite.rb +39 -0
- data/lib/arel/visitors/to_sql.rb +889 -0
- data/lib/arel/visitors/visitor.rb +46 -0
- data/lib/arel/visitors/where_sql.rb +23 -0
- data/lib/arel/window_predications.rb +9 -0
- data/lib/rails/generators/active_record/migration.rb +14 -1
- data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
- data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
- data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
- metadata +108 -26
- data/lib/active_record/collection_cache_key.rb +0 -53
@@ -44,7 +44,7 @@ module ActiveRecord
|
|
44
44
|
def initialize(values)
|
45
45
|
@values = values
|
46
46
|
@indexes = values.each_with_index.find_all { |thing, i|
|
47
|
-
|
47
|
+
Substitute === thing
|
48
48
|
}.map(&:last)
|
49
49
|
end
|
50
50
|
|
@@ -56,6 +56,28 @@ module ActiveRecord
|
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
+
class PartialQueryCollector
|
60
|
+
def initialize
|
61
|
+
@parts = []
|
62
|
+
@binds = []
|
63
|
+
end
|
64
|
+
|
65
|
+
def <<(str)
|
66
|
+
@parts << str
|
67
|
+
self
|
68
|
+
end
|
69
|
+
|
70
|
+
def add_bind(obj)
|
71
|
+
@binds << obj
|
72
|
+
@parts << Substitute.new
|
73
|
+
self
|
74
|
+
end
|
75
|
+
|
76
|
+
def value
|
77
|
+
[@parts, @binds]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
59
81
|
def self.query(sql)
|
60
82
|
Query.new(sql)
|
61
83
|
end
|
@@ -64,6 +86,10 @@ module ActiveRecord
|
|
64
86
|
PartialQuery.new(values)
|
65
87
|
end
|
66
88
|
|
89
|
+
def self.partial_query_collector
|
90
|
+
PartialQueryCollector.new
|
91
|
+
end
|
92
|
+
|
67
93
|
class Params # :nodoc:
|
68
94
|
def bind; Substitute.new; end
|
69
95
|
end
|
@@ -106,6 +132,8 @@ module ActiveRecord
|
|
106
132
|
sql = query_builder.sql_for bind_values, connection
|
107
133
|
|
108
134
|
klass.find_by_sql(sql, bind_values, preparable: true, &block)
|
135
|
+
rescue ::RangeError
|
136
|
+
nil
|
109
137
|
end
|
110
138
|
|
111
139
|
def self.unsupported_value?(value)
|
@@ -114,8 +142,7 @@ module ActiveRecord
|
|
114
142
|
end
|
115
143
|
end
|
116
144
|
|
117
|
-
|
118
|
-
|
145
|
+
private
|
119
146
|
attr_reader :query_builder, :bind_map, :klass
|
120
147
|
end
|
121
148
|
end
|
data/lib/active_record/store.rb
CHANGED
@@ -11,14 +11,20 @@ module ActiveRecord
|
|
11
11
|
# of the model. This is very helpful for easily exposing store keys to a form or elsewhere that's
|
12
12
|
# already built around just accessing attributes on the model.
|
13
13
|
#
|
14
|
+
# Every accessor comes with dirty tracking methods (+key_changed?+, +key_was+ and +key_change+) and
|
15
|
+
# methods to access the changes made during the last save (+saved_change_to_key?+, +saved_change_to_key+ and
|
16
|
+
# +key_before_last_save+).
|
17
|
+
#
|
18
|
+
# NOTE: There is no +key_will_change!+ method for accessors, use +store_will_change!+ instead.
|
19
|
+
#
|
14
20
|
# Make sure that you declare the database column used for the serialized store as a text, so there's
|
15
21
|
# plenty of room.
|
16
22
|
#
|
17
23
|
# You can set custom coder to encode/decode your serialized attributes to/from different formats.
|
18
24
|
# JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
|
19
25
|
#
|
20
|
-
# NOTE: If you are using
|
21
|
-
# the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
|
26
|
+
# NOTE: If you are using structured database data types (eg. PostgreSQL +hstore+/+json+, or MySQL 5.7+
|
27
|
+
# +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
|
22
28
|
# Simply use {.store_accessor}[rdoc-ref:ClassMethods#store_accessor] instead to generate
|
23
29
|
# the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
|
24
30
|
# using a symbol.
|
@@ -31,24 +37,40 @@ module ActiveRecord
|
|
31
37
|
#
|
32
38
|
# class User < ActiveRecord::Base
|
33
39
|
# store :settings, accessors: [ :color, :homepage ], coder: JSON
|
40
|
+
# store :parent, accessors: [ :name ], coder: JSON, prefix: true
|
41
|
+
# store :spouse, accessors: [ :name ], coder: JSON, prefix: :partner
|
42
|
+
# store :settings, accessors: [ :two_factor_auth ], suffix: true
|
43
|
+
# store :settings, accessors: [ :login_retry ], suffix: :config
|
34
44
|
# end
|
35
45
|
#
|
36
|
-
# u = User.new(color: 'black', homepage: '37signals.com')
|
46
|
+
# u = User.new(color: 'black', homepage: '37signals.com', parent_name: 'Mary', partner_name: 'Lily')
|
37
47
|
# u.color # Accessor stored attribute
|
48
|
+
# u.parent_name # Accessor stored attribute with prefix
|
49
|
+
# u.partner_name # Accessor stored attribute with custom prefix
|
50
|
+
# u.two_factor_auth_settings # Accessor stored attribute with suffix
|
51
|
+
# u.login_retry_config # Accessor stored attribute with custom suffix
|
38
52
|
# u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
|
39
53
|
#
|
40
54
|
# # There is no difference between strings and symbols for accessing custom attributes
|
41
55
|
# u.settings[:country] # => 'Denmark'
|
42
56
|
# u.settings['country'] # => 'Denmark'
|
43
57
|
#
|
58
|
+
# # Dirty tracking
|
59
|
+
# u.color = 'green'
|
60
|
+
# u.color_changed? # => true
|
61
|
+
# u.color_was # => 'black'
|
62
|
+
# u.color_change # => ['black', 'red']
|
63
|
+
#
|
44
64
|
# # Add additional accessors to an existing store through store_accessor
|
45
65
|
# class SuperUser < User
|
46
66
|
# store_accessor :settings, :privileges, :servants
|
67
|
+
# store_accessor :parent, :birthday, prefix: true
|
68
|
+
# store_accessor :settings, :secret_question, suffix: :config
|
47
69
|
# end
|
48
70
|
#
|
49
71
|
# The stored attribute names can be retrieved using {.stored_attributes}[rdoc-ref:rdoc-ref:ClassMethods#stored_attributes].
|
50
72
|
#
|
51
|
-
# User.stored_attributes[:settings] # [:color, :homepage]
|
73
|
+
# User.stored_attributes[:settings] # [:color, :homepage, :two_factor_auth, :login_retry]
|
52
74
|
#
|
53
75
|
# == Overwriting default accessors
|
54
76
|
#
|
@@ -81,21 +103,78 @@ module ActiveRecord
|
|
81
103
|
module ClassMethods
|
82
104
|
def store(store_attribute, options = {})
|
83
105
|
serialize store_attribute, IndifferentCoder.new(store_attribute, options[:coder])
|
84
|
-
store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
|
106
|
+
store_accessor(store_attribute, options[:accessors], options.slice(:prefix, :suffix)) if options.has_key? :accessors
|
85
107
|
end
|
86
108
|
|
87
|
-
def store_accessor(store_attribute, *keys)
|
109
|
+
def store_accessor(store_attribute, *keys, prefix: nil, suffix: nil)
|
88
110
|
keys = keys.flatten
|
89
111
|
|
112
|
+
accessor_prefix =
|
113
|
+
case prefix
|
114
|
+
when String, Symbol
|
115
|
+
"#{prefix}_"
|
116
|
+
when TrueClass
|
117
|
+
"#{store_attribute}_"
|
118
|
+
else
|
119
|
+
""
|
120
|
+
end
|
121
|
+
accessor_suffix =
|
122
|
+
case suffix
|
123
|
+
when String, Symbol
|
124
|
+
"_#{suffix}"
|
125
|
+
when TrueClass
|
126
|
+
"_#{store_attribute}"
|
127
|
+
else
|
128
|
+
""
|
129
|
+
end
|
130
|
+
|
90
131
|
_store_accessors_module.module_eval do
|
91
132
|
keys.each do |key|
|
92
|
-
|
133
|
+
accessor_key = "#{accessor_prefix}#{key}#{accessor_suffix}"
|
134
|
+
|
135
|
+
define_method("#{accessor_key}=") do |value|
|
93
136
|
write_store_attribute(store_attribute, key, value)
|
94
137
|
end
|
95
138
|
|
96
|
-
define_method(
|
139
|
+
define_method(accessor_key) do
|
97
140
|
read_store_attribute(store_attribute, key)
|
98
141
|
end
|
142
|
+
|
143
|
+
define_method("#{accessor_key}_changed?") do
|
144
|
+
return false unless attribute_changed?(store_attribute)
|
145
|
+
prev_store, new_store = changes[store_attribute]
|
146
|
+
prev_store&.dig(key) != new_store&.dig(key)
|
147
|
+
end
|
148
|
+
|
149
|
+
define_method("#{accessor_key}_change") do
|
150
|
+
return unless attribute_changed?(store_attribute)
|
151
|
+
prev_store, new_store = changes[store_attribute]
|
152
|
+
[prev_store&.dig(key), new_store&.dig(key)]
|
153
|
+
end
|
154
|
+
|
155
|
+
define_method("#{accessor_key}_was") do
|
156
|
+
return unless attribute_changed?(store_attribute)
|
157
|
+
prev_store, _new_store = changes[store_attribute]
|
158
|
+
prev_store&.dig(key)
|
159
|
+
end
|
160
|
+
|
161
|
+
define_method("saved_change_to_#{accessor_key}?") do
|
162
|
+
return false unless saved_change_to_attribute?(store_attribute)
|
163
|
+
prev_store, new_store = saved_change_to_attribute(store_attribute)
|
164
|
+
prev_store&.dig(key) != new_store&.dig(key)
|
165
|
+
end
|
166
|
+
|
167
|
+
define_method("saved_change_to_#{accessor_key}") do
|
168
|
+
return unless saved_change_to_attribute?(store_attribute)
|
169
|
+
prev_store, new_store = saved_change_to_attribute(store_attribute)
|
170
|
+
[prev_store&.dig(key), new_store&.dig(key)]
|
171
|
+
end
|
172
|
+
|
173
|
+
define_method("#{accessor_key}_before_last_save") do
|
174
|
+
return unless saved_change_to_attribute?(store_attribute)
|
175
|
+
prev_store, _new_store = saved_change_to_attribute(store_attribute)
|
176
|
+
prev_store&.dig(key)
|
177
|
+
end
|
99
178
|
end
|
100
179
|
end
|
101
180
|
|
@@ -4,17 +4,18 @@ module ActiveRecord
|
|
4
4
|
class TableMetadata # :nodoc:
|
5
5
|
delegate :foreign_type, :foreign_key, :join_primary_key, :join_foreign_key, to: :association, prefix: true
|
6
6
|
|
7
|
-
def initialize(klass, arel_table, association = nil)
|
7
|
+
def initialize(klass, arel_table, association = nil, types = klass)
|
8
8
|
@klass = klass
|
9
|
+
@types = types
|
9
10
|
@arel_table = arel_table
|
10
11
|
@association = association
|
11
12
|
end
|
12
13
|
|
13
14
|
def resolve_column_aliases(hash)
|
14
15
|
new_hash = hash.dup
|
15
|
-
hash.
|
16
|
-
if
|
17
|
-
new_hash[
|
16
|
+
hash.each_key do |key|
|
17
|
+
if key.is_a?(Symbol) && new_key = klass.attribute_aliases[key.to_s]
|
18
|
+
new_hash[new_key] = new_hash.delete(key)
|
18
19
|
end
|
19
20
|
end
|
20
21
|
new_hash
|
@@ -29,11 +30,7 @@ module ActiveRecord
|
|
29
30
|
end
|
30
31
|
|
31
32
|
def type(column_name)
|
32
|
-
|
33
|
-
klass.type_for_attribute(column_name)
|
34
|
-
else
|
35
|
-
Type.default_value
|
36
|
-
end
|
33
|
+
types.type_for_attribute(column_name)
|
37
34
|
end
|
38
35
|
|
39
36
|
def has_column?(column_name)
|
@@ -52,13 +49,12 @@ module ActiveRecord
|
|
52
49
|
elsif association && !association.polymorphic?
|
53
50
|
association_klass = association.klass
|
54
51
|
arel_table = association_klass.arel_table.alias(table_name)
|
52
|
+
TableMetadata.new(association_klass, arel_table, association)
|
55
53
|
else
|
56
54
|
type_caster = TypeCaster::Connection.new(klass, table_name)
|
57
|
-
association_klass = nil
|
58
55
|
arel_table = Arel::Table.new(table_name, type_caster: type_caster)
|
56
|
+
TableMetadata.new(nil, arel_table, association, type_caster)
|
59
57
|
end
|
60
|
-
|
61
|
-
TableMetadata.new(association_klass, arel_table, association)
|
62
58
|
end
|
63
59
|
|
64
60
|
def polymorphic_association?
|
@@ -73,10 +69,7 @@ module ActiveRecord
|
|
73
69
|
klass.reflect_on_aggregation(aggregation_name)
|
74
70
|
end
|
75
71
|
|
76
|
-
|
77
|
-
|
78
|
-
protected
|
79
|
-
|
80
|
-
attr_reader :klass, :arel_table, :association
|
72
|
+
private
|
73
|
+
attr_reader :klass, :types, :arel_table, :association
|
81
74
|
end
|
82
75
|
end
|
@@ -1,5 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_record/database_configurations"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module Tasks # :nodoc:
|
5
7
|
class DatabaseAlreadyExists < StandardError; end # :nodoc:
|
@@ -8,7 +10,7 @@ module ActiveRecord
|
|
8
10
|
# ActiveRecord::Tasks::DatabaseTasks is a utility class, which encapsulates
|
9
11
|
# logic behind common tasks used to manage database and migrations.
|
10
12
|
#
|
11
|
-
# The tasks defined here are used with
|
13
|
+
# The tasks defined here are used with Rails commands provided by Active Record.
|
12
14
|
#
|
13
15
|
# In order to use DatabaseTasks, a few config values need to be set. All the needed
|
14
16
|
# config values are set by Rails already, so it's necessary to do it only if you
|
@@ -101,25 +103,30 @@ module ActiveRecord
|
|
101
103
|
@env ||= Rails.env
|
102
104
|
end
|
103
105
|
|
106
|
+
def spec
|
107
|
+
@spec ||= "primary"
|
108
|
+
end
|
109
|
+
|
104
110
|
def seed_loader
|
105
111
|
@seed_loader ||= Rails.application
|
106
112
|
end
|
107
113
|
|
108
114
|
def current_config(options = {})
|
109
115
|
options.reverse_merge! env: env
|
116
|
+
options[:spec] ||= "primary"
|
110
117
|
if options.has_key?(:config)
|
111
118
|
@current_config = options[:config]
|
112
119
|
else
|
113
|
-
@current_config ||= ActiveRecord::Base.configurations
|
120
|
+
@current_config ||= ActiveRecord::Base.configurations.configs_for(env_name: options[:env], spec_name: options[:spec]).config
|
114
121
|
end
|
115
122
|
end
|
116
123
|
|
117
124
|
def create(*arguments)
|
118
125
|
configuration = arguments.first
|
119
126
|
class_for_adapter(configuration["adapter"]).new(*arguments).create
|
120
|
-
$stdout.puts "Created database '#{configuration['database']}'"
|
127
|
+
$stdout.puts "Created database '#{configuration['database']}'" if verbose?
|
121
128
|
rescue DatabaseAlreadyExists
|
122
|
-
$stderr.puts "Database '#{configuration['database']}' already exists"
|
129
|
+
$stderr.puts "Database '#{configuration['database']}' already exists" if verbose?
|
123
130
|
rescue Exception => error
|
124
131
|
$stderr.puts error
|
125
132
|
$stderr.puts "Couldn't create '#{configuration['database']}' database. Please check your configuration."
|
@@ -134,8 +141,45 @@ module ActiveRecord
|
|
134
141
|
end
|
135
142
|
end
|
136
143
|
|
137
|
-
def
|
138
|
-
|
144
|
+
def setup_initial_database_yaml
|
145
|
+
return {} unless defined?(Rails)
|
146
|
+
|
147
|
+
begin
|
148
|
+
Rails.application.config.load_database_yaml
|
149
|
+
rescue
|
150
|
+
$stderr.puts "Rails couldn't infer whether you are using multiple databases from your database.yml and can't generate the tasks for the non-primary databases. If you'd like to use this feature, please simplify your ERB."
|
151
|
+
|
152
|
+
{}
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def for_each(databases)
|
157
|
+
database_configs = ActiveRecord::DatabaseConfigurations.new(databases).configs_for(env_name: Rails.env)
|
158
|
+
|
159
|
+
# if this is a single database application we don't want tasks for each primary database
|
160
|
+
return if database_configs.count == 1
|
161
|
+
|
162
|
+
database_configs.each do |db_config|
|
163
|
+
yield db_config.spec_name
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
def raise_for_multi_db(environment = env, command:)
|
168
|
+
db_configs = ActiveRecord::Base.configurations.configs_for(env_name: environment)
|
169
|
+
|
170
|
+
if db_configs.count > 1
|
171
|
+
dbs_list = []
|
172
|
+
|
173
|
+
db_configs.each do |db|
|
174
|
+
dbs_list << "#{command}:#{db.spec_name}"
|
175
|
+
end
|
176
|
+
|
177
|
+
raise "You're using a multiple database application. To use `#{command}` you must run the namespaced task with a VERSION. Available tasks are #{dbs_list.to_sentence}."
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def create_current(environment = env, spec_name = nil)
|
182
|
+
each_current_configuration(environment, spec_name) { |configuration|
|
139
183
|
create configuration
|
140
184
|
}
|
141
185
|
ActiveRecord::Base.establish_connection(environment.to_sym)
|
@@ -144,7 +188,7 @@ module ActiveRecord
|
|
144
188
|
def drop(*arguments)
|
145
189
|
configuration = arguments.first
|
146
190
|
class_for_adapter(configuration["adapter"]).new(*arguments).drop
|
147
|
-
$stdout.puts "Dropped database '#{configuration['database']}'"
|
191
|
+
$stdout.puts "Dropped database '#{configuration['database']}'" if verbose?
|
148
192
|
rescue ActiveRecord::NoDatabaseError
|
149
193
|
$stderr.puts "Database '#{configuration['database']}' does not exist"
|
150
194
|
rescue Exception => error
|
@@ -163,20 +207,56 @@ module ActiveRecord
|
|
163
207
|
}
|
164
208
|
end
|
165
209
|
|
210
|
+
def truncate_tables(configuration)
|
211
|
+
ActiveRecord::Base.connected_to(database: { truncation: configuration }) do
|
212
|
+
conn = ActiveRecord::Base.connection
|
213
|
+
table_names = conn.tables
|
214
|
+
table_names -= [
|
215
|
+
conn.schema_migration.table_name,
|
216
|
+
InternalMetadata.table_name
|
217
|
+
]
|
218
|
+
|
219
|
+
ActiveRecord::Base.connection.truncate_tables(*table_names)
|
220
|
+
end
|
221
|
+
end
|
222
|
+
private :truncate_tables
|
223
|
+
|
224
|
+
def truncate_all(environment = env)
|
225
|
+
ActiveRecord::Base.configurations.configs_for(env_name: environment).each do |db_config|
|
226
|
+
truncate_tables db_config.config
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
166
230
|
def migrate
|
167
231
|
check_target_version
|
168
232
|
|
169
|
-
verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] != "false" : true
|
170
233
|
scope = ENV["SCOPE"]
|
171
|
-
verbose_was, Migration.verbose = Migration.verbose, verbose
|
234
|
+
verbose_was, Migration.verbose = Migration.verbose, verbose?
|
235
|
+
|
172
236
|
Base.connection.migration_context.migrate(target_version) do |migration|
|
173
237
|
scope.blank? || scope == migration.scope
|
174
238
|
end
|
239
|
+
|
175
240
|
ActiveRecord::Base.clear_cache!
|
176
241
|
ensure
|
177
242
|
Migration.verbose = verbose_was
|
178
243
|
end
|
179
244
|
|
245
|
+
def migrate_status
|
246
|
+
unless ActiveRecord::Base.connection.schema_migration.table_exists?
|
247
|
+
Kernel.abort "Schema migrations table does not exist yet."
|
248
|
+
end
|
249
|
+
|
250
|
+
# output
|
251
|
+
puts "\ndatabase: #{ActiveRecord::Base.connection_config[:database]}\n\n"
|
252
|
+
puts "#{'Status'.center(8)} #{'Migration ID'.ljust(14)} Migration Name"
|
253
|
+
puts "-" * 50
|
254
|
+
ActiveRecord::Base.connection.migration_context.migrations_status.each do |status, version, name|
|
255
|
+
puts "#{status.center(8)} #{version.ljust(14)} #{name}"
|
256
|
+
end
|
257
|
+
puts
|
258
|
+
end
|
259
|
+
|
180
260
|
def check_target_version
|
181
261
|
if target_version && !(Migration::MigrationFilenameRegexp.match?(ENV["VERSION"]) || /\A\d+\z/.match?(ENV["VERSION"]))
|
182
262
|
raise "Invalid format of target version: `VERSION=#{ENV['VERSION']}`"
|
@@ -187,8 +267,8 @@ module ActiveRecord
|
|
187
267
|
ENV["VERSION"].to_i if ENV["VERSION"] && !ENV["VERSION"].empty?
|
188
268
|
end
|
189
269
|
|
190
|
-
def charset_current(environment = env)
|
191
|
-
charset ActiveRecord::Base.configurations
|
270
|
+
def charset_current(environment = env, specification_name = spec)
|
271
|
+
charset ActiveRecord::Base.configurations.configs_for(env_name: environment, spec_name: specification_name).config
|
192
272
|
end
|
193
273
|
|
194
274
|
def charset(*arguments)
|
@@ -196,8 +276,8 @@ module ActiveRecord
|
|
196
276
|
class_for_adapter(configuration["adapter"]).new(*arguments).charset
|
197
277
|
end
|
198
278
|
|
199
|
-
def collation_current(environment = env)
|
200
|
-
collation ActiveRecord::Base.configurations
|
279
|
+
def collation_current(environment = env, specification_name = spec)
|
280
|
+
collation ActiveRecord::Base.configurations.configs_for(env_name: environment, spec_name: specification_name).config
|
201
281
|
end
|
202
282
|
|
203
283
|
def collation(*arguments)
|
@@ -234,9 +314,10 @@ module ActiveRecord
|
|
234
314
|
class_for_adapter(configuration["adapter"]).new(*arguments).structure_load(filename, structure_load_flags)
|
235
315
|
end
|
236
316
|
|
237
|
-
def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env) # :nodoc:
|
238
|
-
file ||=
|
317
|
+
def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env, spec_name = "primary") # :nodoc:
|
318
|
+
file ||= dump_filename(spec_name, format)
|
239
319
|
|
320
|
+
verbose_was, Migration.verbose = Migration.verbose, verbose? && ENV["VERBOSE"]
|
240
321
|
check_schema_file(file)
|
241
322
|
ActiveRecord::Base.establish_connection(configuration)
|
242
323
|
|
@@ -250,27 +331,74 @@ module ActiveRecord
|
|
250
331
|
end
|
251
332
|
ActiveRecord::InternalMetadata.create_table
|
252
333
|
ActiveRecord::InternalMetadata[:environment] = environment
|
334
|
+
ensure
|
335
|
+
Migration.verbose = verbose_was
|
336
|
+
end
|
337
|
+
|
338
|
+
def dump_schema(configuration, format = ActiveRecord::Base.schema_format, spec_name = "primary") # :nodoc:
|
339
|
+
require "active_record/schema_dumper"
|
340
|
+
filename = dump_filename(spec_name, format)
|
341
|
+
connection = ActiveRecord::Base.connection
|
342
|
+
|
343
|
+
case format
|
344
|
+
when :ruby
|
345
|
+
File.open(filename, "w:utf-8") do |file|
|
346
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
|
347
|
+
end
|
348
|
+
when :sql
|
349
|
+
structure_dump(configuration, filename)
|
350
|
+
if connection.schema_migration.table_exists?
|
351
|
+
File.open(filename, "a") do |f|
|
352
|
+
f.puts connection.dump_schema_information
|
353
|
+
f.print "\n"
|
354
|
+
end
|
355
|
+
end
|
356
|
+
end
|
253
357
|
end
|
254
358
|
|
255
359
|
def schema_file(format = ActiveRecord::Base.schema_format)
|
360
|
+
File.join(db_dir, schema_file_type(format))
|
361
|
+
end
|
362
|
+
|
363
|
+
def schema_file_type(format = ActiveRecord::Base.schema_format)
|
256
364
|
case format
|
257
365
|
when :ruby
|
258
|
-
|
366
|
+
"schema.rb"
|
259
367
|
when :sql
|
260
|
-
|
368
|
+
"structure.sql"
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def dump_filename(namespace, format = ActiveRecord::Base.schema_format)
|
373
|
+
filename = if namespace == "primary"
|
374
|
+
schema_file_type(format)
|
375
|
+
else
|
376
|
+
"#{namespace}_#{schema_file_type(format)}"
|
261
377
|
end
|
378
|
+
|
379
|
+
ENV["SCHEMA"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
|
380
|
+
end
|
381
|
+
|
382
|
+
def cache_dump_filename(namespace)
|
383
|
+
filename = if namespace == "primary"
|
384
|
+
"schema_cache.yml"
|
385
|
+
else
|
386
|
+
"#{namespace}_schema_cache.yml"
|
387
|
+
end
|
388
|
+
|
389
|
+
ENV["SCHEMA_CACHE"] || File.join(ActiveRecord::Tasks::DatabaseTasks.db_dir, filename)
|
262
390
|
end
|
263
391
|
|
264
392
|
def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
|
265
|
-
each_current_configuration(environment) { |configuration,
|
266
|
-
load_schema
|
393
|
+
each_current_configuration(environment) { |configuration, spec_name, env|
|
394
|
+
load_schema(configuration, format, file, env, spec_name)
|
267
395
|
}
|
268
396
|
ActiveRecord::Base.establish_connection(environment.to_sym)
|
269
397
|
end
|
270
398
|
|
271
399
|
def check_schema_file(filename)
|
272
400
|
unless File.exist?(filename)
|
273
|
-
message =
|
401
|
+
message = +%{#{filename} doesn't exist yet. Run `rails db:migrate` to create it, then try again.}
|
274
402
|
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)
|
275
403
|
Kernel.abort message
|
276
404
|
end
|
@@ -297,6 +425,9 @@ module ActiveRecord
|
|
297
425
|
end
|
298
426
|
|
299
427
|
private
|
428
|
+
def verbose?
|
429
|
+
ENV["VERBOSE"] ? ENV["VERBOSE"] != "false" : true
|
430
|
+
end
|
300
431
|
|
301
432
|
def class_for_adapter(adapter)
|
302
433
|
_key, task = @tasks.each_pair.detect { |pattern, _task| adapter[pattern] }
|
@@ -306,19 +437,22 @@ module ActiveRecord
|
|
306
437
|
task.is_a?(String) ? task.constantize : task
|
307
438
|
end
|
308
439
|
|
309
|
-
def each_current_configuration(environment)
|
440
|
+
def each_current_configuration(environment, spec_name = nil)
|
310
441
|
environments = [environment]
|
311
442
|
environments << "test" if environment == "development"
|
312
443
|
|
313
|
-
|
314
|
-
|
444
|
+
environments.each do |env|
|
445
|
+
ActiveRecord::Base.configurations.configs_for(env_name: env).each do |db_config|
|
446
|
+
next if spec_name && spec_name != db_config.spec_name
|
315
447
|
|
316
|
-
|
448
|
+
yield db_config.config, db_config.spec_name, env
|
449
|
+
end
|
317
450
|
end
|
318
451
|
end
|
319
452
|
|
320
453
|
def each_local_configuration
|
321
|
-
ActiveRecord::Base.configurations.
|
454
|
+
ActiveRecord::Base.configurations.configs_for.each do |db_config|
|
455
|
+
configuration = db_config.config
|
322
456
|
next unless configuration["database"]
|
323
457
|
|
324
458
|
if local_database?(configuration)
|