activerecord 4.2.11.1 → 5.0.0
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 +1282 -1195
- 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 +8 -4
- data/lib/active_record/aggregations.rb +35 -24
- data/lib/active_record/association_relation.rb +3 -3
- data/lib/active_record/associations.rb +317 -209
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +11 -9
- data/lib/active_record/associations/association_scope.rb +73 -102
- 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 +14 -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 +3 -10
- data/lib/active_record/associations/collection_association.rb +49 -41
- data/lib/active_record/associations/collection_proxy.rb +67 -27
- data/lib/active_record/associations/foreign_association.rb +1 -1
- data/lib/active_record/associations/has_many_association.rb +20 -71
- data/lib/active_record/associations/has_many_through_association.rb +8 -47
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +29 -19
- data/lib/active_record/associations/join_dependency/join_association.rb +16 -10
- data/lib/active_record/associations/preloader.rb +14 -4
- data/lib/active_record/associations/preloader/association.rb +46 -52
- data/lib/active_record/associations/preloader/collection_association.rb +0 -6
- 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/through_association.rb +27 -14
- data/lib/active_record/associations/singular_association.rb +7 -1
- data/lib/active_record/associations/through_association.rb +11 -3
- data/lib/active_record/attribute.rb +68 -18
- 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 +76 -47
- 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 +2 -2
- 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 +30 -3
- data/lib/active_record/attribute_set/builder.rb +6 -4
- data/lib/active_record/attributes.rb +199 -81
- data/lib/active_record/autosave_association.rb +49 -16
- 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 +40 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +452 -182
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +65 -61
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +2 -2
- data/lib/active_record/connection_adapters/abstract/quoting.rb +74 -10
- 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 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +72 -17
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +380 -141
- data/lib/active_record/connection_adapters/abstract/transaction.rb +51 -34
- data/lib/active_record/connection_adapters/abstract_adapter.rb +141 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +401 -370
- 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 +125 -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 +29 -166
- data/lib/active_record/connection_adapters/postgresql/column.rb +5 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +10 -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 +27 -57
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +2 -2
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +1 -1
- 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 +3 -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 +26 -18
- 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 +234 -148
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +248 -160
- 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 +149 -192
- 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 +89 -107
- data/lib/active_record/counter_cache.rb +13 -24
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +113 -76
- data/lib/active_record/errors.rb +87 -48
- 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 +76 -40
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +32 -40
- data/lib/active_record/integration.rb +4 -4
- 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 +43 -21
- data/lib/active_record/migration.rb +363 -133
- 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 +129 -41
- data/lib/active_record/nested_attributes.rb +58 -29
- data/lib/active_record/null_relation.rb +16 -8
- data/lib/active_record/persistence.rb +121 -80
- data/lib/active_record/query_cache.rb +15 -18
- data/lib/active_record/querying.rb +10 -9
- 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 +69 -46
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +282 -115
- data/lib/active_record/relation.rb +176 -116
- data/lib/active_record/relation/batches.rb +139 -34
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +79 -108
- data/lib/active_record/relation/delegation.rb +7 -20
- data/lib/active_record/relation/finder_methods.rb +163 -81
- 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 +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +95 -66
- data/lib/active_record/schema.rb +26 -22
- data/lib/active_record/schema_dumper.rb +62 -38
- data/lib/active_record/schema_migration.rb +11 -14
- data/lib/active_record/scoping.rb +32 -15
- data/lib/active_record/scoping/default.rb +23 -9
- data/lib/active_record/scoping/named.rb +49 -28
- data/lib/active_record/secure_token.rb +38 -0
- data/lib/active_record/serialization.rb +2 -4
- data/lib/active_record/statement_cache.rb +16 -14
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +58 -0
- data/lib/active_record/table_metadata.rb +68 -0
- data/lib/active_record/tasks/database_tasks.rb +57 -43
- data/lib/active_record/tasks/mysql_database_tasks.rb +6 -14
- data/lib/active_record/tasks/postgresql_database_tasks.rb +11 -2
- 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 +58 -0
- data/lib/active_record/transactions.rb +138 -56
- 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 +29 -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 +30 -29
- data/lib/rails/generators/active_record/migration.rb +7 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +7 -4
- 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 +32 -15
- 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 +59 -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.
|
@@ -287,8 +279,9 @@ module ActiveRecord
|
|
287
279
|
# Returns an <tt>#inspect</tt>-like string for the value of the
|
288
280
|
# attribute +attr_name+. String attributes are truncated up to 50
|
289
281
|
# characters, Date and Time attributes are returned in the
|
290
|
-
# <tt>:db</tt> format
|
291
|
-
# <tt>#inspect</tt> without
|
282
|
+
# <tt>:db</tt> format, Array attributes are truncated up to 10 values.
|
283
|
+
# Other attributes return the value of <tt>#inspect</tt> without
|
284
|
+
# modification.
|
292
285
|
#
|
293
286
|
# person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
|
294
287
|
#
|
@@ -299,7 +292,7 @@ module ActiveRecord
|
|
299
292
|
# # => "\"2012-10-22 00:15:07\""
|
300
293
|
#
|
301
294
|
# person.attribute_for_inspect(:tag_ids)
|
302
|
-
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
295
|
+
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]"
|
303
296
|
def attribute_for_inspect(attr_name)
|
304
297
|
value = read_attribute(attr_name)
|
305
298
|
|
@@ -307,6 +300,9 @@ module ActiveRecord
|
|
307
300
|
"#{value[0, 50]}...".inspect
|
308
301
|
elsif value.is_a?(Date) || value.is_a?(Time)
|
309
302
|
%("#{value.to_s(:db)}")
|
303
|
+
elsif value.is_a?(Array) && value.size > 10
|
304
|
+
inspected = value.first(10).inspect
|
305
|
+
%(#{inspected[0...-1]}, ...])
|
310
306
|
else
|
311
307
|
value.inspect
|
312
308
|
end
|
@@ -338,7 +334,7 @@ module ActiveRecord
|
|
338
334
|
#
|
339
335
|
# Note: +:id+ is always present.
|
340
336
|
#
|
341
|
-
# Alias for the
|
337
|
+
# Alias for the #read_attribute method.
|
342
338
|
#
|
343
339
|
# class Person < ActiveRecord::Base
|
344
340
|
# belongs_to :organization
|
@@ -356,7 +352,7 @@ module ActiveRecord
|
|
356
352
|
end
|
357
353
|
|
358
354
|
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
|
359
|
-
# (Alias for the protected
|
355
|
+
# (Alias for the protected #write_attribute method).
|
360
356
|
#
|
361
357
|
# class Person < ActiveRecord::Base
|
362
358
|
# end
|
@@ -369,6 +365,39 @@ module ActiveRecord
|
|
369
365
|
write_attribute(attr_name, value)
|
370
366
|
end
|
371
367
|
|
368
|
+
# Returns the name of all database fields which have been read from this
|
369
|
+
# model. This can be useful in development mode to determine which fields
|
370
|
+
# need to be selected. For performance critical pages, selecting only the
|
371
|
+
# required fields can be an easy performance win (assuming you aren't using
|
372
|
+
# all of the fields on the model).
|
373
|
+
#
|
374
|
+
# For example:
|
375
|
+
#
|
376
|
+
# class PostsController < ActionController::Base
|
377
|
+
# after_action :print_accessed_fields, only: :index
|
378
|
+
#
|
379
|
+
# def index
|
380
|
+
# @posts = Post.all
|
381
|
+
# end
|
382
|
+
#
|
383
|
+
# private
|
384
|
+
#
|
385
|
+
# def print_accessed_fields
|
386
|
+
# p @posts.first.accessed_fields
|
387
|
+
# end
|
388
|
+
# end
|
389
|
+
#
|
390
|
+
# Which allows you to quickly change your code to:
|
391
|
+
#
|
392
|
+
# class PostsController < ActionController::Base
|
393
|
+
# def index
|
394
|
+
# @posts = Post.select(:id, :title, :author_id, :updated_at)
|
395
|
+
# end
|
396
|
+
# end
|
397
|
+
def accessed_fields
|
398
|
+
@attributes.accessed
|
399
|
+
end
|
400
|
+
|
372
401
|
protected
|
373
402
|
|
374
403
|
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
|