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,9 +1,10 @@
|
|
1
|
-
|
2
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require "active_support/core_ext/module/attribute_accessors"
|
3
|
+
require "active_record/attribute_mutation_tracker"
|
3
4
|
|
4
5
|
module ActiveRecord
|
5
6
|
module AttributeMethods
|
6
|
-
module Dirty
|
7
|
+
module Dirty
|
7
8
|
extend ActiveSupport::Concern
|
8
9
|
|
9
10
|
include ActiveModel::Dirty
|
@@ -15,6 +16,18 @@ module ActiveRecord
|
|
15
16
|
|
16
17
|
class_attribute :partial_writes, instance_writer: false
|
17
18
|
self.partial_writes = true
|
19
|
+
|
20
|
+
after_create { changes_internally_applied }
|
21
|
+
after_update { changes_internally_applied }
|
22
|
+
|
23
|
+
# Attribute methods for "changed in last call to save?"
|
24
|
+
attribute_method_affix(prefix: "saved_change_to_", suffix: "?")
|
25
|
+
attribute_method_prefix("saved_change_to_")
|
26
|
+
attribute_method_suffix("_before_last_save")
|
27
|
+
|
28
|
+
# Attribute methods for "will change if I call save?"
|
29
|
+
attribute_method_affix(prefix: "will_save_change_to_", suffix: "?")
|
30
|
+
attribute_method_suffix("_change_to_be_saved", "_in_database")
|
18
31
|
end
|
19
32
|
|
20
33
|
# Attempts to +save+ the record and clears changed attributes if successful.
|
@@ -35,9 +48,9 @@ module ActiveRecord
|
|
35
48
|
# <tt>reload</tt> the record and clears changed attributes.
|
36
49
|
def reload(*)
|
37
50
|
super.tap do
|
38
|
-
@mutation_tracker = nil
|
39
51
|
@previous_mutation_tracker = nil
|
40
|
-
|
52
|
+
clear_mutation_trackers
|
53
|
+
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
41
54
|
end
|
42
55
|
end
|
43
56
|
|
@@ -46,106 +59,276 @@ module ActiveRecord
|
|
46
59
|
@attributes = self.class._default_attributes.map do |attr|
|
47
60
|
attr.with_value_from_user(@attributes.fetch_value(attr.name))
|
48
61
|
end
|
49
|
-
|
62
|
+
clear_mutation_trackers
|
50
63
|
end
|
51
64
|
|
52
|
-
def
|
65
|
+
def changes_internally_applied # :nodoc:
|
66
|
+
@mutations_before_last_save = mutations_from_database
|
67
|
+
forget_attribute_assignments
|
68
|
+
@mutations_from_database = AttributeMutationTracker.new(@attributes)
|
69
|
+
end
|
70
|
+
|
71
|
+
def changes_applied # :nodoc:
|
53
72
|
@previous_mutation_tracker = mutation_tracker
|
54
|
-
@changed_attributes = HashWithIndifferentAccess.new
|
55
|
-
|
73
|
+
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
74
|
+
@mutation_tracker = nil
|
75
|
+
@mutations_from_database = nil
|
56
76
|
end
|
57
77
|
|
58
|
-
def clear_changes_information
|
78
|
+
def clear_changes_information # :nodoc:
|
59
79
|
@previous_mutation_tracker = nil
|
60
|
-
@changed_attributes = HashWithIndifferentAccess.new
|
61
|
-
|
80
|
+
@changed_attributes = ActiveSupport::HashWithIndifferentAccess.new
|
81
|
+
forget_attribute_assignments
|
82
|
+
clear_mutation_trackers
|
62
83
|
end
|
63
84
|
|
64
|
-
def raw_write_attribute(attr_name, *)
|
85
|
+
def raw_write_attribute(attr_name, *) # :nodoc:
|
65
86
|
result = super
|
66
87
|
clear_attribute_change(attr_name)
|
67
88
|
result
|
68
89
|
end
|
69
90
|
|
70
|
-
def clear_attribute_changes(attr_names)
|
91
|
+
def clear_attribute_changes(attr_names) # :nodoc:
|
71
92
|
super
|
72
93
|
attr_names.each do |attr_name|
|
73
94
|
clear_attribute_change(attr_name)
|
74
95
|
end
|
75
96
|
end
|
76
97
|
|
77
|
-
def changed_attributes
|
98
|
+
def changed_attributes # :nodoc:
|
78
99
|
# This should only be set by methods which will call changed_attributes
|
79
100
|
# multiple times when it is known that the computed value cannot change.
|
80
101
|
if defined?(@cached_changed_attributes)
|
81
102
|
@cached_changed_attributes
|
82
103
|
else
|
104
|
+
emit_warning_if_needed("changed_attributes", "saved_changes.transform_values(&:first)")
|
83
105
|
super.reverse_merge(mutation_tracker.changed_values).freeze
|
84
106
|
end
|
85
107
|
end
|
86
108
|
|
87
|
-
def changes
|
109
|
+
def changes # :nodoc:
|
88
110
|
cache_changed_attributes do
|
111
|
+
emit_warning_if_needed("changes", "saved_changes")
|
89
112
|
super
|
90
113
|
end
|
91
114
|
end
|
92
115
|
|
93
|
-
def previous_changes
|
116
|
+
def previous_changes # :nodoc:
|
117
|
+
unless previous_mutation_tracker.equal?(mutations_before_last_save)
|
118
|
+
ActiveSupport::Deprecation.warn(<<-EOW.strip_heredoc)
|
119
|
+
The behavior of `previous_changes` inside of after callbacks is
|
120
|
+
deprecated without replacement. In the next release of Rails,
|
121
|
+
this method inside of `after_save` will return the changes that
|
122
|
+
were just saved.
|
123
|
+
EOW
|
124
|
+
end
|
94
125
|
previous_mutation_tracker.changes
|
95
126
|
end
|
96
127
|
|
97
|
-
def attribute_changed_in_place?(attr_name)
|
128
|
+
def attribute_changed_in_place?(attr_name) # :nodoc:
|
98
129
|
mutation_tracker.changed_in_place?(attr_name)
|
99
130
|
end
|
100
131
|
|
101
|
-
|
132
|
+
# Did this attribute change when we last saved? This method can be invoked
|
133
|
+
# as `saved_change_to_name?` instead of `saved_change_to_attribute?("name")`.
|
134
|
+
# Behaves similarly to +attribute_changed?+. This method is useful in
|
135
|
+
# after callbacks to determine if the call to save changed a certain
|
136
|
+
# attribute.
|
137
|
+
#
|
138
|
+
# ==== Options
|
139
|
+
#
|
140
|
+
# +from+ When passed, this method will return false unless the original
|
141
|
+
# value is equal to the given option
|
142
|
+
#
|
143
|
+
# +to+ When passed, this method will return false unless the value was
|
144
|
+
# changed to the given value
|
145
|
+
def saved_change_to_attribute?(attr_name, **options)
|
146
|
+
mutations_before_last_save.changed?(attr_name, **options)
|
147
|
+
end
|
102
148
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
149
|
+
# Returns the change to an attribute during the last save. If the
|
150
|
+
# attribute was changed, the result will be an array containing the
|
151
|
+
# original value and the saved value.
|
152
|
+
#
|
153
|
+
# Behaves similarly to +attribute_change+. This method is useful in after
|
154
|
+
# callbacks, to see the change in an attribute that just occurred
|
155
|
+
#
|
156
|
+
# This method can be invoked as `saved_change_to_name` in instead of
|
157
|
+
# `saved_change_to_attribute("name")`
|
158
|
+
def saved_change_to_attribute(attr_name)
|
159
|
+
mutations_before_last_save.change_to_attribute(attr_name)
|
108
160
|
end
|
109
161
|
|
110
|
-
|
111
|
-
|
162
|
+
# Returns the original value of an attribute before the last save.
|
163
|
+
# Behaves similarly to +attribute_was+. This method is useful in after
|
164
|
+
# callbacks to get the original value of an attribute before the save that
|
165
|
+
# just occurred
|
166
|
+
def attribute_before_last_save(attr_name)
|
167
|
+
mutations_before_last_save.original_value(attr_name)
|
112
168
|
end
|
113
169
|
|
114
|
-
|
115
|
-
|
170
|
+
# Did the last call to `save` have any changes to change?
|
171
|
+
def saved_changes?
|
172
|
+
mutations_before_last_save.any_changes?
|
116
173
|
end
|
117
174
|
|
118
|
-
|
119
|
-
|
175
|
+
# Returns a hash containing all the changes that were just saved.
|
176
|
+
def saved_changes
|
177
|
+
mutations_before_last_save.changes
|
120
178
|
end
|
121
179
|
|
122
|
-
|
123
|
-
|
180
|
+
# Alias for `attribute_changed?`
|
181
|
+
def will_save_change_to_attribute?(attr_name, **options)
|
182
|
+
mutations_from_database.changed?(attr_name, **options)
|
124
183
|
end
|
125
184
|
|
126
|
-
|
127
|
-
|
185
|
+
# Alias for `attribute_change`
|
186
|
+
def attribute_change_to_be_saved(attr_name)
|
187
|
+
mutations_from_database.change_to_attribute(attr_name)
|
128
188
|
end
|
129
189
|
|
130
|
-
|
131
|
-
|
132
|
-
|
190
|
+
# Alias for `attribute_was`
|
191
|
+
def attribute_in_database(attr_name)
|
192
|
+
mutations_from_database.original_value(attr_name)
|
193
|
+
end
|
194
|
+
|
195
|
+
# Alias for `changed?`
|
196
|
+
def has_changes_to_save?
|
197
|
+
mutations_from_database.any_changes?
|
133
198
|
end
|
134
199
|
|
135
|
-
|
136
|
-
|
200
|
+
# Alias for `changes`
|
201
|
+
def changes_to_save
|
202
|
+
mutations_from_database.changes
|
137
203
|
end
|
138
204
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
ensure
|
143
|
-
clear_changed_attributes_cache
|
205
|
+
# Alias for `changed`
|
206
|
+
def changed_attribute_names_to_save
|
207
|
+
mutations_from_database.changed_attribute_names
|
144
208
|
end
|
145
209
|
|
146
|
-
|
147
|
-
|
210
|
+
# Alias for `changed_attributes`
|
211
|
+
def attributes_in_database
|
212
|
+
mutations_from_database.changed_values
|
148
213
|
end
|
214
|
+
|
215
|
+
def attribute_was(*)
|
216
|
+
emit_warning_if_needed("attribute_was", "attribute_before_last_save")
|
217
|
+
super
|
218
|
+
end
|
219
|
+
|
220
|
+
def attribute_change(*)
|
221
|
+
emit_warning_if_needed("attribute_change", "saved_change_to_attribute")
|
222
|
+
super
|
223
|
+
end
|
224
|
+
|
225
|
+
def attribute_changed?(*)
|
226
|
+
emit_warning_if_needed("attribute_changed?", "saved_change_to_attribute?")
|
227
|
+
super
|
228
|
+
end
|
229
|
+
|
230
|
+
def changed?(*)
|
231
|
+
emit_warning_if_needed("changed?", "saved_changes?")
|
232
|
+
super
|
233
|
+
end
|
234
|
+
|
235
|
+
def changed(*)
|
236
|
+
emit_warning_if_needed("changed", "saved_changes.keys")
|
237
|
+
super
|
238
|
+
end
|
239
|
+
|
240
|
+
private
|
241
|
+
|
242
|
+
def mutation_tracker
|
243
|
+
unless defined?(@mutation_tracker)
|
244
|
+
@mutation_tracker = nil
|
245
|
+
end
|
246
|
+
@mutation_tracker ||= AttributeMutationTracker.new(@attributes)
|
247
|
+
end
|
248
|
+
|
249
|
+
def emit_warning_if_needed(method_name, new_method_name)
|
250
|
+
unless mutation_tracker.equal?(mutations_from_database)
|
251
|
+
ActiveSupport::Deprecation.warn(<<-EOW.squish)
|
252
|
+
The behavior of `#{method_name}` inside of after callbacks will
|
253
|
+
be changing in the next version of Rails. The new return value will reflect the
|
254
|
+
behavior of calling the method after `save` returned (e.g. the opposite of what
|
255
|
+
it returns now). To maintain the current behavior, use `#{new_method_name}`
|
256
|
+
instead.
|
257
|
+
EOW
|
258
|
+
end
|
259
|
+
end
|
260
|
+
|
261
|
+
def mutations_from_database
|
262
|
+
unless defined?(@mutations_from_database)
|
263
|
+
@mutations_from_database = nil
|
264
|
+
end
|
265
|
+
@mutations_from_database ||= mutation_tracker
|
266
|
+
end
|
267
|
+
|
268
|
+
def changes_include?(attr_name)
|
269
|
+
super || mutation_tracker.changed?(attr_name)
|
270
|
+
end
|
271
|
+
|
272
|
+
def clear_attribute_change(attr_name)
|
273
|
+
mutation_tracker.forget_change(attr_name)
|
274
|
+
mutations_from_database.forget_change(attr_name)
|
275
|
+
end
|
276
|
+
|
277
|
+
def attribute_will_change!(attr_name)
|
278
|
+
super
|
279
|
+
if self.class.has_attribute?(attr_name)
|
280
|
+
mutations_from_database.force_change(attr_name)
|
281
|
+
else
|
282
|
+
ActiveSupport::Deprecation.warn(<<-EOW.squish)
|
283
|
+
#{attr_name} is not an attribute known to Active Record.
|
284
|
+
This behavior is deprecated and will be removed in the next
|
285
|
+
version of Rails. If you'd like #{attr_name} to be managed
|
286
|
+
by Active Record, add `attribute :#{attr_name} to your class.
|
287
|
+
EOW
|
288
|
+
mutations_from_database.deprecated_force_change(attr_name)
|
289
|
+
end
|
290
|
+
end
|
291
|
+
|
292
|
+
def _update_record(*)
|
293
|
+
partial_writes? ? super(keys_for_partial_write) : super
|
294
|
+
end
|
295
|
+
|
296
|
+
def _create_record(*)
|
297
|
+
partial_writes? ? super(keys_for_partial_write) : super
|
298
|
+
end
|
299
|
+
|
300
|
+
def keys_for_partial_write
|
301
|
+
changed_attribute_names_to_save & self.class.column_names
|
302
|
+
end
|
303
|
+
|
304
|
+
def forget_attribute_assignments
|
305
|
+
@attributes = @attributes.map(&:forgetting_assignment)
|
306
|
+
end
|
307
|
+
|
308
|
+
def clear_mutation_trackers
|
309
|
+
@mutation_tracker = nil
|
310
|
+
@mutations_from_database = nil
|
311
|
+
@mutations_before_last_save = nil
|
312
|
+
end
|
313
|
+
|
314
|
+
def previous_mutation_tracker
|
315
|
+
@previous_mutation_tracker ||= NullMutationTracker.instance
|
316
|
+
end
|
317
|
+
|
318
|
+
def mutations_before_last_save
|
319
|
+
@mutations_before_last_save ||= previous_mutation_tracker
|
320
|
+
end
|
321
|
+
|
322
|
+
def cache_changed_attributes
|
323
|
+
@cached_changed_attributes = changed_attributes
|
324
|
+
yield
|
325
|
+
ensure
|
326
|
+
clear_changed_attributes_cache
|
327
|
+
end
|
328
|
+
|
329
|
+
def clear_changed_attributes_cache
|
330
|
+
remove_instance_variable(:@cached_changed_attributes) if defined?(@cached_changed_attributes)
|
331
|
+
end
|
149
332
|
end
|
150
333
|
end
|
151
334
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require "set"
|
2
2
|
|
3
3
|
module ActiveRecord
|
4
4
|
module AttributeMethods
|
@@ -9,7 +9,7 @@ module ActiveRecord
|
|
9
9
|
# available.
|
10
10
|
def to_key
|
11
11
|
sync_with_transaction_state
|
12
|
-
key =
|
12
|
+
key = id
|
13
13
|
[key] if key
|
14
14
|
end
|
15
15
|
|
@@ -45,97 +45,98 @@ module ActiveRecord
|
|
45
45
|
attribute_was(self.class.primary_key)
|
46
46
|
end
|
47
47
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
attr_name == 'id' || super
|
48
|
+
def id_in_database
|
49
|
+
sync_with_transaction_state
|
50
|
+
attribute_in_database(self.class.primary_key)
|
52
51
|
end
|
53
52
|
|
54
|
-
|
55
|
-
def define_method_attribute(attr_name)
|
56
|
-
super
|
53
|
+
private
|
57
54
|
|
58
|
-
|
59
|
-
|
60
|
-
end
|
55
|
+
def attribute_method?(attr_name)
|
56
|
+
attr_name == "id" || super
|
61
57
|
end
|
62
58
|
|
63
|
-
|
59
|
+
module ClassMethods
|
60
|
+
ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was id_in_database).to_set
|
64
61
|
|
65
|
-
|
66
|
-
|
67
|
-
|
62
|
+
def instance_method_already_implemented?(method_name)
|
63
|
+
super || primary_key && ID_ATTRIBUTE_METHODS.include?(method_name)
|
64
|
+
end
|
68
65
|
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
def primary_key
|
73
|
-
@primary_key = reset_primary_key unless defined? @primary_key
|
74
|
-
@primary_key
|
75
|
-
end
|
66
|
+
def dangerous_attribute_method?(method_name)
|
67
|
+
super && !ID_ATTRIBUTE_METHODS.include?(method_name)
|
68
|
+
end
|
76
69
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
70
|
+
# Defines the primary key field -- can be overridden in subclasses.
|
71
|
+
# Overwriting will negate any effect of the +primary_key_prefix_type+
|
72
|
+
# setting, though.
|
73
|
+
def primary_key
|
74
|
+
@primary_key = reset_primary_key unless defined? @primary_key
|
75
|
+
@primary_key
|
76
|
+
end
|
82
77
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
self.primary_key = base_class.primary_key
|
78
|
+
# Returns a quoted version of the primary key name, used to construct
|
79
|
+
# SQL statements.
|
80
|
+
def quoted_primary_key
|
81
|
+
@quoted_primary_key ||= connection.quote_column_name(primary_key)
|
88
82
|
end
|
89
|
-
end
|
90
83
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
elsif base_name && primary_key_prefix_type == :table_name_with_underscore
|
95
|
-
base_name.foreign_key
|
96
|
-
else
|
97
|
-
if ActiveRecord::Base != self && table_exists?
|
98
|
-
pk = connection.schema_cache.primary_keys(table_name)
|
99
|
-
suppress_composite_primary_key(pk)
|
84
|
+
def reset_primary_key #:nodoc:
|
85
|
+
if self == base_class
|
86
|
+
self.primary_key = get_primary_key(base_class.name)
|
100
87
|
else
|
101
|
-
|
88
|
+
self.primary_key = base_class.primary_key
|
102
89
|
end
|
103
90
|
end
|
104
|
-
end
|
105
91
|
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
# Project.primary_key # => "foo_id"
|
121
|
-
def primary_key=(value)
|
122
|
-
@primary_key = value && value.to_s
|
123
|
-
@quoted_primary_key = nil
|
124
|
-
@attributes_builder = nil
|
125
|
-
end
|
92
|
+
def get_primary_key(base_name) #:nodoc:
|
93
|
+
if base_name && primary_key_prefix_type == :table_name
|
94
|
+
base_name.foreign_key(false)
|
95
|
+
elsif base_name && primary_key_prefix_type == :table_name_with_underscore
|
96
|
+
base_name.foreign_key
|
97
|
+
else
|
98
|
+
if ActiveRecord::Base != self && table_exists?
|
99
|
+
pk = connection.schema_cache.primary_keys(table_name)
|
100
|
+
suppress_composite_primary_key(pk)
|
101
|
+
else
|
102
|
+
"id"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
126
106
|
|
127
|
-
|
107
|
+
# Sets the name of the primary key column.
|
108
|
+
#
|
109
|
+
# class Project < ActiveRecord::Base
|
110
|
+
# self.primary_key = 'sysid'
|
111
|
+
# end
|
112
|
+
#
|
113
|
+
# You can also define the #primary_key method yourself:
|
114
|
+
#
|
115
|
+
# class Project < ActiveRecord::Base
|
116
|
+
# def self.primary_key
|
117
|
+
# 'foo_' + super
|
118
|
+
# end
|
119
|
+
# end
|
120
|
+
#
|
121
|
+
# Project.primary_key # => "foo_id"
|
122
|
+
def primary_key=(value)
|
123
|
+
@primary_key = value && value.to_s
|
124
|
+
@quoted_primary_key = nil
|
125
|
+
@attributes_builder = nil
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
128
129
|
|
129
|
-
|
130
|
-
|
130
|
+
def suppress_composite_primary_key(pk)
|
131
|
+
return pk unless pk.is_a?(Array)
|
131
132
|
|
132
|
-
|
133
|
-
|
133
|
+
warn <<-WARNING.strip_heredoc
|
134
|
+
WARNING: Active Record does not support composite primary key.
|
134
135
|
|
135
|
-
|
136
|
-
|
136
|
+
#{table_name} has composite primary key. Composite primary key is ignored.
|
137
|
+
WARNING
|
138
|
+
end
|
137
139
|
end
|
138
|
-
end
|
139
140
|
end
|
140
141
|
end
|
141
142
|
end
|
@@ -4,51 +4,56 @@ module ActiveRecord
|
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
6
|
module ClassMethods
|
7
|
-
|
7
|
+
private
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
9
|
+
# We want to generate the methods via module_eval rather than
|
10
|
+
# define_method, because define_method is slower on dispatch.
|
11
|
+
# Evaluating many similar methods may use more memory as the instruction
|
12
|
+
# sequences are duplicated and cached (in MRI). define_method may
|
13
|
+
# be slower on dispatch, but if you're careful about the closure
|
14
|
+
# created, then define_method will consume much less memory.
|
15
|
+
#
|
16
|
+
# But sometimes the database might return columns with
|
17
|
+
# characters that are not allowed in normal method names (like
|
18
|
+
# 'my_column(omg)'. So to work around this we first define with
|
19
|
+
# the __temp__ identifier, and then use alias method to rename
|
20
|
+
# it to what we want.
|
21
|
+
#
|
22
|
+
# We are also defining a constant to hold the frozen string of
|
23
|
+
# the attribute name. Using a constant means that we do not have
|
24
|
+
# to allocate an object on each call to the attribute method.
|
25
|
+
# Making it frozen means that it doesn't get duped when used to
|
26
|
+
# key the @attributes in read_attribute.
|
27
|
+
def define_method_attribute(name)
|
28
|
+
safe_name = name.unpack("h*".freeze).first
|
29
|
+
temp_method = "__temp__#{safe_name}"
|
30
30
|
|
31
|
-
|
31
|
+
ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
|
32
32
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
33
|
+
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
|
34
|
+
def #{temp_method}
|
35
|
+
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
|
36
|
+
_read_attribute(name) { |n| missing_attribute(n, caller) }
|
37
|
+
end
|
38
|
+
STR
|
39
39
|
|
40
|
-
|
41
|
-
|
42
|
-
|
40
|
+
generated_attribute_methods.module_eval do
|
41
|
+
alias_method name, temp_method
|
42
|
+
undef_method temp_method
|
43
|
+
end
|
43
44
|
end
|
44
|
-
end
|
45
45
|
end
|
46
46
|
|
47
47
|
# Returns the value of the attribute identified by <tt>attr_name</tt> after
|
48
48
|
# it has been typecast (for example, "2004-12-12" in a date column is cast
|
49
49
|
# to a date object, like Date.new(2004, 12, 12)).
|
50
50
|
def read_attribute(attr_name, &block)
|
51
|
-
name = attr_name
|
51
|
+
name = if self.class.attribute_alias?(attr_name)
|
52
|
+
self.class.attribute_alias(attr_name).to_s
|
53
|
+
else
|
54
|
+
attr_name.to_s
|
55
|
+
end
|
56
|
+
|
52
57
|
name = self.class.primary_key if name == "id".freeze && self.class.primary_key
|
53
58
|
_read_attribute(name, &block)
|
54
59
|
end
|
@@ -69,7 +74,6 @@ module ActiveRecord
|
|
69
74
|
|
70
75
|
alias :attribute :_read_attribute
|
71
76
|
private :attribute
|
72
|
-
|
73
77
|
end
|
74
78
|
end
|
75
79
|
end
|
@@ -26,7 +26,7 @@ module ActiveRecord
|
|
26
26
|
# ==== Parameters
|
27
27
|
#
|
28
28
|
# * +attr_name+ - The field name that should be serialized.
|
29
|
-
# * +class_name_or_coder+ - Optional, a coder object, which responds to
|
29
|
+
# * +class_name_or_coder+ - Optional, a coder object, which responds to +.load+ and +.dump+
|
30
30
|
# or a class name that the object type should be equal to.
|
31
31
|
#
|
32
32
|
# ==== Example
|
@@ -50,12 +50,12 @@ module ActiveRecord
|
|
50
50
|
# to ensure special objects (e.g. Active Record models) are dumped correctly
|
51
51
|
# using the #as_json hook.
|
52
52
|
coder = if class_name_or_coder == ::JSON
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
53
|
+
Coders::JSON
|
54
|
+
elsif [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
|
55
|
+
class_name_or_coder
|
56
|
+
else
|
57
|
+
Coders::YAMLColumn.new(attr_name, class_name_or_coder)
|
58
|
+
end
|
59
59
|
|
60
60
|
decorate_attribute_type(attr_name, :serialize) do |type|
|
61
61
|
Type::Serialized.new(type, coder)
|