activerecord 6.0.5.1 → 6.1.7.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1163 -774
- data/MIT-LICENSE +1 -1
- data/README.rdoc +2 -2
- data/lib/active_record/aggregations.rb +5 -5
- data/lib/active_record/association_relation.rb +30 -12
- data/lib/active_record/associations/alias_tracker.rb +19 -15
- data/lib/active_record/associations/association.rb +49 -26
- data/lib/active_record/associations/association_scope.rb +18 -20
- data/lib/active_record/associations/belongs_to_association.rb +23 -10
- data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
- data/lib/active_record/associations/builder/association.rb +32 -5
- data/lib/active_record/associations/builder/belongs_to.rb +10 -7
- data/lib/active_record/associations/builder/collection_association.rb +5 -4
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
- data/lib/active_record/associations/builder/has_many.rb +6 -2
- data/lib/active_record/associations/builder/has_one.rb +11 -14
- data/lib/active_record/associations/builder/singular_association.rb +1 -1
- data/lib/active_record/associations/collection_association.rb +32 -18
- data/lib/active_record/associations/collection_proxy.rb +12 -5
- data/lib/active_record/associations/foreign_association.rb +13 -0
- data/lib/active_record/associations/has_many_association.rb +24 -2
- data/lib/active_record/associations/has_many_through_association.rb +10 -4
- data/lib/active_record/associations/has_one_association.rb +15 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +37 -21
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +63 -49
- data/lib/active_record/associations/preloader/association.rb +14 -8
- data/lib/active_record/associations/preloader/through_association.rb +1 -1
- data/lib/active_record/associations/preloader.rb +5 -3
- data/lib/active_record/associations/singular_association.rb +1 -1
- data/lib/active_record/associations.rb +118 -11
- data/lib/active_record/attribute_assignment.rb +10 -8
- data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
- data/lib/active_record/attribute_methods/dirty.rb +1 -11
- data/lib/active_record/attribute_methods/primary_key.rb +6 -2
- data/lib/active_record/attribute_methods/query.rb +3 -6
- data/lib/active_record/attribute_methods/read.rb +8 -11
- data/lib/active_record/attribute_methods/serialization.rb +11 -5
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
- data/lib/active_record/attribute_methods/write.rb +12 -20
- data/lib/active_record/attribute_methods.rb +64 -54
- data/lib/active_record/attributes.rb +33 -8
- data/lib/active_record/autosave_association.rb +47 -30
- data/lib/active_record/base.rb +2 -14
- data/lib/active_record/callbacks.rb +152 -22
- data/lib/active_record/coders/yaml_column.rb +16 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -23
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -8
- data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -35
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +114 -26
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +228 -83
- data/lib/active_record/connection_adapters/abstract/transaction.rb +92 -33
- data/lib/active_record/connection_adapters/abstract_adapter.rb +52 -76
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
- data/lib/active_record/connection_adapters/column.rb +15 -1
- data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
- data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +24 -24
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -4
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
- data/lib/active_record/connection_adapters/pool_config.rb +73 -0
- data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +14 -53
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
- data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
- data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +30 -4
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -64
- data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
- data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +32 -5
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
- data/lib/active_record/connection_adapters.rb +52 -0
- data/lib/active_record/connection_handling.rb +218 -71
- data/lib/active_record/core.rb +265 -64
- data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
- data/lib/active_record/database_configurations/database_config.rb +52 -9
- data/lib/active_record/database_configurations/hash_config.rb +54 -8
- data/lib/active_record/database_configurations/url_config.rb +15 -40
- data/lib/active_record/database_configurations.rb +125 -85
- data/lib/active_record/delegated_type.rb +209 -0
- data/lib/active_record/destroy_association_async_job.rb +36 -0
- data/lib/active_record/enum.rb +69 -34
- data/lib/active_record/errors.rb +47 -12
- data/lib/active_record/explain.rb +9 -4
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +10 -17
- data/lib/active_record/fixture_set/model_metadata.rb +1 -2
- data/lib/active_record/fixture_set/render_context.rb +1 -1
- data/lib/active_record/fixture_set/table_row.rb +2 -2
- data/lib/active_record/fixtures.rb +58 -9
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +40 -18
- data/lib/active_record/insert_all.rb +38 -5
- data/lib/active_record/integration.rb +3 -5
- data/lib/active_record/internal_metadata.rb +18 -7
- data/lib/active_record/legacy_yaml_adapter.rb +7 -3
- data/lib/active_record/locking/optimistic.rb +24 -17
- data/lib/active_record/locking/pessimistic.rb +6 -2
- data/lib/active_record/log_subscriber.rb +27 -8
- data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
- data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
- data/lib/active_record/middleware/database_selector.rb +4 -1
- data/lib/active_record/migration/command_recorder.rb +47 -27
- data/lib/active_record/migration/compatibility.rb +72 -18
- data/lib/active_record/migration.rb +114 -84
- data/lib/active_record/model_schema.rb +89 -14
- data/lib/active_record/nested_attributes.rb +2 -3
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +50 -45
- data/lib/active_record/query_cache.rb +15 -5
- data/lib/active_record/querying.rb +11 -6
- data/lib/active_record/railtie.rb +61 -59
- data/lib/active_record/railties/console_sandbox.rb +2 -4
- data/lib/active_record/railties/databases.rake +279 -101
- data/lib/active_record/readonly_attributes.rb +4 -0
- data/lib/active_record/reflection.rb +60 -44
- data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
- data/lib/active_record/relation/batches.rb +38 -31
- data/lib/active_record/relation/calculations.rb +104 -43
- data/lib/active_record/relation/finder_methods.rb +44 -14
- data/lib/active_record/relation/from_clause.rb +1 -1
- data/lib/active_record/relation/merger.rb +20 -23
- data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
- data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
- data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +61 -38
- data/lib/active_record/relation/query_methods.rb +324 -196
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +8 -7
- data/lib/active_record/relation/where_clause.rb +111 -61
- data/lib/active_record/relation.rb +100 -81
- data/lib/active_record/result.rb +41 -33
- data/lib/active_record/runtime_registry.rb +2 -2
- data/lib/active_record/sanitization.rb +6 -17
- data/lib/active_record/schema_dumper.rb +34 -4
- data/lib/active_record/schema_migration.rb +2 -8
- data/lib/active_record/scoping/default.rb +1 -3
- data/lib/active_record/scoping/named.rb +1 -17
- data/lib/active_record/secure_token.rb +16 -8
- data/lib/active_record/serialization.rb +5 -3
- data/lib/active_record/signed_id.rb +116 -0
- data/lib/active_record/statement_cache.rb +20 -4
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +2 -2
- data/lib/active_record/table_metadata.rb +42 -51
- data/lib/active_record/tasks/database_tasks.rb +140 -113
- data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
- data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
- data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
- data/lib/active_record/test_databases.rb +5 -4
- data/lib/active_record/test_fixtures.rb +79 -31
- data/lib/active_record/timestamp.rb +4 -6
- data/lib/active_record/touch_later.rb +21 -21
- data/lib/active_record/transactions.rb +19 -66
- data/lib/active_record/type/serialized.rb +6 -2
- data/lib/active_record/type.rb +8 -1
- data/lib/active_record/type_caster/connection.rb +0 -1
- data/lib/active_record/type_caster/map.rb +8 -5
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/numericality.rb +35 -0
- data/lib/active_record/validations/uniqueness.rb +24 -4
- data/lib/active_record/validations.rb +1 -0
- data/lib/active_record.rb +7 -14
- data/lib/arel/attributes/attribute.rb +4 -0
- data/lib/arel/collectors/bind.rb +5 -0
- data/lib/arel/collectors/composite.rb +8 -0
- data/lib/arel/collectors/sql_string.rb +7 -0
- data/lib/arel/collectors/substitute_binds.rb +7 -0
- data/lib/arel/nodes/binary.rb +82 -8
- data/lib/arel/nodes/bind_param.rb +8 -0
- data/lib/arel/nodes/casted.rb +21 -9
- data/lib/arel/nodes/equality.rb +6 -9
- data/lib/arel/nodes/grouping.rb +3 -0
- data/lib/arel/nodes/homogeneous_in.rb +76 -0
- data/lib/arel/nodes/in.rb +8 -1
- data/lib/arel/nodes/infix_operation.rb +13 -1
- data/lib/arel/nodes/join_source.rb +1 -1
- data/lib/arel/nodes/node.rb +7 -6
- data/lib/arel/nodes/ordering.rb +27 -0
- data/lib/arel/nodes/sql_literal.rb +3 -0
- data/lib/arel/nodes/table_alias.rb +7 -3
- data/lib/arel/nodes/unary.rb +0 -1
- data/lib/arel/nodes.rb +3 -1
- data/lib/arel/predications.rb +12 -18
- data/lib/arel/select_manager.rb +1 -2
- data/lib/arel/table.rb +13 -5
- data/lib/arel/visitors/dot.rb +14 -2
- data/lib/arel/visitors/mysql.rb +11 -1
- data/lib/arel/visitors/postgresql.rb +15 -4
- data/lib/arel/visitors/to_sql.rb +89 -78
- data/lib/arel/visitors.rb +0 -7
- data/lib/arel.rb +5 -13
- data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
- data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
- data/lib/rails/generators/active_record/migration.rb +6 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
- data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
- metadata +24 -25
- data/lib/active_record/advisory_lock_base.rb +0 -18
- data/lib/active_record/attribute_decorators.rb +0 -88
- data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
- data/lib/active_record/define_callbacks.rb +0 -22
- data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
- data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
- data/lib/active_record/relation/where_clause_factory.rb +0 -33
- data/lib/arel/attributes.rb +0 -22
- data/lib/arel/visitors/depth_first.rb +0 -203
- data/lib/arel/visitors/ibm_db.rb +0 -34
- data/lib/arel/visitors/informix.rb +0 -62
- data/lib/arel/visitors/mssql.rb +0 -156
- data/lib/arel/visitors/oracle.rb +0 -158
- data/lib/arel/visitors/oracle12.rb +0 -65
- data/lib/arel/visitors/where_sql.rb +0 -22
@@ -89,7 +89,7 @@ module ActiveRecord
|
|
89
89
|
# This method is useful in validations and before callbacks to determine
|
90
90
|
# if the next call to +save+ will change a particular attribute. It can be
|
91
91
|
# invoked as +will_save_change_to_name?+ instead of
|
92
|
-
# <tt>will_save_change_to_attribute("name")</tt>.
|
92
|
+
# <tt>will_save_change_to_attribute?("name")</tt>.
|
93
93
|
#
|
94
94
|
# ==== Options
|
95
95
|
#
|
@@ -156,16 +156,6 @@ module ActiveRecord
|
|
156
156
|
end
|
157
157
|
|
158
158
|
private
|
159
|
-
def mutations_from_database
|
160
|
-
sync_with_transaction_state if @transaction_state&.finalized?
|
161
|
-
super
|
162
|
-
end
|
163
|
-
|
164
|
-
def mutations_before_last_save
|
165
|
-
sync_with_transaction_state if @transaction_state&.finalized?
|
166
|
-
super
|
167
|
-
end
|
168
|
-
|
169
159
|
def write_attribute_without_type_cast(attr_name, value)
|
170
160
|
result = super
|
171
161
|
clear_attribute_change(attr_name)
|
@@ -31,7 +31,7 @@ module ActiveRecord
|
|
31
31
|
|
32
32
|
# Returns the primary key column's value before type cast.
|
33
33
|
def id_before_type_cast
|
34
|
-
|
34
|
+
attribute_before_type_cast(@primary_key)
|
35
35
|
end
|
36
36
|
|
37
37
|
# Returns the primary key column's previous value.
|
@@ -44,13 +44,17 @@ module ActiveRecord
|
|
44
44
|
attribute_in_database(@primary_key)
|
45
45
|
end
|
46
46
|
|
47
|
+
def id_for_database # :nodoc:
|
48
|
+
@attributes[@primary_key].value_for_database
|
49
|
+
end
|
50
|
+
|
47
51
|
private
|
48
52
|
def attribute_method?(attr_name)
|
49
53
|
attr_name == "id" || super
|
50
54
|
end
|
51
55
|
|
52
56
|
module ClassMethods
|
53
|
-
ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was id_in_database).to_set
|
57
|
+
ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was id_in_database id_for_database).to_set
|
54
58
|
|
55
59
|
def instance_method_already_implemented?(method_name)
|
56
60
|
super || primary_key && ID_ATTRIBUTE_METHODS.include?(method_name)
|
@@ -17,7 +17,7 @@ module ActiveRecord
|
|
17
17
|
when false, nil then false
|
18
18
|
else
|
19
19
|
if !type_for_attribute(attr_name) { false }
|
20
|
-
if Numeric === value || value
|
20
|
+
if Numeric === value || !value.match?(/[^0-9]/)
|
21
21
|
!value.to_i.zero?
|
22
22
|
else
|
23
23
|
return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value)
|
@@ -31,11 +31,8 @@ module ActiveRecord
|
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
def attribute?(attribute_name)
|
37
|
-
query_attribute(attribute_name)
|
38
|
-
end
|
34
|
+
alias :attribute? :query_attribute
|
35
|
+
private :attribute?
|
39
36
|
end
|
40
37
|
end
|
41
38
|
end
|
@@ -7,16 +7,14 @@ module ActiveRecord
|
|
7
7
|
|
8
8
|
module ClassMethods # :nodoc:
|
9
9
|
private
|
10
|
-
def define_method_attribute(name)
|
10
|
+
def define_method_attribute(name, owner:)
|
11
11
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
12
|
-
|
12
|
+
owner, name
|
13
13
|
) do |temp_method_name, attr_name_expr|
|
14
|
-
|
15
|
-
def #{temp_method_name}
|
16
|
-
|
17
|
-
|
18
|
-
end
|
19
|
-
RUBY
|
14
|
+
owner <<
|
15
|
+
"def #{temp_method_name}" <<
|
16
|
+
" _read_attribute(#{attr_name_expr}) { |n| missing_attribute(n, caller) }" <<
|
17
|
+
"end"
|
20
18
|
end
|
21
19
|
end
|
22
20
|
end
|
@@ -29,14 +27,13 @@ module ActiveRecord
|
|
29
27
|
name = self.class.attribute_aliases[name] || name
|
30
28
|
|
31
29
|
name = @primary_key if name == "id" && @primary_key
|
32
|
-
|
30
|
+
@attributes.fetch_value(name, &block)
|
33
31
|
end
|
34
32
|
|
35
33
|
# This method exists to avoid the expensive primary_key check internally, without
|
36
34
|
# breaking compatibility with the read_attribute API
|
37
35
|
def _read_attribute(attr_name, &block) # :nodoc
|
38
|
-
|
39
|
-
@attributes.fetch_value(attr_name.to_s, &block)
|
36
|
+
@attributes.fetch_value(attr_name, &block)
|
40
37
|
end
|
41
38
|
|
42
39
|
alias :attribute :_read_attribute
|
@@ -41,6 +41,12 @@ module ActiveRecord
|
|
41
41
|
# * +class_name_or_coder+ - Optional, a coder object, which responds to +.load+ and +.dump+
|
42
42
|
# or a class name that the object type should be equal to.
|
43
43
|
#
|
44
|
+
# ==== Options
|
45
|
+
#
|
46
|
+
# +default+ The default value to use when no value is provided. If this option
|
47
|
+
# is not passed, the previous default value (if any) will be used.
|
48
|
+
# Otherwise, the default will be +nil+.
|
49
|
+
#
|
44
50
|
# ==== Example
|
45
51
|
#
|
46
52
|
# # Serialize a preferences attribute.
|
@@ -57,7 +63,7 @@ module ActiveRecord
|
|
57
63
|
# class User < ActiveRecord::Base
|
58
64
|
# serialize :preferences, Hash
|
59
65
|
# end
|
60
|
-
def serialize(attr_name, class_name_or_coder = Object)
|
66
|
+
def serialize(attr_name, class_name_or_coder = Object, **options)
|
61
67
|
# When ::JSON is used, force it to go through the Active Support JSON encoder
|
62
68
|
# to ensure special objects (e.g. Active Record models) are dumped correctly
|
63
69
|
# using the #as_json hook.
|
@@ -69,12 +75,12 @@ module ActiveRecord
|
|
69
75
|
Coders::YAMLColumn.new(attr_name, class_name_or_coder)
|
70
76
|
end
|
71
77
|
|
72
|
-
decorate_attribute_type(attr_name,
|
73
|
-
if type_incompatible_with_serialize?(
|
74
|
-
raise ColumnNotSerializableError.new(attr_name,
|
78
|
+
decorate_attribute_type(attr_name.to_s, **options) do |cast_type|
|
79
|
+
if type_incompatible_with_serialize?(cast_type, class_name_or_coder)
|
80
|
+
raise ColumnNotSerializableError.new(attr_name, cast_type)
|
75
81
|
end
|
76
82
|
|
77
|
-
Type::Serialized.new(
|
83
|
+
Type::Serialized.new(cast_type, coder)
|
78
84
|
end
|
79
85
|
end
|
80
86
|
|
@@ -1,9 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "active_support/core_ext/object/try"
|
4
|
+
|
3
5
|
module ActiveRecord
|
4
6
|
module AttributeMethods
|
5
7
|
module TimeZoneConversion
|
6
8
|
class TimeZoneConverter < DelegateClass(Type::Value) # :nodoc:
|
9
|
+
def self.new(subtype)
|
10
|
+
self === subtype ? subtype : super
|
11
|
+
end
|
12
|
+
|
7
13
|
def deserialize(value)
|
8
14
|
convert_time_to_time_zone(super)
|
9
15
|
end
|
@@ -62,21 +68,14 @@ module ActiveRecord
|
|
62
68
|
end
|
63
69
|
|
64
70
|
module ClassMethods # :nodoc:
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
# We need to apply this decorator here, rather than on module inclusion. The closure
|
69
|
-
# created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
|
70
|
-
# sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
|
71
|
-
# `skip_time_zone_conversion_for_attributes` would not be picked up.
|
72
|
-
subclass.class_eval do
|
73
|
-
matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
|
74
|
-
decorate_matching_attribute_types(matcher, "_time_zone_conversion") do |type|
|
75
|
-
TimeZoneConverter.new(type)
|
76
|
-
end
|
77
|
-
end
|
71
|
+
def define_attribute(name, cast_type, **)
|
72
|
+
if create_time_zone_conversion_attribute?(name, cast_type)
|
73
|
+
cast_type = TimeZoneConverter.new(cast_type)
|
78
74
|
end
|
75
|
+
super
|
76
|
+
end
|
79
77
|
|
78
|
+
private
|
80
79
|
def create_time_zone_conversion_attribute?(name, cast_type)
|
81
80
|
enabled_for_column = time_zone_aware_attributes &&
|
82
81
|
!skip_time_zone_conversion_for_attributes.include?(name.to_sym)
|
@@ -11,16 +11,14 @@ module ActiveRecord
|
|
11
11
|
|
12
12
|
module ClassMethods # :nodoc:
|
13
13
|
private
|
14
|
-
def define_method_attribute=(name)
|
14
|
+
def define_method_attribute=(name, owner:)
|
15
15
|
ActiveModel::AttributeMethods::AttrNames.define_attribute_accessor_method(
|
16
|
-
|
16
|
+
owner, name, writer: true,
|
17
17
|
) do |temp_method_name, attr_name_expr|
|
18
|
-
|
19
|
-
def #{temp_method_name}(value)
|
20
|
-
|
21
|
-
|
22
|
-
end
|
23
|
-
RUBY
|
18
|
+
owner <<
|
19
|
+
"def #{temp_method_name}(value)" <<
|
20
|
+
" _write_attribute(#{attr_name_expr}, value)" <<
|
21
|
+
"end"
|
24
22
|
end
|
25
23
|
end
|
26
24
|
end
|
@@ -33,27 +31,21 @@ module ActiveRecord
|
|
33
31
|
name = self.class.attribute_aliases[name] || name
|
34
32
|
|
35
33
|
name = @primary_key if name == "id" && @primary_key
|
36
|
-
|
34
|
+
@attributes.write_from_user(name, value)
|
37
35
|
end
|
38
36
|
|
39
37
|
# This method exists to avoid the expensive primary_key check internally, without
|
40
38
|
# breaking compatibility with the write_attribute API
|
41
39
|
def _write_attribute(attr_name, value) # :nodoc:
|
42
|
-
|
43
|
-
@attributes.write_from_user(attr_name.to_s, value)
|
44
|
-
value
|
40
|
+
@attributes.write_from_user(attr_name, value)
|
45
41
|
end
|
46
42
|
|
43
|
+
alias :attribute= :_write_attribute
|
44
|
+
private :attribute=
|
45
|
+
|
47
46
|
private
|
48
47
|
def write_attribute_without_type_cast(attr_name, value)
|
49
|
-
|
50
|
-
@attributes.write_cast_value(attr_name.to_s, value)
|
51
|
-
value
|
52
|
-
end
|
53
|
-
|
54
|
-
# Dispatch target for <tt>*=</tt> attribute methods.
|
55
|
-
def attribute=(attribute_name, value)
|
56
|
-
_write_attribute(attribute_name, value)
|
48
|
+
@attributes.write_cast_value(attr_name, value)
|
57
49
|
end
|
58
50
|
end
|
59
51
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "mutex_m"
|
4
|
+
require "active_support/core_ext/enumerable"
|
4
5
|
|
5
6
|
module ActiveRecord
|
6
7
|
# = Active Record Attribute Methods
|
@@ -18,8 +19,6 @@ module ActiveRecord
|
|
18
19
|
include TimeZoneConversion
|
19
20
|
include Dirty
|
20
21
|
include Serialization
|
21
|
-
|
22
|
-
delegate :column_for_attribute, to: :class
|
23
22
|
end
|
24
23
|
|
25
24
|
RESTRICTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
|
@@ -28,6 +27,17 @@ module ActiveRecord
|
|
28
27
|
include Mutex_m
|
29
28
|
end
|
30
29
|
|
30
|
+
class << self
|
31
|
+
def dangerous_attribute_methods # :nodoc:
|
32
|
+
@dangerous_attribute_methods ||= (
|
33
|
+
Base.instance_methods +
|
34
|
+
Base.private_instance_methods -
|
35
|
+
Base.superclass.instance_methods -
|
36
|
+
Base.superclass.private_instance_methods
|
37
|
+
).map { |m| -m.to_s }.to_set.freeze
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
31
41
|
module ClassMethods
|
32
42
|
def inherited(child_class) #:nodoc:
|
33
43
|
child_class.initialize_generated_modules
|
@@ -97,7 +107,7 @@ module ActiveRecord
|
|
97
107
|
# A method name is 'dangerous' if it is already (re)defined by Active Record, but
|
98
108
|
# not by any ancestors. (So 'puts' is not dangerous but 'save' is.)
|
99
109
|
def dangerous_attribute_method?(name) # :nodoc:
|
100
|
-
|
110
|
+
::ActiveRecord::AttributeMethods.dangerous_attribute_methods.include?(name.to_s)
|
101
111
|
end
|
102
112
|
|
103
113
|
def method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
|
@@ -115,13 +125,11 @@ module ActiveRecord
|
|
115
125
|
# A class method is 'dangerous' if it is already (re)defined by Active Record, but
|
116
126
|
# not by any ancestors. (So 'puts' is not dangerous but 'new' is.)
|
117
127
|
def dangerous_class_method?(method_name)
|
118
|
-
RESTRICTED_CLASS_METHODS.include?(method_name.to_s)
|
119
|
-
end
|
128
|
+
return true if RESTRICTED_CLASS_METHODS.include?(method_name.to_s)
|
120
129
|
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
klass.method(name).owner != superklass.method(name).owner
|
130
|
+
if Base.respond_to?(method_name, true)
|
131
|
+
if Object.respond_to?(method_name, true)
|
132
|
+
Base.method(method_name).owner != Object.method(method_name).owner
|
125
133
|
else
|
126
134
|
true
|
127
135
|
end
|
@@ -140,7 +148,7 @@ module ActiveRecord
|
|
140
148
|
# Person.attribute_method?(:age=) # => true
|
141
149
|
# Person.attribute_method?(:nothing) # => false
|
142
150
|
def attribute_method?(attribute)
|
143
|
-
super || (table_exists? && column_names.include?(attribute.to_s.
|
151
|
+
super || (table_exists? && column_names.include?(attribute.to_s.delete_suffix("=")))
|
144
152
|
end
|
145
153
|
|
146
154
|
# Returns an array of column names as strings if it's not an abstract class and
|
@@ -156,39 +164,27 @@ module ActiveRecord
|
|
156
164
|
attribute_types.keys
|
157
165
|
else
|
158
166
|
[]
|
159
|
-
end
|
167
|
+
end.freeze
|
160
168
|
end
|
161
169
|
|
162
170
|
# Returns true if the given attribute exists, otherwise false.
|
163
171
|
#
|
164
172
|
# class Person < ActiveRecord::Base
|
173
|
+
# alias_attribute :new_name, :name
|
165
174
|
# end
|
166
175
|
#
|
167
|
-
# Person.has_attribute?('name')
|
168
|
-
# Person.has_attribute?(
|
169
|
-
# Person.has_attribute?(:
|
176
|
+
# Person.has_attribute?('name') # => true
|
177
|
+
# Person.has_attribute?('new_name') # => true
|
178
|
+
# Person.has_attribute?(:age) # => true
|
179
|
+
# Person.has_attribute?(:nothing) # => false
|
170
180
|
def has_attribute?(attr_name)
|
171
|
-
|
181
|
+
attr_name = attr_name.to_s
|
182
|
+
attr_name = attribute_aliases[attr_name] || attr_name
|
183
|
+
attribute_types.key?(attr_name)
|
172
184
|
end
|
173
185
|
|
174
|
-
|
175
|
-
|
176
|
-
# named attribute does not exist.
|
177
|
-
#
|
178
|
-
# class Person < ActiveRecord::Base
|
179
|
-
# end
|
180
|
-
#
|
181
|
-
# person = Person.new
|
182
|
-
# person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
|
183
|
-
# # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
|
184
|
-
#
|
185
|
-
# person.column_for_attribute(:nothing)
|
186
|
-
# # => #<ActiveRecord::ConnectionAdapters::NullColumn:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
|
187
|
-
def column_for_attribute(name)
|
188
|
-
name = name.to_s
|
189
|
-
columns_hash.fetch(name) do
|
190
|
-
ConnectionAdapters::NullColumn.new(name)
|
191
|
-
end
|
186
|
+
def _has_attribute?(attr_name) # :nodoc:
|
187
|
+
attribute_types.key?(attr_name)
|
192
188
|
end
|
193
189
|
end
|
194
190
|
|
@@ -217,7 +213,7 @@ module ActiveRecord
|
|
217
213
|
# have been allocated but not yet initialized.
|
218
214
|
if defined?(@attributes)
|
219
215
|
if name = self.class.symbol_column_to_string(name.to_sym)
|
220
|
-
return
|
216
|
+
return _has_attribute?(name)
|
221
217
|
end
|
222
218
|
end
|
223
219
|
|
@@ -227,14 +223,22 @@ module ActiveRecord
|
|
227
223
|
# Returns +true+ if the given attribute is in the attributes hash, otherwise +false+.
|
228
224
|
#
|
229
225
|
# class Person < ActiveRecord::Base
|
226
|
+
# alias_attribute :new_name, :name
|
230
227
|
# end
|
231
228
|
#
|
232
229
|
# person = Person.new
|
233
|
-
# person.has_attribute?(:name)
|
234
|
-
# person.has_attribute?(
|
235
|
-
# person.has_attribute?(
|
230
|
+
# person.has_attribute?(:name) # => true
|
231
|
+
# person.has_attribute?(:new_name) # => true
|
232
|
+
# person.has_attribute?('age') # => true
|
233
|
+
# person.has_attribute?(:nothing) # => false
|
236
234
|
def has_attribute?(attr_name)
|
237
|
-
|
235
|
+
attr_name = attr_name.to_s
|
236
|
+
attr_name = self.class.attribute_aliases[attr_name] || attr_name
|
237
|
+
@attributes.key?(attr_name)
|
238
|
+
end
|
239
|
+
|
240
|
+
def _has_attribute?(attr_name) # :nodoc:
|
241
|
+
@attributes.key?(attr_name)
|
238
242
|
end
|
239
243
|
|
240
244
|
# Returns an array of names for the attributes available on this object.
|
@@ -278,8 +282,10 @@ module ActiveRecord
|
|
278
282
|
# person.attribute_for_inspect(:tag_ids)
|
279
283
|
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
|
280
284
|
def attribute_for_inspect(attr_name)
|
285
|
+
attr_name = attr_name.to_s
|
286
|
+
attr_name = self.class.attribute_aliases[attr_name] || attr_name
|
281
287
|
value = _read_attribute(attr_name)
|
282
|
-
format_for_inspect(value)
|
288
|
+
format_for_inspect(attr_name, value)
|
283
289
|
end
|
284
290
|
|
285
291
|
# Returns +true+ if the specified +attribute+ has been set by the user or by a
|
@@ -297,8 +303,10 @@ module ActiveRecord
|
|
297
303
|
# task.is_done = true
|
298
304
|
# task.attribute_present?(:title) # => true
|
299
305
|
# task.attribute_present?(:is_done) # => true
|
300
|
-
def attribute_present?(
|
301
|
-
|
306
|
+
def attribute_present?(attr_name)
|
307
|
+
attr_name = attr_name.to_s
|
308
|
+
attr_name = self.class.attribute_aliases[attr_name] || attr_name
|
309
|
+
value = _read_attribute(attr_name)
|
302
310
|
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
|
303
311
|
end
|
304
312
|
|
@@ -377,8 +385,8 @@ module ActiveRecord
|
|
377
385
|
end
|
378
386
|
|
379
387
|
def attributes_with_values(attribute_names)
|
380
|
-
attribute_names.
|
381
|
-
|
388
|
+
attribute_names.index_with do |name|
|
389
|
+
_read_attribute(name)
|
382
390
|
end
|
383
391
|
end
|
384
392
|
|
@@ -386,7 +394,7 @@ module ActiveRecord
|
|
386
394
|
def attributes_for_update(attribute_names)
|
387
395
|
attribute_names &= self.class.column_names
|
388
396
|
attribute_names.delete_if do |name|
|
389
|
-
readonly_attribute?(name)
|
397
|
+
self.class.readonly_attribute?(name)
|
390
398
|
end
|
391
399
|
end
|
392
400
|
|
@@ -399,18 +407,20 @@ module ActiveRecord
|
|
399
407
|
end
|
400
408
|
end
|
401
409
|
|
402
|
-
def format_for_inspect(value)
|
403
|
-
if value.
|
404
|
-
"#{value[0, 50]}...".inspect
|
405
|
-
elsif value.is_a?(Date) || value.is_a?(Time)
|
406
|
-
%("#{value.to_s(:db)}")
|
407
|
-
else
|
410
|
+
def format_for_inspect(name, value)
|
411
|
+
if value.nil?
|
408
412
|
value.inspect
|
409
|
-
|
410
|
-
|
413
|
+
else
|
414
|
+
inspected_value = if value.is_a?(String) && value.length > 50
|
415
|
+
"#{value[0, 50]}...".inspect
|
416
|
+
elsif value.is_a?(Date) || value.is_a?(Time)
|
417
|
+
%("#{value.to_s(:inspect)}")
|
418
|
+
else
|
419
|
+
value.inspect
|
420
|
+
end
|
411
421
|
|
412
|
-
|
413
|
-
|
422
|
+
inspection_filter.filter_param(name, inspected_value)
|
423
|
+
end
|
414
424
|
end
|
415
425
|
|
416
426
|
def pk_attribute?(name)
|
@@ -12,6 +12,9 @@ module ActiveRecord
|
|
12
12
|
end
|
13
13
|
|
14
14
|
module ClassMethods
|
15
|
+
##
|
16
|
+
# :call-seq: attribute(name, cast_type = nil, **options)
|
17
|
+
#
|
15
18
|
# Defines an attribute with a type on this model. It will override the
|
16
19
|
# type of existing attributes if needed. This allows control over how
|
17
20
|
# values are converted to and from SQL when assigned to a model. It also
|
@@ -170,7 +173,7 @@ module ActiveRecord
|
|
170
173
|
# class Money < Struct.new(:amount, :currency)
|
171
174
|
# end
|
172
175
|
#
|
173
|
-
# class MoneyType < Type::Value
|
176
|
+
# class MoneyType < ActiveRecord::Type::Value
|
174
177
|
# def initialize(currency_converter:)
|
175
178
|
# @currency_converter = currency_converter
|
176
179
|
# end
|
@@ -205,13 +208,13 @@ module ActiveRecord
|
|
205
208
|
# tracking is performed. The methods +changed?+ and +changed_in_place?+
|
206
209
|
# will be called from ActiveModel::Dirty. See the documentation for those
|
207
210
|
# methods in ActiveModel::Type::Value for more details.
|
208
|
-
def attribute(name, cast_type =
|
211
|
+
def attribute(name, cast_type = nil, **options, &block)
|
209
212
|
name = name.to_s
|
210
213
|
reload_schema_from_cache
|
211
214
|
|
212
215
|
self.attributes_to_define_after_schema_loads =
|
213
216
|
attributes_to_define_after_schema_loads.merge(
|
214
|
-
name => [cast_type, options]
|
217
|
+
name => [cast_type || block, options]
|
215
218
|
)
|
216
219
|
end
|
217
220
|
|
@@ -246,11 +249,7 @@ module ActiveRecord
|
|
246
249
|
def load_schema! # :nodoc:
|
247
250
|
super
|
248
251
|
attributes_to_define_after_schema_loads.each do |name, (type, options)|
|
249
|
-
|
250
|
-
type = ActiveRecord::Type.lookup(type, **options.except(:default))
|
251
|
-
end
|
252
|
-
|
253
|
-
define_attribute(name, type, **options.slice(:default))
|
252
|
+
define_attribute(name, _lookup_cast_type(name, type, options), **options.slice(:default))
|
254
253
|
end
|
255
254
|
end
|
256
255
|
|
@@ -273,6 +272,32 @@ module ActiveRecord
|
|
273
272
|
end
|
274
273
|
_default_attributes[name] = default_attribute
|
275
274
|
end
|
275
|
+
|
276
|
+
def decorate_attribute_type(attr_name, **default)
|
277
|
+
type, options = attributes_to_define_after_schema_loads[attr_name]
|
278
|
+
|
279
|
+
default.with_defaults!(default: options[:default]) if options&.key?(:default)
|
280
|
+
|
281
|
+
attribute(attr_name, **default) do |cast_type|
|
282
|
+
if type && !type.is_a?(Proc)
|
283
|
+
cast_type = _lookup_cast_type(attr_name, type, options)
|
284
|
+
end
|
285
|
+
|
286
|
+
yield cast_type
|
287
|
+
end
|
288
|
+
end
|
289
|
+
|
290
|
+
def _lookup_cast_type(name, type, options)
|
291
|
+
case type
|
292
|
+
when Symbol
|
293
|
+
adapter_name = ActiveRecord::Type.adapter_name_from(self)
|
294
|
+
ActiveRecord::Type.lookup(type, **options.except(:default), adapter: adapter_name)
|
295
|
+
when Proc
|
296
|
+
type[type_for_attribute(name)]
|
297
|
+
else
|
298
|
+
type || type_for_attribute(name)
|
299
|
+
end
|
300
|
+
end
|
276
301
|
end
|
277
302
|
end
|
278
303
|
end
|