activerecord 4.1.0 → 4.2.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 +4 -4
- data/CHANGELOG.md +776 -1330
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/association_relation.rb +4 -0
- data/lib/active_record/associations/alias_tracker.rb +14 -13
- data/lib/active_record/associations/association.rb +2 -2
- data/lib/active_record/associations/association_scope.rb +83 -43
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +15 -4
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +9 -6
- data/lib/active_record/associations/builder/has_many.rb +1 -1
- data/lib/active_record/associations/builder/has_one.rb +2 -2
- data/lib/active_record/associations/builder/singular_association.rb +8 -1
- data/lib/active_record/associations/collection_association.rb +66 -29
- data/lib/active_record/associations/collection_proxy.rb +22 -26
- data/lib/active_record/associations/has_many_association.rb +65 -18
- data/lib/active_record/associations/has_many_through_association.rb +55 -27
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +19 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +20 -12
- data/lib/active_record/associations/preloader/association.rb +34 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +49 -59
- data/lib/active_record/associations/singular_association.rb +25 -4
- data/lib/active_record/associations/through_association.rb +23 -14
- data/lib/active_record/associations.rb +171 -42
- data/lib/active_record/attribute.rb +149 -0
- data/lib/active_record/attribute_assignment.rb +18 -10
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +98 -44
- data/lib/active_record/attribute_methods/primary_key.rb +14 -8
- data/lib/active_record/attribute_methods/query.rb +1 -1
- data/lib/active_record/attribute_methods/read.rb +22 -59
- data/lib/active_record/attribute_methods/serialization.rb +37 -147
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +34 -28
- data/lib/active_record/attribute_methods/write.rb +14 -21
- data/lib/active_record/attribute_methods.rb +67 -94
- data/lib/active_record/attribute_set/builder.rb +86 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +139 -0
- data/lib/active_record/autosave_association.rb +45 -38
- data/lib/active_record/base.rb +10 -20
- data/lib/active_record/callbacks.rb +7 -7
- data/lib/active_record/coders/json.rb +13 -0
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +78 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +38 -59
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -0
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -55
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +46 -5
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +126 -54
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +198 -64
- data/lib/active_record/connection_adapters/abstract/transaction.rb +126 -114
- data/lib/active_record/connection_adapters/abstract_adapter.rb +154 -55
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +240 -135
- data/lib/active_record/connection_adapters/column.rb +28 -239
- data/lib/active_record/connection_adapters/connection_specification.rb +16 -25
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +20 -22
- data/lib/active_record/connection_adapters/mysql_adapter.rb +65 -149
- data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
- data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
- data/lib/active_record/connection_adapters/postgresql/database_statements.rb +39 -27
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +99 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +14 -0
- data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +27 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +17 -0
- data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
- data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
- data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
- data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
- data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
- data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
- data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
- data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +97 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
- data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
- data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
- data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -374
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +55 -135
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +127 -38
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +220 -466
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +66 -61
- data/lib/active_record/connection_handling.rb +3 -3
- data/lib/active_record/core.rb +143 -32
- data/lib/active_record/counter_cache.rb +60 -7
- data/lib/active_record/enum.rb +10 -11
- data/lib/active_record/errors.rb +49 -27
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +56 -70
- data/lib/active_record/gem_version.rb +2 -2
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +35 -17
- data/lib/active_record/log_subscriber.rb +1 -1
- data/lib/active_record/migration/command_recorder.rb +19 -2
- data/lib/active_record/migration/join_table.rb +1 -1
- data/lib/active_record/migration.rb +52 -49
- data/lib/active_record/model_schema.rb +49 -57
- data/lib/active_record/nested_attributes.rb +7 -7
- data/lib/active_record/null_relation.rb +19 -5
- data/lib/active_record/persistence.rb +50 -31
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +14 -11
- data/lib/active_record/railties/databases.rake +56 -54
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +286 -102
- data/lib/active_record/relation/batches.rb +0 -1
- data/lib/active_record/relation/calculations.rb +39 -31
- data/lib/active_record/relation/delegation.rb +2 -2
- data/lib/active_record/relation/finder_methods.rb +80 -36
- data/lib/active_record/relation/merger.rb +25 -30
- data/lib/active_record/relation/predicate_builder/array_handler.rb +31 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
- data/lib/active_record/relation/predicate_builder.rb +11 -10
- data/lib/active_record/relation/query_methods.rb +141 -55
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +69 -30
- data/lib/active_record/result.rb +18 -7
- data/lib/active_record/sanitization.rb +12 -2
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +58 -26
- data/lib/active_record/schema_migration.rb +11 -0
- data/lib/active_record/scoping/default.rb +8 -7
- data/lib/active_record/scoping/named.rb +4 -0
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +95 -10
- data/lib/active_record/store.rb +19 -10
- data/lib/active_record/tasks/database_tasks.rb +73 -7
- data/lib/active_record/tasks/mysql_database_tasks.rb +3 -2
- data/lib/active_record/tasks/postgresql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/sqlite_database_tasks.rb +5 -1
- data/lib/active_record/timestamp.rb +11 -9
- data/lib/active_record/transactions.rb +37 -21
- data/lib/active_record/type/big_integer.rb +13 -0
- data/lib/active_record/type/binary.rb +50 -0
- data/lib/active_record/type/boolean.rb +30 -0
- data/lib/active_record/type/date.rb +46 -0
- data/lib/active_record/type/date_time.rb +43 -0
- data/lib/active_record/type/decimal.rb +40 -0
- data/lib/active_record/type/decimal_without_scale.rb +11 -0
- data/lib/active_record/type/decorator.rb +14 -0
- data/lib/active_record/type/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +17 -0
- data/lib/active_record/type/integer.rb +55 -0
- data/lib/active_record/type/mutable.rb +16 -0
- data/lib/active_record/type/numeric.rb +36 -0
- data/lib/active_record/type/serialized.rb +56 -0
- data/lib/active_record/type/string.rb +36 -0
- data/lib/active_record/type/text.rb +11 -0
- data/lib/active_record/type/time.rb +26 -0
- data/lib/active_record/type/time_value.rb +38 -0
- data/lib/active_record/type/type_map.rb +64 -0
- data/lib/active_record/type/unsigned_integer.rb +15 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/type.rb +23 -0
- data/lib/active_record/validations/associated.rb +5 -3
- data/lib/active_record/validations/presence.rb +6 -4
- data/lib/active_record/validations/uniqueness.rb +11 -17
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +3 -0
- data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
- data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +4 -1
- data/lib/rails/generators/active_record/migration/templates/migration.rb +6 -0
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +65 -10
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -11,6 +11,15 @@ module ActiveRecord
|
|
11
11
|
# If the passed hash responds to <tt>permitted?</tt> method and the return value
|
12
12
|
# of this method is +false+ an <tt>ActiveModel::ForbiddenAttributesError</tt>
|
13
13
|
# exception is raised.
|
14
|
+
#
|
15
|
+
# cat = Cat.new(name: "Gorby", status: "yawning")
|
16
|
+
# cat.attributes # => { "name" => "Gorby", "status" => "yawning", "created_at" => nil, "updated_at" => nil}
|
17
|
+
# cat.assign_attributes(status: "sleeping")
|
18
|
+
# cat.attributes # => { "name" => "Gorby", "status" => "sleeping", "created_at" => nil, "updated_at" => nil }
|
19
|
+
#
|
20
|
+
# New attributes will be persisted in the database when the object is saved.
|
21
|
+
#
|
22
|
+
# Aliased to <tt>attributes=</tt>.
|
14
23
|
def assign_attributes(new_attributes)
|
15
24
|
if !new_attributes.respond_to?(:stringify_keys)
|
16
25
|
raise ArgumentError, "When assigning attributes, you must pass a hash as an argument."
|
@@ -106,7 +115,7 @@ module ActiveRecord
|
|
106
115
|
end
|
107
116
|
|
108
117
|
class MultiparameterAttribute #:nodoc:
|
109
|
-
attr_reader :object, :name, :values, :
|
118
|
+
attr_reader :object, :name, :values, :cast_type
|
110
119
|
|
111
120
|
def initialize(object, name, values)
|
112
121
|
@object = object
|
@@ -117,22 +126,22 @@ module ActiveRecord
|
|
117
126
|
def read_value
|
118
127
|
return if values.values.compact.empty?
|
119
128
|
|
120
|
-
@
|
121
|
-
klass
|
129
|
+
@cast_type = object.type_for_attribute(name)
|
130
|
+
klass = cast_type.klass
|
122
131
|
|
123
132
|
if klass == Time
|
124
133
|
read_time
|
125
134
|
elsif klass == Date
|
126
135
|
read_date
|
127
136
|
else
|
128
|
-
read_other
|
137
|
+
read_other
|
129
138
|
end
|
130
139
|
end
|
131
140
|
|
132
141
|
private
|
133
142
|
|
134
143
|
def instantiate_time_object(set_values)
|
135
|
-
if object.class.send(:create_time_zone_conversion_attribute?, name,
|
144
|
+
if object.class.send(:create_time_zone_conversion_attribute?, name, cast_type)
|
136
145
|
Time.zone.local(*set_values)
|
137
146
|
else
|
138
147
|
Time.send(object.class.default_timezone, *set_values)
|
@@ -140,9 +149,9 @@ module ActiveRecord
|
|
140
149
|
end
|
141
150
|
|
142
151
|
def read_time
|
143
|
-
# If column is a :time (and not :date or :
|
152
|
+
# If column is a :time (and not :date or :datetime) there is no need to validate if
|
144
153
|
# there are year/month/day fields
|
145
|
-
if
|
154
|
+
if cast_type.type == :time
|
146
155
|
# if the column is a time set the values to their defaults as January 1, 1970, but only if they're nil
|
147
156
|
{ 1 => 1970, 2 => 1, 3 => 1 }.each do |key,value|
|
148
157
|
values[key] ||= value
|
@@ -172,13 +181,12 @@ module ActiveRecord
|
|
172
181
|
end
|
173
182
|
end
|
174
183
|
|
175
|
-
def read_other
|
184
|
+
def read_other
|
176
185
|
max_position = extract_max_param
|
177
186
|
positions = (1..max_position)
|
178
187
|
validate_required_parameters!(positions)
|
179
188
|
|
180
|
-
|
181
|
-
klass.new(*set_values)
|
189
|
+
values.slice(*positions)
|
182
190
|
end
|
183
191
|
|
184
192
|
# Checks whether some blank date parameter exists. Note that this is different
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module AttributeDecorators # :nodoc:
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
included do
|
6
|
+
class_attribute :attribute_type_decorations, instance_accessor: false # :internal:
|
7
|
+
self.attribute_type_decorations = TypeDecorator.new
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods # :nodoc:
|
11
|
+
def decorate_attribute_type(column_name, decorator_name, &block)
|
12
|
+
matcher = ->(name, _) { name == column_name.to_s }
|
13
|
+
key = "_#{column_name}_#{decorator_name}"
|
14
|
+
decorate_matching_attribute_types(matcher, key, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
def decorate_matching_attribute_types(matcher, decorator_name, &block)
|
18
|
+
clear_caches_calculated_from_columns
|
19
|
+
decorator_name = decorator_name.to_s
|
20
|
+
|
21
|
+
# Create new hashes so we don't modify parent classes
|
22
|
+
self.attribute_type_decorations = attribute_type_decorations.merge(decorator_name => [matcher, block])
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def add_user_provided_columns(*)
|
28
|
+
super.map do |column|
|
29
|
+
decorated_type = attribute_type_decorations.apply(column.name, column.cast_type)
|
30
|
+
column.with_type(decorated_type)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class TypeDecorator # :nodoc:
|
36
|
+
delegate :clear, to: :@decorations
|
37
|
+
|
38
|
+
def initialize(decorations = {})
|
39
|
+
@decorations = decorations
|
40
|
+
end
|
41
|
+
|
42
|
+
def merge(*args)
|
43
|
+
TypeDecorator.new(@decorations.merge(*args))
|
44
|
+
end
|
45
|
+
|
46
|
+
def apply(name, type)
|
47
|
+
decorations = decorators_for(name, type)
|
48
|
+
decorations.inject(type) do |new_type, block|
|
49
|
+
block.call(new_type)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def decorators_for(name, type)
|
56
|
+
matching(name, type).map(&:last)
|
57
|
+
end
|
58
|
+
|
59
|
+
def matching(name, type)
|
60
|
+
@decorations.values.select do |(matcher, _)|
|
61
|
+
matcher.call(name, type)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -43,7 +43,7 @@ module ActiveRecord
|
|
43
43
|
# task.read_attribute_before_type_cast('completed_on') # => "2012-10-21"
|
44
44
|
# task.read_attribute_before_type_cast(:completed_on) # => "2012-10-21"
|
45
45
|
def read_attribute_before_type_cast(attr_name)
|
46
|
-
@attributes[attr_name.to_s]
|
46
|
+
@attributes[attr_name.to_s].value_before_type_cast
|
47
47
|
end
|
48
48
|
|
49
49
|
# Returns a hash of attributes before typecasting and deserialization.
|
@@ -57,7 +57,7 @@ module ActiveRecord
|
|
57
57
|
# task.attributes_before_type_cast
|
58
58
|
# # => {"id"=>nil, "title"=>nil, "is_done"=>true, "completed_on"=>"2012-10-21", "created_at"=>nil, "updated_at"=>nil}
|
59
59
|
def attributes_before_type_cast
|
60
|
-
@attributes
|
60
|
+
@attributes.values_before_type_cast
|
61
61
|
end
|
62
62
|
|
63
63
|
private
|
@@ -34,28 +34,52 @@ module ActiveRecord
|
|
34
34
|
# <tt>reload</tt> the record and clears changed attributes.
|
35
35
|
def reload(*)
|
36
36
|
super.tap do
|
37
|
-
|
37
|
+
clear_changes_information
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
41
|
+
def initialize_dup(other) # :nodoc:
|
42
|
+
super
|
43
|
+
calculate_changes_from_defaults
|
44
|
+
end
|
45
45
|
|
46
|
-
|
47
|
-
def initialize_internals_callback
|
46
|
+
def changes_applied
|
48
47
|
super
|
49
|
-
|
48
|
+
store_original_raw_attributes
|
49
|
+
end
|
50
|
+
|
51
|
+
def clear_changes_information
|
52
|
+
super
|
53
|
+
original_raw_attributes.clear
|
54
|
+
end
|
55
|
+
|
56
|
+
def changed_attributes
|
57
|
+
# This should only be set by methods which will call changed_attributes
|
58
|
+
# multiple times when it is known that the computed value cannot change.
|
59
|
+
if defined?(@cached_changed_attributes)
|
60
|
+
@cached_changed_attributes
|
61
|
+
else
|
62
|
+
super.reverse_merge(attributes_changed_in_place).freeze
|
63
|
+
end
|
50
64
|
end
|
51
65
|
|
52
|
-
def
|
66
|
+
def changes
|
67
|
+
cache_changed_attributes do
|
68
|
+
super
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def attribute_changed_in_place?(attr_name)
|
73
|
+
old_value = original_raw_attribute(attr_name)
|
74
|
+
@attributes[attr_name].changed_in_place_from?(old_value)
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def calculate_changes_from_defaults
|
53
80
|
@changed_attributes = nil
|
54
|
-
|
55
|
-
|
56
|
-
self.class.columns.each do |c|
|
57
|
-
attr, orig_value = c.name, c.default
|
58
|
-
changed_attributes[attr] = orig_value if _field_changed?(attr, orig_value, @attributes[attr])
|
81
|
+
self.class.column_defaults.each do |attr, orig_value|
|
82
|
+
set_attribute_was(attr, orig_value) if _field_changed?(attr, orig_value)
|
59
83
|
end
|
60
84
|
end
|
61
85
|
|
@@ -63,27 +87,43 @@ module ActiveRecord
|
|
63
87
|
def write_attribute(attr, value)
|
64
88
|
attr = attr.to_s
|
65
89
|
|
66
|
-
|
90
|
+
old_value = old_attribute_value(attr)
|
67
91
|
|
68
|
-
super
|
92
|
+
result = super
|
93
|
+
store_original_raw_attribute(attr)
|
94
|
+
save_changed_attribute(attr, old_value)
|
95
|
+
result
|
69
96
|
end
|
70
97
|
|
71
|
-
def
|
72
|
-
|
98
|
+
def raw_write_attribute(attr, value)
|
99
|
+
attr = attr.to_s
|
100
|
+
|
101
|
+
result = super
|
102
|
+
original_raw_attributes[attr] = value
|
103
|
+
result
|
104
|
+
end
|
105
|
+
|
106
|
+
def save_changed_attribute(attr, old_value)
|
73
107
|
if attribute_changed?(attr)
|
74
|
-
|
75
|
-
changed_attributes.delete(attr) unless _field_changed?(attr, old, value)
|
108
|
+
clear_attribute_changes(attr) unless _field_changed?(attr, old_value)
|
76
109
|
else
|
77
|
-
|
78
|
-
changed_attributes[attr] = old if _field_changed?(attr, old, value)
|
110
|
+
set_attribute_was(attr, old_value) if _field_changed?(attr, old_value)
|
79
111
|
end
|
80
112
|
end
|
81
113
|
|
82
|
-
def
|
114
|
+
def old_attribute_value(attr)
|
115
|
+
if attribute_changed?(attr)
|
116
|
+
changed_attributes[attr]
|
117
|
+
else
|
118
|
+
clone_attribute_value(:_read_attribute, attr)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def _update_record(*)
|
83
123
|
partial_writes? ? super(keys_for_partial_write) : super
|
84
124
|
end
|
85
125
|
|
86
|
-
def
|
126
|
+
def _create_record(*)
|
87
127
|
partial_writes? ? super(keys_for_partial_write) : super
|
88
128
|
end
|
89
129
|
|
@@ -93,34 +133,48 @@ module ActiveRecord
|
|
93
133
|
changed
|
94
134
|
end
|
95
135
|
|
96
|
-
def _field_changed?(attr,
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
136
|
+
def _field_changed?(attr, old_value)
|
137
|
+
@attributes[attr].changed_from?(old_value)
|
138
|
+
end
|
139
|
+
|
140
|
+
def attributes_changed_in_place
|
141
|
+
changed_in_place.each_with_object({}) do |attr_name, h|
|
142
|
+
orig = @attributes[attr_name].original_value
|
143
|
+
h[attr_name] = orig
|
104
144
|
end
|
145
|
+
end
|
146
|
+
|
147
|
+
def changed_in_place
|
148
|
+
self.class.attribute_names.select do |attr_name|
|
149
|
+
attribute_changed_in_place?(attr_name)
|
150
|
+
end
|
151
|
+
end
|
105
152
|
|
106
|
-
|
153
|
+
def original_raw_attribute(attr_name)
|
154
|
+
original_raw_attributes.fetch(attr_name) do
|
155
|
+
read_attribute_before_type_cast(attr_name)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def original_raw_attributes
|
160
|
+
@original_raw_attributes ||= {}
|
107
161
|
end
|
108
162
|
|
109
|
-
def
|
110
|
-
|
111
|
-
# Hence we don't record it as a change if the value changes from nil to ''.
|
112
|
-
# If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
|
113
|
-
# be typecast back to 0 (''.to_i => 0)
|
114
|
-
column.null && (old.nil? || old == 0) && value.blank?
|
163
|
+
def store_original_raw_attribute(attr_name)
|
164
|
+
original_raw_attributes[attr_name] = @attributes[attr_name].value_for_database
|
115
165
|
end
|
116
166
|
|
117
|
-
def
|
118
|
-
|
119
|
-
|
167
|
+
def store_original_raw_attributes
|
168
|
+
attribute_names.each do |attr|
|
169
|
+
store_original_raw_attribute(attr)
|
170
|
+
end
|
120
171
|
end
|
121
172
|
|
122
|
-
def
|
123
|
-
|
173
|
+
def cache_changed_attributes
|
174
|
+
@cached_changed_attributes = changed_attributes
|
175
|
+
yield
|
176
|
+
ensure
|
177
|
+
remove_instance_variable(:@cached_changed_attributes)
|
124
178
|
end
|
125
179
|
end
|
126
180
|
end
|
@@ -15,8 +15,10 @@ module ActiveRecord
|
|
15
15
|
|
16
16
|
# Returns the primary key value.
|
17
17
|
def id
|
18
|
-
|
19
|
-
|
18
|
+
if pk = self.class.primary_key
|
19
|
+
sync_with_transaction_state
|
20
|
+
_read_attribute(pk)
|
21
|
+
end
|
20
22
|
end
|
21
23
|
|
22
24
|
# Sets the primary key value.
|
@@ -37,6 +39,12 @@ module ActiveRecord
|
|
37
39
|
read_attribute_before_type_cast(self.class.primary_key)
|
38
40
|
end
|
39
41
|
|
42
|
+
# Returns the primary key previous value.
|
43
|
+
def id_was
|
44
|
+
sync_with_transaction_state
|
45
|
+
attribute_was(self.class.primary_key)
|
46
|
+
end
|
47
|
+
|
40
48
|
protected
|
41
49
|
|
42
50
|
def attribute_method?(attr_name)
|
@@ -52,7 +60,7 @@ module ActiveRecord
|
|
52
60
|
end
|
53
61
|
end
|
54
62
|
|
55
|
-
ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast).to_set
|
63
|
+
ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was).to_set
|
56
64
|
|
57
65
|
def dangerous_attribute_method?(method_name)
|
58
66
|
super && !ID_ATTRIBUTE_METHODS.include?(method_name)
|
@@ -81,12 +89,9 @@ module ActiveRecord
|
|
81
89
|
end
|
82
90
|
|
83
91
|
def get_primary_key(base_name) #:nodoc:
|
84
|
-
|
85
|
-
|
86
|
-
case primary_key_prefix_type
|
87
|
-
when :table_name
|
92
|
+
if base_name && primary_key_prefix_type == :table_name
|
88
93
|
base_name.foreign_key(false)
|
89
|
-
|
94
|
+
elsif base_name && primary_key_prefix_type == :table_name_with_underscore
|
90
95
|
base_name.foreign_key
|
91
96
|
else
|
92
97
|
if ActiveRecord::Base != self && table_exists?
|
@@ -115,6 +120,7 @@ module ActiveRecord
|
|
115
120
|
def primary_key=(value)
|
116
121
|
@primary_key = value && value.to_s
|
117
122
|
@quoted_primary_key = nil
|
123
|
+
@attributes_builder = nil
|
118
124
|
end
|
119
125
|
end
|
120
126
|
end
|
@@ -22,12 +22,12 @@ module ActiveRecord
|
|
22
22
|
# the attribute name. Using a constant means that we do not have
|
23
23
|
# to allocate an object on each call to the attribute method.
|
24
24
|
# Making it frozen means that it doesn't get duped when used to
|
25
|
-
# key the @
|
25
|
+
# key the @attributes in read_attribute.
|
26
26
|
def method_body(method_name, const_name)
|
27
27
|
<<-EOMETHOD
|
28
28
|
def #{method_name}
|
29
29
|
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
|
30
|
-
|
30
|
+
_read_attribute(name) { |n| missing_attribute(n, caller) }
|
31
31
|
end
|
32
32
|
EOMETHOD
|
33
33
|
end
|
@@ -35,35 +35,20 @@ module ActiveRecord
|
|
35
35
|
|
36
36
|
extend ActiveSupport::Concern
|
37
37
|
|
38
|
-
ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
|
39
|
-
|
40
|
-
included do
|
41
|
-
class_attribute :attribute_types_cached_by_default, instance_writer: false
|
42
|
-
self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
|
43
|
-
end
|
44
|
-
|
45
38
|
module ClassMethods
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
cached_attributes.merge attribute_names.map { |attr| attr.to_s }
|
39
|
+
[:cache_attributes, :cached_attributes, :cache_attribute?].each do |method_name|
|
40
|
+
define_method method_name do |*|
|
41
|
+
cached_attributes_deprecation_warning(method_name)
|
42
|
+
true
|
43
|
+
end
|
52
44
|
end
|
53
45
|
|
54
|
-
|
55
|
-
# with datatype <tt>:datetime, :timestamp, :time, :date</tt> are cached.
|
56
|
-
def cached_attributes
|
57
|
-
@cached_attributes ||= columns.select { |c| cacheable_column?(c) }.map { |col| col.name }.to_set
|
58
|
-
end
|
46
|
+
protected
|
59
47
|
|
60
|
-
|
61
|
-
|
62
|
-
cached_attributes.include?(attr_name)
|
48
|
+
def cached_attributes_deprecation_warning(method_name)
|
49
|
+
ActiveSupport::Deprecation.warn "Calling `#{method_name}` is no longer necessary. All attributes are cached."
|
63
50
|
end
|
64
51
|
|
65
|
-
protected
|
66
|
-
|
67
52
|
if Module.methods_transplantable?
|
68
53
|
def define_method_attribute(name)
|
69
54
|
method = ReaderMethodCache[name]
|
@@ -79,7 +64,7 @@ module ActiveRecord
|
|
79
64
|
generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
|
80
65
|
def #{temp_method}
|
81
66
|
name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
|
82
|
-
|
67
|
+
_read_attribute(name) { |n| missing_attribute(n, caller) }
|
83
68
|
end
|
84
69
|
STR
|
85
70
|
|
@@ -89,51 +74,29 @@ module ActiveRecord
|
|
89
74
|
end
|
90
75
|
end
|
91
76
|
end
|
92
|
-
|
93
|
-
private
|
94
|
-
|
95
|
-
def cacheable_column?(column)
|
96
|
-
if attribute_types_cached_by_default == ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
|
97
|
-
! serialized_attributes.include? column.name
|
98
|
-
else
|
99
|
-
attribute_types_cached_by_default.include?(column.type)
|
100
|
-
end
|
101
|
-
end
|
102
77
|
end
|
103
78
|
|
79
|
+
ID = 'id'.freeze
|
80
|
+
|
104
81
|
# Returns the value of the attribute identified by <tt>attr_name</tt> after
|
105
82
|
# it has been typecast (for example, "2004-12-12" in a date column is cast
|
106
83
|
# to a date object, like Date.new(2004, 12, 12)).
|
107
|
-
def read_attribute(attr_name)
|
108
|
-
# If it's cached, just return it
|
109
|
-
# We use #[] first as a perf optimization for non-nil values. See https://gist.github.com/jonleighton/3552829.
|
84
|
+
def read_attribute(attr_name, &block)
|
110
85
|
name = attr_name.to_s
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
return @attributes.fetch(name) {
|
116
|
-
if name == 'id' && self.class.primary_key != name
|
117
|
-
read_attribute(self.class.primary_key)
|
118
|
-
end
|
119
|
-
} unless column
|
120
|
-
|
121
|
-
value = @attributes.fetch(name) {
|
122
|
-
return block_given? ? yield(name) : nil
|
123
|
-
}
|
86
|
+
name = self.class.primary_key if name == ID
|
87
|
+
_read_attribute(name, &block)
|
88
|
+
end
|
124
89
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
end
|
130
|
-
}
|
90
|
+
# This method exists to avoid the expensive primary_key check internally, without
|
91
|
+
# breaking compatibility with the read_attribute API
|
92
|
+
def _read_attribute(attr_name) # :nodoc:
|
93
|
+
@attributes.fetch_value(attr_name.to_s) { |n| yield n if block_given? }
|
131
94
|
end
|
132
95
|
|
133
96
|
private
|
134
97
|
|
135
98
|
def attribute(attribute_name)
|
136
|
-
|
99
|
+
_read_attribute(attribute_name)
|
137
100
|
end
|
138
101
|
end
|
139
102
|
end
|