activerecord 4.2.11.3 → 5.0.7.2
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1638 -1132
- data/MIT-LICENSE +2 -2
- data/README.rdoc +7 -8
- data/examples/performance.rb +2 -3
- data/examples/simple.rb +0 -1
- data/lib/active_record.rb +7 -2
- data/lib/active_record/aggregations.rb +34 -21
- data/lib/active_record/association_relation.rb +7 -4
- data/lib/active_record/associations.rb +347 -218
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +22 -10
- data/lib/active_record/associations/association_scope.rb +75 -104
- data/lib/active_record/associations/belongs_to_association.rb +21 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +43 -18
- data/lib/active_record/associations/builder/collection_association.rb +7 -19
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +16 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +11 -6
- data/lib/active_record/associations/builder/singular_association.rb +13 -11
- data/lib/active_record/associations/collection_association.rb +85 -69
- data/lib/active_record/associations/collection_proxy.rb +104 -46
- data/lib/active_record/associations/foreign_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +21 -78
- data/lib/active_record/associations/has_many_through_association.rb +6 -47
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +38 -22
- data/lib/active_record/associations/join_dependency/join_association.rb +15 -14
- data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
- data/lib/active_record/associations/preloader.rb +14 -4
- data/lib/active_record/associations/preloader/association.rb +52 -71
- data/lib/active_record/associations/preloader/collection_association.rb +0 -7
- data/lib/active_record/associations/preloader/has_many_through.rb +1 -1
- data/lib/active_record/associations/preloader/has_one.rb +0 -8
- data/lib/active_record/associations/preloader/singular_association.rb +0 -1
- data/lib/active_record/associations/preloader/through_association.rb +36 -17
- data/lib/active_record/associations/singular_association.rb +13 -1
- data/lib/active_record/associations/through_association.rb +12 -4
- data/lib/active_record/attribute.rb +69 -19
- data/lib/active_record/attribute/user_provided_default.rb +28 -0
- data/lib/active_record/attribute_assignment.rb +19 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods.rb +69 -44
- data/lib/active_record/attribute_methods/before_type_cast.rb +1 -1
- data/lib/active_record/attribute_methods/dirty.rb +46 -86
- data/lib/active_record/attribute_methods/primary_key.rb +16 -3
- data/lib/active_record/attribute_methods/query.rb +2 -2
- data/lib/active_record/attribute_methods/read.rb +31 -59
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +61 -14
- data/lib/active_record/attribute_methods/write.rb +13 -37
- data/lib/active_record/attribute_mutation_tracker.rb +70 -0
- data/lib/active_record/attribute_set.rb +32 -3
- data/lib/active_record/attribute_set/builder.rb +42 -16
- data/lib/active_record/attributes.rb +199 -81
- data/lib/active_record/autosave_association.rb +54 -17
- data/lib/active_record/base.rb +32 -23
- data/lib/active_record/callbacks.rb +39 -43
- data/lib/active_record/coders/json.rb +1 -1
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +50 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +467 -189
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -62
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/quoting.rb +86 -13
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +61 -39
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +236 -188
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +407 -156
- data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +177 -71
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +433 -399
- data/lib/active_record/connection_adapters/column.rb +28 -43
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -27
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
- data/lib/active_record/connection_adapters/mysql/database_statements.rb +108 -0
- data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
- data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
- data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +25 -166
- data/lib/active_record/connection_adapters/postgresql/column.rb +33 -11
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +18 -72
- data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +37 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -1
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +7 -22
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +13 -3
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +1 -26
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +31 -17
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +0 -4
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +1 -1
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +56 -19
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +29 -10
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -79
- data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +250 -154
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/utils.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +264 -170
- data/lib/active_record/connection_adapters/schema_cache.rb +36 -23
- data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
- data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
- data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
- data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +151 -194
- data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
- data/lib/active_record/connection_handling.rb +37 -14
- data/lib/active_record/core.rb +92 -108
- data/lib/active_record/counter_cache.rb +13 -24
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +116 -76
- data/lib/active_record/errors.rb +87 -48
- data/lib/active_record/explain.rb +20 -9
- data/lib/active_record/explain_registry.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixture_set/file.rb +26 -5
- data/lib/active_record/fixtures.rb +77 -41
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +17 -14
- data/lib/active_record/internal_metadata.rb +56 -0
- data/lib/active_record/legacy_yaml_adapter.rb +18 -2
- data/lib/active_record/locale/en.yml +3 -2
- data/lib/active_record/locking/optimistic.rb +15 -15
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +48 -24
- data/lib/active_record/migration.rb +362 -111
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +126 -0
- data/lib/active_record/model_schema.rb +270 -73
- data/lib/active_record/nested_attributes.rb +58 -29
- data/lib/active_record/no_touching.rb +4 -0
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +152 -90
- data/lib/active_record/query_cache.rb +18 -23
- data/lib/active_record/querying.rb +12 -11
- data/lib/active_record/railtie.rb +23 -16
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +52 -41
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +302 -115
- data/lib/active_record/relation.rb +187 -120
- data/lib/active_record/relation/batches.rb +141 -36
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +92 -117
- data/lib/active_record/relation/delegation.rb +8 -20
- data/lib/active_record/relation/finder_methods.rb +173 -89
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +16 -42
- data/lib/active_record/relation/predicate_builder.rb +120 -107
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
- data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
- data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
- data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
- data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +308 -244
- data/lib/active_record/relation/record_fetch_warning.rb +49 -0
- data/lib/active_record/relation/spawn_methods.rb +4 -7
- data/lib/active_record/relation/where_clause.rb +174 -0
- data/lib/active_record/relation/where_clause_factory.rb +38 -0
- data/lib/active_record/result.rb +11 -4
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +105 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +54 -37
- data/lib/active_record/schema_migration.rb +11 -14
- data/lib/active_record/scoping.rb +34 -16
- data/lib/active_record/scoping/default.rb +28 -10
- data/lib/active_record/scoping/named.rb +59 -26
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +3 -5
- data/lib/active_record/statement_cache.rb +17 -15
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +69 -0
- data/lib/active_record/tasks/database_tasks.rb +66 -49
- data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
- data/lib/active_record/tasks/postgresql_database_tasks.rb +12 -3
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +20 -9
- data/lib/active_record/touch_later.rb +63 -0
- data/lib/active_record/transactions.rb +139 -57
- data/lib/active_record/type.rb +66 -17
- data/lib/active_record/type/adapter_specific_registry.rb +130 -0
- data/lib/active_record/type/date.rb +2 -45
- data/lib/active_record/type/date_time.rb +2 -49
- data/lib/active_record/type/internal/abstract_json.rb +33 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +15 -14
- data/lib/active_record/type/time.rb +10 -16
- data/lib/active_record/type/type_map.rb +4 -4
- data/lib/active_record/type_caster.rb +7 -0
- data/lib/active_record/type_caster/connection.rb +29 -0
- data/lib/active_record/type_caster/map.rb +19 -0
- data/lib/active_record/validations.rb +33 -32
- data/lib/active_record/validations/absence.rb +23 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +24 -0
- data/lib/active_record/validations/presence.rb +11 -12
- data/lib/active_record/validations/uniqueness.rb +33 -33
- data/lib/rails/generators/active_record/migration.rb +15 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -5
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +8 -3
- data/lib/rails/generators/active_record/migration/templates/migration.rb +8 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +33 -16
- data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +58 -34
- data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -498
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +0 -11
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
- data/lib/active_record/serializers/xml_serializer.rb +0 -193
- data/lib/active_record/type/big_integer.rb +0 -13
- data/lib/active_record/type/binary.rb +0 -50
- data/lib/active_record/type/boolean.rb +0 -31
- data/lib/active_record/type/decimal.rb +0 -64
- data/lib/active_record/type/decimal_without_scale.rb +0 -11
- data/lib/active_record/type/decorator.rb +0 -14
- data/lib/active_record/type/float.rb +0 -19
- data/lib/active_record/type/integer.rb +0 -59
- data/lib/active_record/type/mutable.rb +0 -16
- data/lib/active_record/type/numeric.rb +0 -36
- data/lib/active_record/type/string.rb +0 -40
- data/lib/active_record/type/text.rb +0 -11
- data/lib/active_record/type/time_value.rb +0 -38
- data/lib/active_record/type/unsigned_integer.rb +0 -15
- data/lib/active_record/type/value.rb +0 -110
@@ -15,7 +15,7 @@ module ActiveRecord
|
|
15
15
|
end
|
16
16
|
|
17
17
|
def decorate_matching_attribute_types(matcher, decorator_name, &block)
|
18
|
-
|
18
|
+
reload_schema_from_cache
|
19
19
|
decorator_name = decorator_name.to_s
|
20
20
|
|
21
21
|
# Create new hashes so we don't modify parent classes
|
@@ -24,10 +24,11 @@ module ActiveRecord
|
|
24
24
|
|
25
25
|
private
|
26
26
|
|
27
|
-
def
|
28
|
-
super
|
29
|
-
|
30
|
-
|
27
|
+
def load_schema!
|
28
|
+
super
|
29
|
+
attribute_types.each do |name, type|
|
30
|
+
decorated_type = attribute_type_decorations.apply(name, type)
|
31
|
+
define_attribute(name, decorated_type)
|
31
32
|
end
|
32
33
|
end
|
33
34
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'active_support/core_ext/enumerable'
|
2
2
|
require 'active_support/core_ext/string/filters'
|
3
3
|
require 'mutex_m'
|
4
|
-
require '
|
4
|
+
require 'concurrent/map'
|
5
5
|
|
6
6
|
module ActiveRecord
|
7
7
|
# = Active Record Attribute Methods
|
@@ -34,30 +34,6 @@ module ActiveRecord
|
|
34
34
|
|
35
35
|
BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
|
36
36
|
|
37
|
-
class AttributeMethodCache
|
38
|
-
def initialize
|
39
|
-
@module = Module.new
|
40
|
-
@method_cache = ThreadSafe::Cache.new
|
41
|
-
end
|
42
|
-
|
43
|
-
def [](name)
|
44
|
-
@method_cache.compute_if_absent(name) do
|
45
|
-
safe_name = name.unpack('h*').first
|
46
|
-
temp_method = "__temp__#{safe_name}"
|
47
|
-
ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
|
48
|
-
@module.module_eval method_body(temp_method, safe_name), __FILE__, __LINE__
|
49
|
-
@module.instance_method temp_method
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
# Override this method in the subclasses for method body.
|
56
|
-
def method_body(method_name, const_name)
|
57
|
-
raise NotImplementedError, "Subclasses must implement a method_body(method_name, const_name) method."
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
37
|
class GeneratedAttributeMethods < Module; end # :nodoc:
|
62
38
|
|
63
39
|
module ClassMethods
|
@@ -83,7 +59,7 @@ module ActiveRecord
|
|
83
59
|
generated_attribute_methods.synchronize do
|
84
60
|
return false if @attribute_methods_generated
|
85
61
|
superclass.define_attribute_methods unless self == base_class
|
86
|
-
super(
|
62
|
+
super(attribute_names)
|
87
63
|
@attribute_methods_generated = true
|
88
64
|
end
|
89
65
|
true
|
@@ -96,7 +72,7 @@ module ActiveRecord
|
|
96
72
|
end
|
97
73
|
end
|
98
74
|
|
99
|
-
# Raises
|
75
|
+
# Raises an ActiveRecord::DangerousAttributeError exception when an
|
100
76
|
# \Active \Record method is defined in the model, otherwise +false+.
|
101
77
|
#
|
102
78
|
# class Person < ActiveRecord::Base
|
@@ -106,7 +82,7 @@ module ActiveRecord
|
|
106
82
|
# end
|
107
83
|
#
|
108
84
|
# Person.instance_method_already_implemented?(:save)
|
109
|
-
# # => ActiveRecord::DangerousAttributeError: save is defined by
|
85
|
+
# # => ActiveRecord::DangerousAttributeError: save is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name.
|
110
86
|
#
|
111
87
|
# Person.instance_method_already_implemented?(:name)
|
112
88
|
# # => false
|
@@ -150,7 +126,7 @@ module ActiveRecord
|
|
150
126
|
BLACKLISTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
|
151
127
|
end
|
152
128
|
|
153
|
-
def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc
|
129
|
+
def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
|
154
130
|
if klass.respond_to?(name, true)
|
155
131
|
if superklass.respond_to?(name, true)
|
156
132
|
klass.method(name).owner != superklass.method(name).owner
|
@@ -185,14 +161,27 @@ module ActiveRecord
|
|
185
161
|
# # => ["id", "created_at", "updated_at", "name", "age"]
|
186
162
|
def attribute_names
|
187
163
|
@attribute_names ||= if !abstract_class? && table_exists?
|
188
|
-
|
164
|
+
attribute_types.keys
|
189
165
|
else
|
190
166
|
[]
|
191
167
|
end
|
192
168
|
end
|
193
169
|
|
170
|
+
# Returns true if the given attribute exists, otherwise false.
|
171
|
+
#
|
172
|
+
# class Person < ActiveRecord::Base
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# Person.has_attribute?('name') # => true
|
176
|
+
# Person.has_attribute?(:age) # => true
|
177
|
+
# Person.has_attribute?(:nothing) # => false
|
178
|
+
def has_attribute?(attr_name)
|
179
|
+
attribute_types.key?(attr_name.to_s)
|
180
|
+
end
|
181
|
+
|
194
182
|
# Returns the column object for the named attribute.
|
195
|
-
# Returns
|
183
|
+
# Returns a +ActiveRecord::ConnectionAdapters::NullColumn+ if the
|
184
|
+
# named attribute does not exist.
|
196
185
|
#
|
197
186
|
# class Person < ActiveRecord::Base
|
198
187
|
# end
|
@@ -202,23 +191,18 @@ module ActiveRecord
|
|
202
191
|
# # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
|
203
192
|
#
|
204
193
|
# person.column_for_attribute(:nothing)
|
205
|
-
# # => nil
|
194
|
+
# # => #<ActiveRecord::ConnectionAdapters::NullColumn:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
|
206
195
|
def column_for_attribute(name)
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
`#column_for_attribute` will return a null object for non-existent
|
211
|
-
columns in Rails 5. Use `#has_attribute?` if you need to check for
|
212
|
-
an attribute's existence.
|
213
|
-
MSG
|
196
|
+
name = name.to_s
|
197
|
+
columns_hash.fetch(name) do
|
198
|
+
ConnectionAdapters::NullColumn.new(name)
|
214
199
|
end
|
215
|
-
column
|
216
200
|
end
|
217
201
|
end
|
218
202
|
|
219
203
|
# A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
|
220
204
|
# <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
|
221
|
-
# which will all return +true+. It also
|
205
|
+
# which will all return +true+. It also defines the attribute methods if they have
|
222
206
|
# not been generated.
|
223
207
|
#
|
224
208
|
# class Person < ActiveRecord::Base
|
@@ -234,7 +218,15 @@ module ActiveRecord
|
|
234
218
|
# person.respond_to(:nothing) # => false
|
235
219
|
def respond_to?(name, include_private = false)
|
236
220
|
return false unless super
|
237
|
-
|
221
|
+
|
222
|
+
case name
|
223
|
+
when :to_partial_path
|
224
|
+
name = "to_partial_path".freeze
|
225
|
+
when :to_model
|
226
|
+
name = "to_model".freeze
|
227
|
+
else
|
228
|
+
name = name.to_s
|
229
|
+
end
|
238
230
|
|
239
231
|
# If the result is true then check for the select case.
|
240
232
|
# For queries selecting a subset of columns, return false for unselected columns.
|
@@ -338,7 +330,7 @@ module ActiveRecord
|
|
338
330
|
#
|
339
331
|
# Note: +:id+ is always present.
|
340
332
|
#
|
341
|
-
# Alias for the
|
333
|
+
# Alias for the #read_attribute method.
|
342
334
|
#
|
343
335
|
# class Person < ActiveRecord::Base
|
344
336
|
# belongs_to :organization
|
@@ -356,7 +348,7 @@ module ActiveRecord
|
|
356
348
|
end
|
357
349
|
|
358
350
|
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
|
359
|
-
# (Alias for the protected
|
351
|
+
# (Alias for the protected #write_attribute method).
|
360
352
|
#
|
361
353
|
# class Person < ActiveRecord::Base
|
362
354
|
# end
|
@@ -369,6 +361,39 @@ module ActiveRecord
|
|
369
361
|
write_attribute(attr_name, value)
|
370
362
|
end
|
371
363
|
|
364
|
+
# Returns the name of all database fields which have been read from this
|
365
|
+
# model. This can be useful in development mode to determine which fields
|
366
|
+
# need to be selected. For performance critical pages, selecting only the
|
367
|
+
# required fields can be an easy performance win (assuming you aren't using
|
368
|
+
# all of the fields on the model).
|
369
|
+
#
|
370
|
+
# For example:
|
371
|
+
#
|
372
|
+
# class PostsController < ActionController::Base
|
373
|
+
# after_action :print_accessed_fields, only: :index
|
374
|
+
#
|
375
|
+
# def index
|
376
|
+
# @posts = Post.all
|
377
|
+
# end
|
378
|
+
#
|
379
|
+
# private
|
380
|
+
#
|
381
|
+
# def print_accessed_fields
|
382
|
+
# p @posts.first.accessed_fields
|
383
|
+
# end
|
384
|
+
# end
|
385
|
+
#
|
386
|
+
# Which allows you to quickly change your code to:
|
387
|
+
#
|
388
|
+
# class PostsController < ActionController::Base
|
389
|
+
# def index
|
390
|
+
# @posts = Post.select(:id, :title, :author_id, :updated_at)
|
391
|
+
# end
|
392
|
+
# end
|
393
|
+
def accessed_fields
|
394
|
+
@attributes.accessed
|
395
|
+
end
|
396
|
+
|
372
397
|
protected
|
373
398
|
|
374
399
|
def clone_attribute_value(reader_method, attribute_name) # :nodoc:
|
@@ -2,7 +2,7 @@ module ActiveRecord
|
|
2
2
|
module AttributeMethods
|
3
3
|
# = Active Record Attribute Methods Before Type Cast
|
4
4
|
#
|
5
|
-
#
|
5
|
+
# ActiveRecord::AttributeMethods::BeforeTypeCast provides a way to
|
6
6
|
# read the value of the attributes before typecasting and deserialization.
|
7
7
|
#
|
8
8
|
# class Task < ActiveRecord::Base
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_support/core_ext/module/attribute_accessors'
|
2
|
+
require 'active_record/attribute_mutation_tracker'
|
2
3
|
|
3
4
|
module ActiveRecord
|
4
5
|
module AttributeMethods
|
@@ -34,24 +35,43 @@ module ActiveRecord
|
|
34
35
|
# <tt>reload</tt> the record and clears changed attributes.
|
35
36
|
def reload(*)
|
36
37
|
super.tap do
|
37
|
-
|
38
|
+
@mutation_tracker = nil
|
39
|
+
@previous_mutation_tracker = nil
|
40
|
+
@changed_attributes = HashWithIndifferentAccess.new
|
38
41
|
end
|
39
42
|
end
|
40
43
|
|
41
44
|
def initialize_dup(other) # :nodoc:
|
42
45
|
super
|
43
|
-
@
|
44
|
-
|
46
|
+
@attributes = self.class._default_attributes.map do |attr|
|
47
|
+
attr.with_value_from_user(@attributes.fetch_value(attr.name))
|
48
|
+
end
|
49
|
+
@mutation_tracker = nil
|
45
50
|
end
|
46
51
|
|
47
52
|
def changes_applied
|
48
|
-
|
49
|
-
|
53
|
+
@previous_mutation_tracker = mutation_tracker
|
54
|
+
@changed_attributes = HashWithIndifferentAccess.new
|
55
|
+
store_original_attributes
|
50
56
|
end
|
51
57
|
|
52
58
|
def clear_changes_information
|
59
|
+
@previous_mutation_tracker = nil
|
60
|
+
@changed_attributes = HashWithIndifferentAccess.new
|
61
|
+
store_original_attributes
|
62
|
+
end
|
63
|
+
|
64
|
+
def raw_write_attribute(attr_name, *)
|
65
|
+
result = super
|
66
|
+
clear_attribute_change(attr_name)
|
67
|
+
result
|
68
|
+
end
|
69
|
+
|
70
|
+
def clear_attribute_changes(attr_names)
|
53
71
|
super
|
54
|
-
|
72
|
+
attr_names.each do |attr_name|
|
73
|
+
clear_attribute_change(attr_name)
|
74
|
+
end
|
55
75
|
end
|
56
76
|
|
57
77
|
def changed_attributes
|
@@ -60,7 +80,7 @@ module ActiveRecord
|
|
60
80
|
if defined?(@cached_changed_attributes)
|
61
81
|
@cached_changed_attributes
|
62
82
|
else
|
63
|
-
super.reverse_merge(
|
83
|
+
super.reverse_merge(mutation_tracker.changed_values).freeze
|
64
84
|
end
|
65
85
|
end
|
66
86
|
|
@@ -70,59 +90,29 @@ module ActiveRecord
|
|
70
90
|
end
|
71
91
|
end
|
72
92
|
|
93
|
+
def previous_changes
|
94
|
+
previous_mutation_tracker.changes
|
95
|
+
end
|
96
|
+
|
73
97
|
def attribute_changed_in_place?(attr_name)
|
74
|
-
|
75
|
-
@attributes[attr_name].changed_in_place_from?(old_value)
|
98
|
+
mutation_tracker.changed_in_place?(attr_name)
|
76
99
|
end
|
77
100
|
|
78
101
|
private
|
79
102
|
|
80
|
-
def
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
def calculate_changes_from_defaults
|
85
|
-
@changed_attributes = nil
|
86
|
-
self.class.column_defaults.each do |attr, orig_value|
|
87
|
-
set_attribute_was(attr, orig_value) if _field_changed?(attr, orig_value)
|
103
|
+
def mutation_tracker
|
104
|
+
unless defined?(@mutation_tracker)
|
105
|
+
@mutation_tracker = nil
|
88
106
|
end
|
107
|
+
@mutation_tracker ||= AttributeMutationTracker.new(@attributes)
|
89
108
|
end
|
90
109
|
|
91
|
-
|
92
|
-
|
93
|
-
attr = attr.to_s
|
94
|
-
|
95
|
-
old_value = old_attribute_value(attr)
|
96
|
-
|
97
|
-
result = super
|
98
|
-
store_original_raw_attribute(attr)
|
99
|
-
save_changed_attribute(attr, old_value)
|
100
|
-
result
|
101
|
-
end
|
102
|
-
|
103
|
-
def raw_write_attribute(attr, value)
|
104
|
-
attr = attr.to_s
|
105
|
-
|
106
|
-
result = super
|
107
|
-
original_raw_attributes[attr] = value
|
108
|
-
result
|
110
|
+
def changes_include?(attr_name)
|
111
|
+
super || mutation_tracker.changed?(attr_name)
|
109
112
|
end
|
110
113
|
|
111
|
-
def
|
112
|
-
|
113
|
-
if attribute_changed_by_setter?(attr)
|
114
|
-
clear_attribute_changes(attr) unless _field_changed?(attr, old_value)
|
115
|
-
else
|
116
|
-
set_attribute_was(attr, old_value) if _field_changed?(attr, old_value)
|
117
|
-
end
|
118
|
-
end
|
119
|
-
|
120
|
-
def old_attribute_value(attr)
|
121
|
-
if attribute_changed?(attr)
|
122
|
-
changed_attributes[attr]
|
123
|
-
else
|
124
|
-
clone_attribute_value(:_read_attribute, attr)
|
125
|
-
end
|
114
|
+
def clear_attribute_change(attr_name)
|
115
|
+
mutation_tracker.forget_change(attr_name)
|
126
116
|
end
|
127
117
|
|
128
118
|
def _update_record(*)
|
@@ -133,47 +123,17 @@ module ActiveRecord
|
|
133
123
|
partial_writes? ? super(keys_for_partial_write) : super
|
134
124
|
end
|
135
125
|
|
136
|
-
# Serialized attributes should always be written in case they've been
|
137
|
-
# changed in place.
|
138
126
|
def keys_for_partial_write
|
139
|
-
changed &
|
140
|
-
end
|
141
|
-
|
142
|
-
def _field_changed?(attr, old_value)
|
143
|
-
@attributes[attr].changed_from?(old_value)
|
144
|
-
end
|
145
|
-
|
146
|
-
def attributes_changed_in_place
|
147
|
-
changed_in_place.each_with_object({}) do |attr_name, h|
|
148
|
-
orig = @attributes[attr_name].original_value
|
149
|
-
h[attr_name] = orig
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
def changed_in_place
|
154
|
-
self.class.attribute_names.select do |attr_name|
|
155
|
-
attribute_changed_in_place?(attr_name)
|
156
|
-
end
|
127
|
+
changed & self.class.column_names
|
157
128
|
end
|
158
129
|
|
159
|
-
def
|
160
|
-
|
161
|
-
|
162
|
-
end
|
130
|
+
def store_original_attributes
|
131
|
+
@attributes = @attributes.map(&:forgetting_assignment)
|
132
|
+
@mutation_tracker = nil
|
163
133
|
end
|
164
134
|
|
165
|
-
def
|
166
|
-
@
|
167
|
-
end
|
168
|
-
|
169
|
-
def store_original_raw_attribute(attr_name)
|
170
|
-
original_raw_attributes[attr_name] = @attributes[attr_name].value_for_database rescue nil
|
171
|
-
end
|
172
|
-
|
173
|
-
def store_original_raw_attributes
|
174
|
-
attribute_names.each do |attr|
|
175
|
-
store_original_raw_attribute(attr)
|
176
|
-
end
|
135
|
+
def previous_mutation_tracker
|
136
|
+
@previous_mutation_tracker ||= NullMutationTracker.instance
|
177
137
|
end
|
178
138
|
|
179
139
|
def cache_changed_attributes
|
@@ -5,7 +5,7 @@ module ActiveRecord
|
|
5
5
|
module PrimaryKey
|
6
6
|
extend ActiveSupport::Concern
|
7
7
|
|
8
|
-
# Returns this record's primary key value wrapped in an
|
8
|
+
# Returns this record's primary key value wrapped in an array if one is
|
9
9
|
# available.
|
10
10
|
def to_key
|
11
11
|
sync_with_transaction_state
|
@@ -95,7 +95,8 @@ module ActiveRecord
|
|
95
95
|
base_name.foreign_key
|
96
96
|
else
|
97
97
|
if ActiveRecord::Base != self && table_exists?
|
98
|
-
connection.schema_cache.primary_keys(table_name)
|
98
|
+
pk = connection.schema_cache.primary_keys(table_name)
|
99
|
+
suppress_composite_primary_key(pk)
|
99
100
|
else
|
100
101
|
'id'
|
101
102
|
end
|
@@ -108,7 +109,7 @@ module ActiveRecord
|
|
108
109
|
# self.primary_key = 'sysid'
|
109
110
|
# end
|
110
111
|
#
|
111
|
-
# You can also define the
|
112
|
+
# You can also define the #primary_key method yourself:
|
112
113
|
#
|
113
114
|
# class Project < ActiveRecord::Base
|
114
115
|
# def self.primary_key
|
@@ -122,6 +123,18 @@ module ActiveRecord
|
|
122
123
|
@quoted_primary_key = nil
|
123
124
|
@attributes_builder = nil
|
124
125
|
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
def suppress_composite_primary_key(pk)
|
130
|
+
return pk unless pk.is_a?(Array)
|
131
|
+
|
132
|
+
warn <<-WARNING.strip_heredoc
|
133
|
+
WARNING: Active Record does not support composite primary key.
|
134
|
+
|
135
|
+
#{table_name} has composite primary key. Composite primary key is ignored.
|
136
|
+
WARNING
|
137
|
+
end
|
125
138
|
end
|
126
139
|
end
|
127
140
|
end
|