activerecord 5.0.7 → 5.1.7
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 +5 -5
- data/CHANGELOG.md +657 -2080
- data/MIT-LICENSE +1 -1
- data/README.rdoc +1 -1
- data/examples/performance.rb +28 -28
- data/examples/simple.rb +3 -3
- data/lib/active_record/aggregations.rb +244 -244
- data/lib/active_record/association_relation.rb +5 -5
- data/lib/active_record/associations/alias_tracker.rb +10 -11
- data/lib/active_record/associations/association.rb +23 -5
- data/lib/active_record/associations/association_scope.rb +95 -81
- data/lib/active_record/associations/belongs_to_association.rb +7 -4
- data/lib/active_record/associations/builder/belongs_to.rb +30 -16
- data/lib/active_record/associations/builder/collection_association.rb +1 -2
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +27 -27
- data/lib/active_record/associations/collection_association.rb +36 -205
- data/lib/active_record/associations/collection_proxy.rb +132 -63
- data/lib/active_record/associations/has_many_association.rb +10 -19
- data/lib/active_record/associations/has_many_through_association.rb +12 -4
- data/lib/active_record/associations/has_one_association.rb +24 -28
- data/lib/active_record/associations/has_one_through_association.rb +5 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +4 -28
- data/lib/active_record/associations/join_dependency/join_base.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
- data/lib/active_record/associations/join_dependency.rb +121 -118
- data/lib/active_record/associations/preloader/association.rb +64 -64
- data/lib/active_record/associations/preloader/belongs_to.rb +0 -2
- data/lib/active_record/associations/preloader/collection_association.rb +6 -6
- data/lib/active_record/associations/preloader/has_many.rb +0 -2
- data/lib/active_record/associations/preloader/singular_association.rb +6 -8
- data/lib/active_record/associations/preloader/through_association.rb +41 -41
- data/lib/active_record/associations/preloader.rb +94 -94
- data/lib/active_record/associations/singular_association.rb +8 -25
- data/lib/active_record/associations/through_association.rb +2 -5
- data/lib/active_record/associations.rb +1591 -1562
- data/lib/active_record/attribute/user_provided_default.rb +4 -2
- data/lib/active_record/attribute.rb +98 -71
- data/lib/active_record/attribute_assignment.rb +61 -61
- data/lib/active_record/attribute_decorators.rb +35 -13
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -7
- data/lib/active_record/attribute_methods/dirty.rb +229 -46
- data/lib/active_record/attribute_methods/primary_key.rb +74 -73
- data/lib/active_record/attribute_methods/read.rb +39 -35
- data/lib/active_record/attribute_methods/serialization.rb +7 -7
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +35 -58
- data/lib/active_record/attribute_methods/write.rb +30 -33
- data/lib/active_record/attribute_methods.rb +56 -65
- data/lib/active_record/attribute_mutation_tracker.rb +63 -11
- data/lib/active_record/attribute_set/builder.rb +27 -33
- data/lib/active_record/attribute_set/yaml_encoder.rb +41 -0
- data/lib/active_record/attribute_set.rb +9 -6
- data/lib/active_record/attributes.rb +22 -22
- data/lib/active_record/autosave_association.rb +18 -13
- data/lib/active_record/base.rb +24 -22
- data/lib/active_record/callbacks.rb +56 -14
- data/lib/active_record/coders/yaml_column.rb +9 -11
- data/lib/active_record/collection_cache_key.rb +3 -4
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +330 -284
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +1 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +39 -37
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +32 -27
- data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -51
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +10 -20
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +74 -79
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +53 -41
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +120 -100
- data/lib/active_record/connection_adapters/abstract/transaction.rb +49 -43
- data/lib/active_record/connection_adapters/abstract_adapter.rb +165 -135
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +404 -424
- data/lib/active_record/connection_adapters/column.rb +26 -4
- data/lib/active_record/connection_adapters/connection_specification.rb +128 -118
- data/lib/active_record/connection_adapters/mysql/column.rb +6 -31
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +36 -49
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +22 -22
- data/lib/active_record/connection_adapters/mysql/quoting.rb +6 -12
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +49 -45
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +16 -19
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -28
- data/lib/active_record/connection_adapters/mysql/schema_statements.rb +43 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +7 -6
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +23 -27
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +32 -53
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +19 -9
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +5 -3
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +16 -16
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +0 -10
- data/lib/active_record/connection_adapters/postgresql/oid/{rails_5_1_point.rb → legacy_point.rb} +9 -16
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +28 -8
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +32 -30
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +2 -1
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +51 -51
- data/lib/active_record/connection_adapters/postgresql/oid.rb +22 -21
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +40 -35
- data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +37 -24
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +19 -23
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +182 -222
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +6 -4
- data/lib/active_record/connection_adapters/postgresql/utils.rb +7 -5
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +198 -167
- data/lib/active_record/connection_adapters/schema_cache.rb +16 -7
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +3 -3
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +1 -1
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +16 -19
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +1 -8
- data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +28 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +17 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +184 -167
- data/lib/active_record/connection_adapters/statement_pool.rb +7 -7
- data/lib/active_record/connection_handling.rb +14 -26
- data/lib/active_record/core.rb +109 -93
- data/lib/active_record/counter_cache.rb +60 -13
- data/lib/active_record/define_callbacks.rb +20 -0
- data/lib/active_record/dynamic_matchers.rb +80 -79
- data/lib/active_record/enum.rb +8 -6
- data/lib/active_record/errors.rb +64 -15
- data/lib/active_record/explain.rb +1 -2
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +7 -4
- data/lib/active_record/fixture_set/file.rb +11 -8
- data/lib/active_record/fixtures.rb +66 -53
- data/lib/active_record/gem_version.rb +1 -1
- data/lib/active_record/inheritance.rb +93 -79
- data/lib/active_record/integration.rb +7 -7
- data/lib/active_record/internal_metadata.rb +3 -16
- data/lib/active_record/legacy_yaml_adapter.rb +1 -1
- data/lib/active_record/locking/optimistic.rb +69 -74
- data/lib/active_record/locking/pessimistic.rb +10 -1
- data/lib/active_record/log_subscriber.rb +23 -28
- data/lib/active_record/migration/command_recorder.rb +94 -94
- data/lib/active_record/migration/compatibility.rb +100 -47
- data/lib/active_record/migration/join_table.rb +6 -6
- data/lib/active_record/migration.rb +153 -155
- data/lib/active_record/model_schema.rb +94 -107
- data/lib/active_record/nested_attributes.rb +200 -199
- data/lib/active_record/null_relation.rb +11 -34
- data/lib/active_record/persistence.rb +65 -50
- data/lib/active_record/query_cache.rb +2 -6
- data/lib/active_record/querying.rb +3 -4
- data/lib/active_record/railtie.rb +16 -17
- data/lib/active_record/railties/controller_runtime.rb +6 -2
- data/lib/active_record/railties/databases.rake +105 -133
- data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
- data/lib/active_record/readonly_attributes.rb +2 -2
- data/lib/active_record/reflection.rb +154 -108
- data/lib/active_record/relation/batches/batch_enumerator.rb +1 -1
- data/lib/active_record/relation/batches.rb +80 -51
- data/lib/active_record/relation/calculations.rb +169 -162
- data/lib/active_record/relation/delegation.rb +32 -31
- data/lib/active_record/relation/finder_methods.rb +197 -231
- data/lib/active_record/relation/merger.rb +58 -62
- data/lib/active_record/relation/predicate_builder/array_handler.rb +7 -5
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +23 -23
- data/lib/active_record/relation/predicate_builder/base_handler.rb +3 -1
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +12 -10
- data/lib/active_record/relation/predicate_builder/range_handler.rb +0 -8
- data/lib/active_record/relation/predicate_builder.rb +92 -89
- data/lib/active_record/relation/query_attribute.rb +1 -1
- data/lib/active_record/relation/query_methods.rb +255 -293
- data/lib/active_record/relation/record_fetch_warning.rb +3 -3
- data/lib/active_record/relation/spawn_methods.rb +4 -5
- data/lib/active_record/relation/where_clause.rb +80 -65
- data/lib/active_record/relation/where_clause_factory.rb +47 -8
- data/lib/active_record/relation.rb +93 -119
- data/lib/active_record/result.rb +41 -32
- data/lib/active_record/runtime_registry.rb +3 -3
- data/lib/active_record/sanitization.rb +176 -192
- data/lib/active_record/schema.rb +3 -3
- data/lib/active_record/schema_dumper.rb +15 -38
- data/lib/active_record/schema_migration.rb +8 -4
- data/lib/active_record/scoping/default.rb +90 -90
- data/lib/active_record/scoping/named.rb +11 -11
- data/lib/active_record/scoping.rb +6 -6
- data/lib/active_record/secure_token.rb +2 -2
- data/lib/active_record/statement_cache.rb +13 -15
- data/lib/active_record/store.rb +31 -32
- data/lib/active_record/suppressor.rb +2 -1
- data/lib/active_record/table_metadata.rb +9 -5
- data/lib/active_record/tasks/database_tasks.rb +65 -55
- data/lib/active_record/tasks/mysql_database_tasks.rb +76 -73
- data/lib/active_record/tasks/postgresql_database_tasks.rb +72 -47
- data/lib/active_record/tasks/sqlite_database_tasks.rb +18 -16
- data/lib/active_record/timestamp.rb +46 -25
- data/lib/active_record/touch_later.rb +1 -2
- data/lib/active_record/transactions.rb +97 -109
- data/lib/active_record/type/adapter_specific_registry.rb +46 -42
- data/lib/active_record/type/decimal_without_scale.rb +13 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +3 -3
- data/lib/active_record/type/internal/abstract_json.rb +4 -0
- data/lib/active_record/type/serialized.rb +14 -8
- data/lib/active_record/type/text.rb +9 -0
- data/lib/active_record/type/time.rb +0 -1
- data/lib/active_record/type/type_map.rb +11 -15
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type.rb +17 -13
- data/lib/active_record/type_caster/connection.rb +8 -6
- data/lib/active_record/type_caster/map.rb +3 -1
- data/lib/active_record/type_caster.rb +2 -2
- data/lib/active_record/validations/associated.rb +1 -1
- data/lib/active_record/validations/presence.rb +2 -2
- data/lib/active_record/validations/uniqueness.rb +8 -39
- data/lib/active_record/validations.rb +4 -4
- data/lib/active_record/version.rb +1 -1
- data/lib/active_record.rb +20 -20
- data/lib/rails/generators/active_record/migration/migration_generator.rb +37 -34
- data/lib/rails/generators/active_record/migration.rb +1 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +9 -9
- data/lib/rails/generators/active_record.rb +4 -4
- metadata +24 -13
- data/lib/active_record/relation/predicate_builder/class_handler.rb +0 -27
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'active_support/core_ext/string/strip'
|
2
|
-
|
3
1
|
module ActiveRecord
|
4
2
|
module AttributeMethods
|
5
3
|
module TimeZoneConversion
|
@@ -26,31 +24,31 @@ module ActiveRecord
|
|
26
24
|
|
27
25
|
private
|
28
26
|
|
29
|
-
|
30
|
-
|
27
|
+
def convert_time_to_time_zone(value)
|
28
|
+
return if value.nil?
|
31
29
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
30
|
+
if value.acts_like?(:time)
|
31
|
+
value.in_time_zone
|
32
|
+
elsif value.is_a?(::Float)
|
33
|
+
value
|
34
|
+
else
|
35
|
+
map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
|
36
|
+
end
|
38
37
|
end
|
39
|
-
end
|
40
38
|
|
41
|
-
|
42
|
-
|
43
|
-
|
39
|
+
def set_time_zone_without_conversion(value)
|
40
|
+
::Time.zone.local_to_utc(value).try(:in_time_zone) if value
|
41
|
+
end
|
44
42
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
43
|
+
def map_avoiding_infinite_recursion(value)
|
44
|
+
map(value) do |v|
|
45
|
+
if value.equal?(v)
|
46
|
+
nil
|
47
|
+
else
|
48
|
+
yield(v)
|
49
|
+
end
|
51
50
|
end
|
52
51
|
end
|
53
|
-
end
|
54
52
|
end
|
55
53
|
|
56
54
|
extend ActiveSupport::Concern
|
@@ -63,53 +61,32 @@ module ActiveRecord
|
|
63
61
|
self.skip_time_zone_conversion_for_attributes = []
|
64
62
|
|
65
63
|
class_attribute :time_zone_aware_types, instance_writer: false
|
66
|
-
self.time_zone_aware_types = [:datetime, :
|
64
|
+
self.time_zone_aware_types = [:datetime, :time]
|
67
65
|
end
|
68
66
|
|
69
67
|
module ClassMethods
|
70
68
|
private
|
71
69
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
70
|
+
def inherited(subclass)
|
71
|
+
super
|
72
|
+
# We need to apply this decorator here, rather than on module inclusion. The closure
|
73
|
+
# created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
|
74
|
+
# sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
|
75
|
+
# `skip_time_zone_conversion_for_attributes` would not be picked up.
|
76
|
+
subclass.class_eval do
|
77
|
+
matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
|
78
|
+
decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type|
|
79
|
+
TimeZoneConverter.new(type)
|
80
|
+
end
|
81
81
|
end
|
82
82
|
end
|
83
|
-
super
|
84
|
-
end
|
85
|
-
|
86
|
-
def create_time_zone_conversion_attribute?(name, cast_type)
|
87
|
-
enabled_for_column = time_zone_aware_attributes &&
|
88
|
-
!self.skip_time_zone_conversion_for_attributes.include?(name.to_sym)
|
89
|
-
result = enabled_for_column &&
|
90
|
-
time_zone_aware_types.include?(cast_type.type)
|
91
|
-
|
92
|
-
if enabled_for_column &&
|
93
|
-
!result &&
|
94
|
-
cast_type.type == :time &&
|
95
|
-
time_zone_aware_types.include?(:not_explicitly_configured)
|
96
|
-
ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
|
97
|
-
Time columns will become time zone aware in Rails 5.1. This
|
98
|
-
still causes `String`s to be parsed as if they were in `Time.zone`,
|
99
|
-
and `Time`s to be converted to `Time.zone`.
|
100
83
|
|
101
|
-
|
84
|
+
def create_time_zone_conversion_attribute?(name, cast_type)
|
85
|
+
enabled_for_column = time_zone_aware_attributes &&
|
86
|
+
!skip_time_zone_conversion_for_attributes.include?(name.to_sym)
|
102
87
|
|
103
|
-
|
104
|
-
|
105
|
-
To use the new behavior, add the following:
|
106
|
-
|
107
|
-
config.active_record.time_zone_aware_types = [:datetime, :time]
|
108
|
-
MESSAGE
|
88
|
+
enabled_for_column && time_zone_aware_types.include?(cast_type.type)
|
109
89
|
end
|
110
|
-
|
111
|
-
result
|
112
|
-
end
|
113
90
|
end
|
114
91
|
end
|
115
92
|
end
|
@@ -8,52 +8,49 @@ module ActiveRecord
|
|
8
8
|
end
|
9
9
|
|
10
10
|
module ClassMethods
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
11
|
+
private
|
12
|
+
|
13
|
+
def define_method_attribute=(name)
|
14
|
+
safe_name = name.unpack("h*".freeze).first
|
15
|
+
ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
|
16
|
+
|
17
|
+
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
|
18
|
+
def __temp__#{safe_name}=(value)
|
19
|
+
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
|
20
|
+
write_attribute(name, value)
|
21
|
+
end
|
22
|
+
alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
|
23
|
+
undef_method :__temp__#{safe_name}=
|
24
|
+
STR
|
25
|
+
end
|
26
26
|
end
|
27
27
|
|
28
28
|
# Updates the attribute identified by <tt>attr_name</tt> with the
|
29
29
|
# specified +value+. Empty strings for Integer and Float columns are
|
30
30
|
# turned into +nil+.
|
31
31
|
def write_attribute(attr_name, value)
|
32
|
-
|
32
|
+
name = if self.class.attribute_alias?(attr_name)
|
33
|
+
self.class.attribute_alias(attr_name).to_s
|
34
|
+
else
|
35
|
+
attr_name.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
name = self.class.primary_key if name == "id".freeze && self.class.primary_key
|
39
|
+
@attributes.write_from_user(name, value)
|
40
|
+
value
|
33
41
|
end
|
34
42
|
|
35
43
|
def raw_write_attribute(attr_name, value) # :nodoc:
|
36
|
-
|
44
|
+
name = attr_name.to_s
|
45
|
+
@attributes.write_cast_value(name, value)
|
46
|
+
value
|
37
47
|
end
|
38
48
|
|
39
49
|
private
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
def write_attribute_with_type_cast(attr_name, value, should_type_cast)
|
46
|
-
attr_name = attr_name.to_s
|
47
|
-
attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
|
48
|
-
|
49
|
-
if should_type_cast
|
50
|
-
@attributes.write_from_user(attr_name, value)
|
51
|
-
else
|
52
|
-
@attributes.write_cast_value(attr_name, value)
|
50
|
+
# Handle *= for method_missing.
|
51
|
+
def attribute=(attribute_name, value)
|
52
|
+
write_attribute(attribute_name, value)
|
53
53
|
end
|
54
|
-
|
55
|
-
value
|
56
|
-
end
|
57
54
|
end
|
58
55
|
end
|
59
56
|
end
|
@@ -1,7 +1,7 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
1
|
+
require "active_support/core_ext/enumerable"
|
2
|
+
require "active_support/core_ext/string/filters"
|
3
|
+
require "mutex_m"
|
4
|
+
require "concurrent/map"
|
5
5
|
|
6
6
|
module ActiveRecord
|
7
7
|
# = Active Record Attribute Methods
|
@@ -148,7 +148,7 @@ module ActiveRecord
|
|
148
148
|
# Person.attribute_method?(:age=) # => true
|
149
149
|
# Person.attribute_method?(:nothing) # => false
|
150
150
|
def attribute_method?(attribute)
|
151
|
-
super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/,
|
151
|
+
super || (table_exists? && column_names.include?(attribute.to_s.sub(/=$/, "")))
|
152
152
|
end
|
153
153
|
|
154
154
|
# Returns an array of column names as strings if it's not an abstract class and
|
@@ -161,10 +161,10 @@ module ActiveRecord
|
|
161
161
|
# # => ["id", "created_at", "updated_at", "name", "age"]
|
162
162
|
def attribute_names
|
163
163
|
@attribute_names ||= if !abstract_class? && table_exists?
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
164
|
+
attribute_types.keys
|
165
|
+
else
|
166
|
+
[]
|
167
|
+
end
|
168
168
|
end
|
169
169
|
|
170
170
|
# Returns true if the given attribute exists, otherwise false.
|
@@ -209,13 +209,13 @@ module ActiveRecord
|
|
209
209
|
# end
|
210
210
|
#
|
211
211
|
# person = Person.new
|
212
|
-
# person.respond_to(:name) # => true
|
213
|
-
# person.respond_to(:name=) # => true
|
214
|
-
# person.respond_to(:name?) # => true
|
215
|
-
# person.respond_to('age') # => true
|
216
|
-
# person.respond_to('age=') # => true
|
217
|
-
# person.respond_to('age?') # => true
|
218
|
-
# person.respond_to(:nothing) # => false
|
212
|
+
# person.respond_to?(:name) # => true
|
213
|
+
# person.respond_to?(:name=) # => true
|
214
|
+
# person.respond_to?(:name?) # => true
|
215
|
+
# person.respond_to?('age') # => true
|
216
|
+
# person.respond_to?('age=') # => true
|
217
|
+
# person.respond_to?('age?') # => true
|
218
|
+
# person.respond_to?(:nothing) # => false
|
219
219
|
def respond_to?(name, include_private = false)
|
220
220
|
return false unless super
|
221
221
|
|
@@ -330,8 +330,6 @@ module ActiveRecord
|
|
330
330
|
#
|
331
331
|
# Note: +:id+ is always present.
|
332
332
|
#
|
333
|
-
# Alias for the #read_attribute method.
|
334
|
-
#
|
335
333
|
# class Person < ActiveRecord::Base
|
336
334
|
# belongs_to :organization
|
337
335
|
# end
|
@@ -396,65 +394,58 @@ module ActiveRecord
|
|
396
394
|
|
397
395
|
protected
|
398
396
|
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
|
403
|
-
value
|
404
|
-
end
|
405
|
-
|
406
|
-
def arel_attributes_with_values_for_create(attribute_names) # :nodoc:
|
407
|
-
arel_attributes_with_values(attributes_for_create(attribute_names))
|
408
|
-
end
|
397
|
+
def attribute_method?(attr_name) # :nodoc:
|
398
|
+
# We check defined? because Syck calls respond_to? before actually calling initialize.
|
399
|
+
defined?(@attributes) && @attributes.key?(attr_name)
|
400
|
+
end
|
409
401
|
|
410
|
-
|
411
|
-
arel_attributes_with_values(attributes_for_update(attribute_names))
|
412
|
-
end
|
402
|
+
private
|
413
403
|
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
end
|
404
|
+
def arel_attributes_with_values_for_create(attribute_names)
|
405
|
+
arel_attributes_with_values(attributes_for_create(attribute_names))
|
406
|
+
end
|
418
407
|
|
419
|
-
|
408
|
+
def arel_attributes_with_values_for_update(attribute_names)
|
409
|
+
arel_attributes_with_values(attributes_for_update(attribute_names))
|
410
|
+
end
|
420
411
|
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
412
|
+
# Returns a Hash of the Arel::Attributes and attribute values that have been
|
413
|
+
# typecasted for use in an Arel insert/update method.
|
414
|
+
def arel_attributes_with_values(attribute_names)
|
415
|
+
attrs = {}
|
416
|
+
arel_table = self.class.arel_table
|
426
417
|
|
427
|
-
|
428
|
-
|
418
|
+
attribute_names.each do |name|
|
419
|
+
attrs[arel_table[name]] = typecasted_attribute_value(name)
|
420
|
+
end
|
421
|
+
attrs
|
429
422
|
end
|
430
|
-
attrs
|
431
|
-
end
|
432
423
|
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
424
|
+
# Filters the primary keys and readonly attributes from the attribute names.
|
425
|
+
def attributes_for_update(attribute_names)
|
426
|
+
attribute_names.reject do |name|
|
427
|
+
readonly_attribute?(name)
|
428
|
+
end
|
437
429
|
end
|
438
|
-
end
|
439
430
|
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
|
431
|
+
# Filters out the primary keys, from the attribute names, when the primary
|
432
|
+
# key is to be generated (e.g. the id attribute has no value).
|
433
|
+
def attributes_for_create(attribute_names)
|
434
|
+
attribute_names.reject do |name|
|
435
|
+
pk_attribute?(name) && id.nil?
|
436
|
+
end
|
445
437
|
end
|
446
|
-
end
|
447
438
|
|
448
|
-
|
449
|
-
|
450
|
-
|
439
|
+
def readonly_attribute?(name)
|
440
|
+
self.class.readonly_attributes.include?(name)
|
441
|
+
end
|
451
442
|
|
452
|
-
|
453
|
-
|
454
|
-
|
443
|
+
def pk_attribute?(name)
|
444
|
+
name == self.class.primary_key
|
445
|
+
end
|
455
446
|
|
456
|
-
|
457
|
-
|
458
|
-
|
447
|
+
def typecasted_attribute_value(name)
|
448
|
+
_read_attribute(name)
|
449
|
+
end
|
459
450
|
end
|
460
451
|
end
|
@@ -1,7 +1,15 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
class AttributeMutationTracker # :nodoc:
|
3
|
+
OPTION_NOT_GIVEN = Object.new
|
4
|
+
|
3
5
|
def initialize(attributes)
|
4
6
|
@attributes = attributes
|
7
|
+
@forced_changes = Set.new
|
8
|
+
@deprecated_forced_changes = Set.new
|
9
|
+
end
|
10
|
+
|
11
|
+
def changed_attribute_names
|
12
|
+
attr_names.select { |attr_name| changed?(attr_name) }
|
5
13
|
end
|
6
14
|
|
7
15
|
def changed_values
|
@@ -14,48 +22,89 @@ module ActiveRecord
|
|
14
22
|
|
15
23
|
def changes
|
16
24
|
attr_names.each_with_object({}.with_indifferent_access) do |attr_name, result|
|
17
|
-
|
18
|
-
|
25
|
+
change = change_to_attribute(attr_name)
|
26
|
+
if change
|
27
|
+
result.merge!(attr_name => change)
|
19
28
|
end
|
20
29
|
end
|
21
30
|
end
|
22
31
|
|
23
|
-
def
|
32
|
+
def change_to_attribute(attr_name)
|
24
33
|
attr_name = attr_name.to_s
|
25
|
-
|
34
|
+
if changed?(attr_name)
|
35
|
+
[attributes[attr_name].original_value, attributes.fetch_value(attr_name)]
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def any_changes?
|
40
|
+
attr_names.any? { |attr| changed?(attr) } || deprecated_forced_changes.any?
|
41
|
+
end
|
42
|
+
|
43
|
+
def changed?(attr_name, from: OPTION_NOT_GIVEN, to: OPTION_NOT_GIVEN)
|
44
|
+
attr_name = attr_name.to_s
|
45
|
+
forced_changes.include?(attr_name) ||
|
46
|
+
attributes[attr_name].changed? &&
|
47
|
+
(OPTION_NOT_GIVEN == from || attributes[attr_name].original_value == from) &&
|
48
|
+
(OPTION_NOT_GIVEN == to || attributes[attr_name].value == to)
|
26
49
|
end
|
27
50
|
|
28
51
|
def changed_in_place?(attr_name)
|
29
|
-
attributes[attr_name].changed_in_place?
|
52
|
+
attributes[attr_name.to_s].changed_in_place?
|
30
53
|
end
|
31
54
|
|
32
55
|
def forget_change(attr_name)
|
33
56
|
attr_name = attr_name.to_s
|
34
57
|
attributes[attr_name] = attributes[attr_name].forgetting_assignment
|
58
|
+
forced_changes.delete(attr_name)
|
59
|
+
end
|
60
|
+
|
61
|
+
def original_value(attr_name)
|
62
|
+
attributes[attr_name.to_s].original_value
|
35
63
|
end
|
36
64
|
|
65
|
+
def force_change(attr_name)
|
66
|
+
forced_changes << attr_name.to_s
|
67
|
+
end
|
68
|
+
|
69
|
+
def deprecated_force_change(attr_name)
|
70
|
+
deprecated_forced_changes << attr_name.to_s
|
71
|
+
end
|
72
|
+
|
73
|
+
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
74
|
+
# Workaround for Ruby 2.2 "private attribute?" warning.
|
37
75
|
protected
|
38
76
|
|
39
|
-
|
77
|
+
attr_reader :attributes, :forced_changes, :deprecated_forced_changes
|
40
78
|
|
41
79
|
private
|
42
80
|
|
43
|
-
|
44
|
-
|
45
|
-
|
81
|
+
def attr_names
|
82
|
+
attributes.keys
|
83
|
+
end
|
46
84
|
end
|
47
85
|
|
48
86
|
class NullMutationTracker # :nodoc:
|
49
87
|
include Singleton
|
50
88
|
|
51
|
-
def
|
89
|
+
def changed_attribute_names(*)
|
90
|
+
[]
|
91
|
+
end
|
92
|
+
|
93
|
+
def changed_values(*)
|
52
94
|
{}
|
53
95
|
end
|
54
96
|
|
55
|
-
def changes
|
97
|
+
def changes(*)
|
56
98
|
{}
|
57
99
|
end
|
58
100
|
|
101
|
+
def change_to_attribute(attr_name)
|
102
|
+
end
|
103
|
+
|
104
|
+
def any_changes?(*)
|
105
|
+
false
|
106
|
+
end
|
107
|
+
|
59
108
|
def changed?(*)
|
60
109
|
false
|
61
110
|
end
|
@@ -66,5 +115,8 @@ module ActiveRecord
|
|
66
115
|
|
67
116
|
def forget_change(*)
|
68
117
|
end
|
118
|
+
|
119
|
+
def original_value(*)
|
120
|
+
end
|
69
121
|
end
|
70
122
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "active_record/attribute"
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
class AttributeSet # :nodoc:
|
@@ -18,7 +18,7 @@ module ActiveRecord
|
|
18
18
|
end
|
19
19
|
|
20
20
|
class LazyAttributeHash # :nodoc:
|
21
|
-
delegate :transform_values, :each_key, :fetch, :except, to: :materialize
|
21
|
+
delegate :transform_values, :each_key, :each_value, :fetch, :except, to: :materialize
|
22
22
|
|
23
23
|
def initialize(types, values, additional_types, default_attributes, delegate_hash = {})
|
24
24
|
@types = types
|
@@ -87,46 +87,40 @@ module ActiveRecord
|
|
87
87
|
end
|
88
88
|
end
|
89
89
|
|
90
|
-
|
91
|
-
|
92
|
-
end
|
93
|
-
|
94
|
-
def init_with(coder)
|
95
|
-
marshal_load(coder["delegate_hash"])
|
96
|
-
end
|
97
|
-
|
90
|
+
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
91
|
+
# Workaround for Ruby 2.2 "private attribute?" warning.
|
98
92
|
protected
|
99
93
|
|
100
|
-
|
94
|
+
attr_reader :types, :values, :additional_types, :delegate_hash, :default_attributes
|
101
95
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
96
|
+
def materialize
|
97
|
+
unless @materialized
|
98
|
+
values.each_key { |key| self[key] }
|
99
|
+
types.each_key { |key| self[key] }
|
100
|
+
unless frozen?
|
101
|
+
@materialized = true
|
102
|
+
end
|
108
103
|
end
|
104
|
+
delegate_hash
|
109
105
|
end
|
110
|
-
delegate_hash
|
111
|
-
end
|
112
106
|
|
113
107
|
private
|
114
108
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
109
|
+
def assign_default_value(name)
|
110
|
+
type = additional_types.fetch(name, types[name])
|
111
|
+
value_present = true
|
112
|
+
value = values.fetch(name) { value_present = false }
|
113
|
+
|
114
|
+
if value_present
|
115
|
+
delegate_hash[name] = Attribute.from_database(name, value, type)
|
116
|
+
elsif types.key?(name)
|
117
|
+
attr = default_attributes[name]
|
118
|
+
if attr
|
119
|
+
delegate_hash[name] = attr.dup
|
120
|
+
else
|
121
|
+
delegate_hash[name] = Attribute.uninitialized(name, type)
|
122
|
+
end
|
128
123
|
end
|
129
124
|
end
|
130
|
-
end
|
131
125
|
end
|
132
126
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
class AttributeSet
|
3
|
+
# Attempts to do more intelligent YAML dumping of an
|
4
|
+
# ActiveRecord::AttributeSet to reduce the size of the resulting string
|
5
|
+
class YAMLEncoder # :nodoc:
|
6
|
+
def initialize(default_types)
|
7
|
+
@default_types = default_types
|
8
|
+
end
|
9
|
+
|
10
|
+
def encode(attribute_set, coder)
|
11
|
+
coder["concise_attributes"] = attribute_set.each_value.map do |attr|
|
12
|
+
if attr.type.equal?(default_types[attr.name])
|
13
|
+
attr.with_type(nil)
|
14
|
+
else
|
15
|
+
attr
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def decode(coder)
|
21
|
+
if coder["attributes"]
|
22
|
+
coder["attributes"]
|
23
|
+
else
|
24
|
+
attributes_hash = Hash[coder["concise_attributes"].map do |attr|
|
25
|
+
if attr.type.nil?
|
26
|
+
attr = attr.with_type(default_types[attr.name])
|
27
|
+
end
|
28
|
+
[attr.name, attr]
|
29
|
+
end]
|
30
|
+
AttributeSet.new(attributes_hash)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
35
|
+
# Workaround for Ruby 2.2 "private attribute?" warning.
|
36
|
+
protected
|
37
|
+
|
38
|
+
attr_reader :default_types
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -1,8 +1,9 @@
|
|
1
|
-
require
|
1
|
+
require "active_record/attribute_set/builder"
|
2
|
+
require "active_record/attribute_set/yaml_encoder"
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
5
|
class AttributeSet # :nodoc:
|
5
|
-
delegate :fetch, :except, to: :attributes
|
6
|
+
delegate :each_value, :fetch, :except, to: :attributes
|
6
7
|
|
7
8
|
def initialize(attributes)
|
8
9
|
@attributes = attributes
|
@@ -97,14 +98,16 @@ module ActiveRecord
|
|
97
98
|
attributes == other.attributes
|
98
99
|
end
|
99
100
|
|
101
|
+
# TODO Change this to private once we've dropped Ruby 2.2 support.
|
102
|
+
# Workaround for Ruby 2.2 "private attribute?" warning.
|
100
103
|
protected
|
101
104
|
|
102
|
-
|
105
|
+
attr_reader :attributes
|
103
106
|
|
104
107
|
private
|
105
108
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
+
def initialized_attributes
|
110
|
+
attributes.select { |_, attr| attr.initialized? }
|
111
|
+
end
|
109
112
|
end
|
110
113
|
end
|