activerecord 4.2.11.3 → 5.0.0.beta1
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 +1029 -1349
- data/MIT-LICENSE +1 -1
- data/README.rdoc +6 -7
- data/examples/performance.rb +2 -2
- data/lib/active_record.rb +7 -3
- data/lib/active_record/aggregations.rb +35 -25
- data/lib/active_record/association_relation.rb +2 -2
- data/lib/active_record/associations.rb +305 -204
- data/lib/active_record/associations/alias_tracker.rb +19 -16
- data/lib/active_record/associations/association.rb +10 -8
- data/lib/active_record/associations/association_scope.rb +73 -102
- data/lib/active_record/associations/belongs_to_association.rb +20 -32
- data/lib/active_record/associations/builder/association.rb +28 -34
- data/lib/active_record/associations/builder/belongs_to.rb +41 -18
- data/lib/active_record/associations/builder/collection_association.rb +8 -24
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +11 -11
- data/lib/active_record/associations/builder/has_many.rb +4 -4
- data/lib/active_record/associations/builder/has_one.rb +10 -5
- data/lib/active_record/associations/builder/singular_association.rb +2 -9
- data/lib/active_record/associations/collection_association.rb +40 -43
- data/lib/active_record/associations/collection_proxy.rb +55 -29
- 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 -52
- data/lib/active_record/associations/has_one_association.rb +12 -5
- data/lib/active_record/associations/join_dependency.rb +28 -18
- data/lib/active_record/associations/join_dependency/join_association.rb +13 -12
- data/lib/active_record/associations/preloader.rb +13 -4
- data/lib/active_record/associations/preloader/association.rb +45 -51
- 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 +5 -4
- data/lib/active_record/associations/singular_association.rb +6 -0
- data/lib/active_record/associations/through_association.rb +11 -3
- data/lib/active_record/attribute.rb +61 -17
- data/lib/active_record/attribute/user_provided_default.rb +23 -0
- data/lib/active_record/attribute_assignment.rb +27 -140
- data/lib/active_record/attribute_decorators.rb +6 -5
- data/lib/active_record/attribute_methods.rb +79 -26
- 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 +26 -42
- data/lib/active_record/attribute_methods/serialization.rb +13 -16
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +42 -9
- data/lib/active_record/attribute_methods/write.rb +13 -24
- 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 +194 -81
- data/lib/active_record/autosave_association.rb +33 -15
- data/lib/active_record/base.rb +30 -18
- data/lib/active_record/callbacks.rb +36 -40
- data/lib/active_record/coders/yaml_column.rb +20 -8
- data/lib/active_record/collection_cache_key.rb +31 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +431 -122
- data/lib/active_record/connection_adapters/abstract/database_limits.rb +3 -3
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +40 -22
- data/lib/active_record/connection_adapters/abstract/quoting.rb +62 -8
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -38
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +229 -185
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +52 -13
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +275 -115
- data/lib/active_record/connection_adapters/abstract/transaction.rb +32 -33
- data/lib/active_record/connection_adapters/abstract_adapter.rb +83 -32
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +384 -221
- data/lib/active_record/connection_adapters/column.rb +27 -41
- data/lib/active_record/connection_adapters/connection_specification.rb +2 -21
- data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
- data/lib/active_record/connection_adapters/mysql/schema_creation.rb +57 -0
- data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +69 -0
- data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +59 -0
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +22 -101
- data/lib/active_record/connection_adapters/postgresql/column.rb +6 -10
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +3 -3
- data/lib/active_record/connection_adapters/postgresql/oid.rb +1 -6
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +23 -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 +1 -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 -2
- 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 +23 -16
- 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 +18 -11
- 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 +54 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +174 -128
- data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +184 -112
- 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/schema_creation.rb +15 -0
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +134 -110
- data/lib/active_record/connection_adapters/statement_pool.rb +28 -11
- data/lib/active_record/connection_handling.rb +5 -5
- data/lib/active_record/core.rb +72 -104
- data/lib/active_record/counter_cache.rb +9 -20
- data/lib/active_record/dynamic_matchers.rb +1 -20
- data/lib/active_record/enum.rb +110 -76
- data/lib/active_record/errors.rb +72 -47
- 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 +19 -4
- data/lib/active_record/fixtures.rb +76 -40
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +27 -40
- data/lib/active_record/integration.rb +4 -4
- 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 +10 -14
- data/lib/active_record/locking/pessimistic.rb +1 -1
- data/lib/active_record/log_subscriber.rb +40 -22
- data/lib/active_record/migration.rb +304 -133
- data/lib/active_record/migration/command_recorder.rb +59 -18
- data/lib/active_record/migration/compatibility.rb +90 -0
- data/lib/active_record/model_schema.rb +92 -40
- data/lib/active_record/nested_attributes.rb +45 -34
- data/lib/active_record/null_relation.rb +15 -7
- data/lib/active_record/persistence.rb +112 -72
- data/lib/active_record/querying.rb +6 -5
- data/lib/active_record/railtie.rb +20 -13
- data/lib/active_record/railties/controller_runtime.rb +1 -1
- data/lib/active_record/railties/databases.rake +47 -38
- data/lib/active_record/readonly_attributes.rb +1 -1
- data/lib/active_record/reflection.rb +182 -57
- data/lib/active_record/relation.rb +152 -100
- data/lib/active_record/relation/batches.rb +133 -33
- data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
- data/lib/active_record/relation/calculations.rb +80 -101
- data/lib/active_record/relation/delegation.rb +6 -19
- data/lib/active_record/relation/finder_methods.rb +58 -46
- data/lib/active_record/relation/from_clause.rb +32 -0
- data/lib/active_record/relation/merger.rb +13 -42
- data/lib/active_record/relation/predicate_builder.rb +99 -105
- data/lib/active_record/relation/predicate_builder/array_handler.rb +11 -16
- data/lib/active_record/relation/predicate_builder/association_query_handler.rb +78 -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/range_handler.rb +17 -0
- data/lib/active_record/relation/query_attribute.rb +19 -0
- data/lib/active_record/relation/query_methods.rb +274 -238
- data/lib/active_record/relation/record_fetch_warning.rb +51 -0
- data/lib/active_record/relation/spawn_methods.rb +3 -6
- data/lib/active_record/relation/where_clause.rb +173 -0
- data/lib/active_record/relation/where_clause_factory.rb +37 -0
- data/lib/active_record/result.rb +4 -3
- data/lib/active_record/runtime_registry.rb +1 -1
- data/lib/active_record/sanitization.rb +94 -65
- data/lib/active_record/schema.rb +23 -22
- data/lib/active_record/schema_dumper.rb +33 -22
- data/lib/active_record/schema_migration.rb +10 -4
- data/lib/active_record/scoping.rb +17 -6
- data/lib/active_record/scoping/default.rb +19 -6
- data/lib/active_record/scoping/named.rb +39 -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 +15 -13
- data/lib/active_record/store.rb +8 -3
- data/lib/active_record/suppressor.rb +54 -0
- data/lib/active_record/table_metadata.rb +64 -0
- data/lib/active_record/tasks/database_tasks.rb +30 -40
- data/lib/active_record/tasks/mysql_database_tasks.rb +7 -15
- 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 +16 -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 +33 -0
- data/lib/active_record/type/internal/timezone.rb +15 -0
- data/lib/active_record/type/serialized.rb +9 -14
- data/lib/active_record/type/time.rb +3 -21
- 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 +24 -0
- data/lib/active_record/validations/associated.rb +10 -3
- data/lib/active_record/validations/length.rb +36 -0
- data/lib/active_record/validations/presence.rb +12 -12
- data/lib/active_record/validations/uniqueness.rb +24 -21
- 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 +4 -1
- data/lib/rails/generators/active_record/model/model_generator.rb +21 -15
- data/lib/rails/generators/active_record/model/templates/model.rb +3 -0
- metadata +50 -35
- 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
|
@@ -37,12 +37,12 @@ module ActiveRecord
|
|
37
37
|
class AttributeMethodCache
|
38
38
|
def initialize
|
39
39
|
@module = Module.new
|
40
|
-
@method_cache =
|
40
|
+
@method_cache = Concurrent::Map.new
|
41
41
|
end
|
42
42
|
|
43
43
|
def [](name)
|
44
44
|
@method_cache.compute_if_absent(name) do
|
45
|
-
safe_name = name.unpack('h*').first
|
45
|
+
safe_name = name.unpack('h*'.freeze).first
|
46
46
|
temp_method = "__temp__#{safe_name}"
|
47
47
|
ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
|
48
48
|
@module.module_eval method_body(temp_method, safe_name), __FILE__, __LINE__
|
@@ -83,7 +83,7 @@ module ActiveRecord
|
|
83
83
|
generated_attribute_methods.synchronize do
|
84
84
|
return false if @attribute_methods_generated
|
85
85
|
superclass.define_attribute_methods unless self == base_class
|
86
|
-
super(
|
86
|
+
super(attribute_names)
|
87
87
|
@attribute_methods_generated = true
|
88
88
|
end
|
89
89
|
true
|
@@ -96,7 +96,7 @@ module ActiveRecord
|
|
96
96
|
end
|
97
97
|
end
|
98
98
|
|
99
|
-
# Raises
|
99
|
+
# Raises an ActiveRecord::DangerousAttributeError exception when an
|
100
100
|
# \Active \Record method is defined in the model, otherwise +false+.
|
101
101
|
#
|
102
102
|
# class Person < ActiveRecord::Base
|
@@ -106,7 +106,7 @@ module ActiveRecord
|
|
106
106
|
# end
|
107
107
|
#
|
108
108
|
# Person.instance_method_already_implemented?(:save)
|
109
|
-
# # => ActiveRecord::DangerousAttributeError: save is defined by
|
109
|
+
# # => 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
110
|
#
|
111
111
|
# Person.instance_method_already_implemented?(:name)
|
112
112
|
# # => false
|
@@ -150,7 +150,7 @@ module ActiveRecord
|
|
150
150
|
BLACKLISTED_CLASS_METHODS.include?(method_name.to_s) || class_method_defined_within?(method_name, Base)
|
151
151
|
end
|
152
152
|
|
153
|
-
def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc
|
153
|
+
def class_method_defined_within?(name, klass, superklass = klass.superclass) # :nodoc:
|
154
154
|
if klass.respond_to?(name, true)
|
155
155
|
if superklass.respond_to?(name, true)
|
156
156
|
klass.method(name).owner != superklass.method(name).owner
|
@@ -185,14 +185,27 @@ module ActiveRecord
|
|
185
185
|
# # => ["id", "created_at", "updated_at", "name", "age"]
|
186
186
|
def attribute_names
|
187
187
|
@attribute_names ||= if !abstract_class? && table_exists?
|
188
|
-
|
188
|
+
attribute_types.keys
|
189
189
|
else
|
190
190
|
[]
|
191
191
|
end
|
192
192
|
end
|
193
193
|
|
194
|
+
# Returns true if the given attribute exists, otherwise false.
|
195
|
+
#
|
196
|
+
# class Person < ActiveRecord::Base
|
197
|
+
# end
|
198
|
+
#
|
199
|
+
# Person.has_attribute?('name') # => true
|
200
|
+
# Person.has_attribute?(:age) # => true
|
201
|
+
# Person.has_attribute?(:nothing) # => false
|
202
|
+
def has_attribute?(attr_name)
|
203
|
+
attribute_types.key?(attr_name.to_s)
|
204
|
+
end
|
205
|
+
|
194
206
|
# Returns the column object for the named attribute.
|
195
|
-
# Returns
|
207
|
+
# Returns a +ActiveRecord::ConnectionAdapters::NullColumn+ if the
|
208
|
+
# named attribute does not exist.
|
196
209
|
#
|
197
210
|
# class Person < ActiveRecord::Base
|
198
211
|
# end
|
@@ -202,23 +215,18 @@ module ActiveRecord
|
|
202
215
|
# # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
|
203
216
|
#
|
204
217
|
# person.column_for_attribute(:nothing)
|
205
|
-
# # => nil
|
218
|
+
# # => #<ActiveRecord::ConnectionAdapters::NullColumn:0xXXX @name=nil, @sql_type=nil, @cast_type=#<Type::Value>, ...>
|
206
219
|
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
|
220
|
+
name = name.to_s
|
221
|
+
columns_hash.fetch(name) do
|
222
|
+
ConnectionAdapters::NullColumn.new(name)
|
214
223
|
end
|
215
|
-
column
|
216
224
|
end
|
217
225
|
end
|
218
226
|
|
219
227
|
# A Person object with a name attribute can ask <tt>person.respond_to?(:name)</tt>,
|
220
228
|
# <tt>person.respond_to?(:name=)</tt>, and <tt>person.respond_to?(:name?)</tt>
|
221
|
-
# which will all return +true+. It also
|
229
|
+
# which will all return +true+. It also defines the attribute methods if they have
|
222
230
|
# not been generated.
|
223
231
|
#
|
224
232
|
# class Person < ActiveRecord::Base
|
@@ -234,7 +242,15 @@ module ActiveRecord
|
|
234
242
|
# person.respond_to(:nothing) # => false
|
235
243
|
def respond_to?(name, include_private = false)
|
236
244
|
return false unless super
|
237
|
-
|
245
|
+
|
246
|
+
case name
|
247
|
+
when :to_partial_path
|
248
|
+
name = "to_partial_path".freeze
|
249
|
+
when :to_model
|
250
|
+
name = "to_model".freeze
|
251
|
+
else
|
252
|
+
name = name.to_s
|
253
|
+
end
|
238
254
|
|
239
255
|
# If the result is true then check for the select case.
|
240
256
|
# For queries selecting a subset of columns, return false for unselected columns.
|
@@ -287,8 +303,9 @@ module ActiveRecord
|
|
287
303
|
# Returns an <tt>#inspect</tt>-like string for the value of the
|
288
304
|
# attribute +attr_name+. String attributes are truncated up to 50
|
289
305
|
# characters, Date and Time attributes are returned in the
|
290
|
-
# <tt>:db</tt> format
|
291
|
-
# <tt>#inspect</tt> without
|
306
|
+
# <tt>:db</tt> format, Array attributes are truncated up to 10 values.
|
307
|
+
# Other attributes return the value of <tt>#inspect</tt> without
|
308
|
+
# modification.
|
292
309
|
#
|
293
310
|
# person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
|
294
311
|
#
|
@@ -299,7 +316,7 @@ module ActiveRecord
|
|
299
316
|
# # => "\"2012-10-22 00:15:07\""
|
300
317
|
#
|
301
318
|
# person.attribute_for_inspect(:tag_ids)
|
302
|
-
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
319
|
+
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, ...]"
|
303
320
|
def attribute_for_inspect(attr_name)
|
304
321
|
value = read_attribute(attr_name)
|
305
322
|
|
@@ -307,6 +324,9 @@ module ActiveRecord
|
|
307
324
|
"#{value[0, 50]}...".inspect
|
308
325
|
elsif value.is_a?(Date) || value.is_a?(Time)
|
309
326
|
%("#{value.to_s(:db)}")
|
327
|
+
elsif value.is_a?(Array) && value.size > 10
|
328
|
+
inspected = value.first(10).inspect
|
329
|
+
%(#{inspected[0...-1]}, ...])
|
310
330
|
else
|
311
331
|
value.inspect
|
312
332
|
end
|
@@ -338,7 +358,7 @@ module ActiveRecord
|
|
338
358
|
#
|
339
359
|
# Note: +:id+ is always present.
|
340
360
|
#
|
341
|
-
# Alias for the
|
361
|
+
# Alias for the #read_attribute method.
|
342
362
|
#
|
343
363
|
# class Person < ActiveRecord::Base
|
344
364
|
# belongs_to :organization
|
@@ -356,7 +376,7 @@ module ActiveRecord
|
|
356
376
|
end
|
357
377
|
|
358
378
|
# Updates the attribute identified by <tt>attr_name</tt> with the specified +value+.
|
359
|
-
# (Alias for the protected
|
379
|
+
# (Alias for the protected #write_attribute method).
|
360
380
|
#
|
361
381
|
# class Person < ActiveRecord::Base
|
362
382
|
# end
|
@@ -364,11 +384,44 @@ module ActiveRecord
|
|
364
384
|
# person = Person.new
|
365
385
|
# person[:age] = '22'
|
366
386
|
# person[:age] # => 22
|
367
|
-
# person[:age]
|
387
|
+
# person[:age] # => Fixnum
|
368
388
|
def []=(attr_name, value)
|
369
389
|
write_attribute(attr_name, value)
|
370
390
|
end
|
371
391
|
|
392
|
+
# Returns the name of all database fields which have been read from this
|
393
|
+
# model. This can be useful in development mode to determine which fields
|
394
|
+
# need to be selected. For performance critical pages, selecting only the
|
395
|
+
# required fields can be an easy performance win (assuming you aren't using
|
396
|
+
# all of the fields on the model).
|
397
|
+
#
|
398
|
+
# For example:
|
399
|
+
#
|
400
|
+
# class PostsController < ActionController::Base
|
401
|
+
# after_action :print_accessed_fields, only: :index
|
402
|
+
#
|
403
|
+
# def index
|
404
|
+
# @posts = Post.all
|
405
|
+
# end
|
406
|
+
#
|
407
|
+
# private
|
408
|
+
#
|
409
|
+
# def print_accessed_fields
|
410
|
+
# p @posts.first.accessed_fields
|
411
|
+
# end
|
412
|
+
# end
|
413
|
+
#
|
414
|
+
# Which allows you to quickly change your code to:
|
415
|
+
#
|
416
|
+
# class PostsController < ActionController::Base
|
417
|
+
# def index
|
418
|
+
# @posts = Post.select(:id, :title, :author_id, :updated_at)
|
419
|
+
# end
|
420
|
+
# end
|
421
|
+
def accessed_fields
|
422
|
+
@attributes.accessed
|
423
|
+
end
|
424
|
+
|
372
425
|
protected
|
373
426
|
|
374
427
|
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
|
@@ -108,7 +108,7 @@ module ActiveRecord
|
|
108
108
|
# self.primary_key = 'sysid'
|
109
109
|
# end
|
110
110
|
#
|
111
|
-
# You can also define the
|
111
|
+
# You can also define the #primary_key method yourself:
|
112
112
|
#
|
113
113
|
# class Project < ActiveRecord::Base
|
114
114
|
# def self.primary_key
|