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
@@ -1,36 +1,23 @@
|
|
1
|
+
require 'active_support/core_ext/string/filters'
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
module AttributeMethods
|
3
5
|
module Serialization
|
4
6
|
extend ActiveSupport::Concern
|
5
7
|
|
6
|
-
included do
|
7
|
-
# Returns a hash of all the attributes that have been specified for
|
8
|
-
# serialization as keys and their class restriction as values.
|
9
|
-
class_attribute :serialized_attributes, instance_accessor: false
|
10
|
-
self.serialized_attributes = {}
|
11
|
-
end
|
12
|
-
|
13
8
|
module ClassMethods
|
14
|
-
##
|
15
|
-
# :method: serialized_attributes
|
16
|
-
#
|
17
|
-
# Returns a hash of all the attributes that have been specified for
|
18
|
-
# serialization as keys and their class restriction as values.
|
19
|
-
|
20
9
|
# If you have an attribute that needs to be saved to the database as an
|
21
10
|
# object, and retrieved as the same object, then specify the name of that
|
22
11
|
# attribute using this method and it will be handled automatically. The
|
23
12
|
# serialization is done through YAML. If +class_name+ is specified, the
|
24
|
-
# serialized object must be of that class on retrieval
|
25
|
-
# <tt>SerializationTypeMismatch</tt> will be raised.
|
26
|
-
#
|
27
|
-
# A notable side effect of serialized attributes is that the model will
|
28
|
-
# be updated on every save, even if it is not dirty.
|
13
|
+
# serialized object must be of that class on assignment and retrieval.
|
14
|
+
# Otherwise <tt>SerializationTypeMismatch</tt> will be raised.
|
29
15
|
#
|
30
16
|
# ==== Parameters
|
31
17
|
#
|
32
18
|
# * +attr_name+ - The field name that should be serialized.
|
33
|
-
# * +
|
19
|
+
# * +class_name_or_coder+ - Optional, a coder object, which responds to `.load` / `.dump`
|
20
|
+
# or a class name that the object type should be equal to.
|
34
21
|
#
|
35
22
|
# ==== Example
|
36
23
|
#
|
@@ -38,141 +25,44 @@ module ActiveRecord
|
|
38
25
|
# class User < ActiveRecord::Base
|
39
26
|
# serialize :preferences
|
40
27
|
# end
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
28
|
+
#
|
29
|
+
# # Serialize preferences using JSON as coder.
|
30
|
+
# class User < ActiveRecord::Base
|
31
|
+
# serialize :preferences, JSON
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# # Serialize preferences as Hash using YAML coder.
|
35
|
+
# class User < ActiveRecord::Base
|
36
|
+
# serialize :preferences, Hash
|
37
|
+
# end
|
38
|
+
def serialize(attr_name, class_name_or_coder = Object)
|
39
|
+
# When ::JSON is used, force it to go through the Active Support JSON encoder
|
40
|
+
# to ensure special objects (e.g. Active Record models) are dumped correctly
|
41
|
+
# using the #as_json hook.
|
42
|
+
coder = if class_name_or_coder == ::JSON
|
43
|
+
Coders::JSON
|
44
|
+
elsif [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
|
45
|
+
class_name_or_coder
|
46
46
|
else
|
47
|
-
Coders::YAMLColumn.new(
|
47
|
+
Coders::YAMLColumn.new(class_name_or_coder)
|
48
48
|
end
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
class Type # :nodoc:
|
57
|
-
def initialize(column)
|
58
|
-
@column = column
|
59
|
-
end
|
60
|
-
|
61
|
-
def type_cast(value)
|
62
|
-
if value.state == :serialized
|
63
|
-
value.unserialized_value @column.type_cast value.value
|
64
|
-
else
|
65
|
-
value.unserialized_value
|
50
|
+
decorate_attribute_type(attr_name, :serialize) do |type|
|
51
|
+
Type::Serialized.new(type, coder)
|
66
52
|
end
|
67
53
|
end
|
68
54
|
|
69
|
-
def
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
ActiveRecord::Store::IndifferentHashAccessor
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
class Attribute < Struct.new(:coder, :value, :state) # :nodoc:
|
79
|
-
def unserialized_value(v = value)
|
80
|
-
state == :serialized ? unserialize(v) : value
|
81
|
-
end
|
82
|
-
|
83
|
-
def serialized_value
|
84
|
-
state == :unserialized ? serialize : value
|
85
|
-
end
|
55
|
+
def serialized_attributes
|
56
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
57
|
+
`serialized_attributes` is deprecated without replacement, and will
|
58
|
+
be removed in Rails 5.0.
|
59
|
+
MSG
|
86
60
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
def serialize
|
93
|
-
self.state = :serialized
|
94
|
-
self.value = coder.dump(value)
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
# This is only added to the model when serialize is called, which
|
99
|
-
# ensures we do not make things slower when serialization is not used.
|
100
|
-
module Behavior # :nodoc:
|
101
|
-
extend ActiveSupport::Concern
|
102
|
-
|
103
|
-
module ClassMethods # :nodoc:
|
104
|
-
def initialize_attributes(attributes, options = {})
|
105
|
-
serialized = (options.delete(:serialized) { true }) ? :serialized : :unserialized
|
106
|
-
super(attributes, options)
|
107
|
-
|
108
|
-
serialized_attributes.each do |key, coder|
|
109
|
-
if attributes.key?(key)
|
110
|
-
attributes[key] = Attribute.new(coder, attributes[key], serialized)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
attributes
|
115
|
-
end
|
116
|
-
end
|
117
|
-
|
118
|
-
def should_record_timestamps?
|
119
|
-
super || (self.record_timestamps && (attributes.keys & self.class.serialized_attributes.keys).present?)
|
120
|
-
end
|
121
|
-
|
122
|
-
def keys_for_partial_write
|
123
|
-
super | (attributes.keys & self.class.serialized_attributes.keys)
|
124
|
-
end
|
125
|
-
|
126
|
-
def type_cast_attribute_for_write(column, value)
|
127
|
-
if column && coder = self.class.serialized_attributes[column.name]
|
128
|
-
Attribute.new(coder, value, :unserialized)
|
129
|
-
else
|
130
|
-
super
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def _field_changed?(attr, old, value)
|
135
|
-
if self.class.serialized_attributes.include?(attr)
|
136
|
-
old != value
|
137
|
-
else
|
138
|
-
super
|
139
|
-
end
|
140
|
-
end
|
141
|
-
|
142
|
-
def read_attribute_before_type_cast(attr_name)
|
143
|
-
if self.class.serialized_attributes.include?(attr_name)
|
144
|
-
super.unserialized_value
|
145
|
-
else
|
146
|
-
super
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def attributes_before_type_cast
|
151
|
-
super.dup.tap do |attributes|
|
152
|
-
self.class.serialized_attributes.each_key do |key|
|
153
|
-
if attributes.key?(key)
|
154
|
-
attributes[key] = attributes[key].unserialized_value
|
155
|
-
end
|
156
|
-
end
|
157
|
-
end
|
158
|
-
end
|
159
|
-
|
160
|
-
def typecasted_attribute_value(name)
|
161
|
-
if self.class.serialized_attributes.include?(name)
|
162
|
-
@attributes[name].serialized_value
|
163
|
-
else
|
164
|
-
super
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
def attributes_for_coder
|
169
|
-
attribute_names.each_with_object({}) do |name, attrs|
|
170
|
-
attrs[name] = if self.class.serialized_attributes.include?(name)
|
171
|
-
@attributes[name].serialized_value
|
172
|
-
else
|
173
|
-
read_attribute(name)
|
174
|
-
end
|
175
|
-
end
|
61
|
+
@serialized_attributes ||= Hash[
|
62
|
+
columns.select { |t| t.cast_type.is_a?(Type::Serialized) }.map { |c|
|
63
|
+
[c.name, c.cast_type.coder]
|
64
|
+
}
|
65
|
+
]
|
176
66
|
end
|
177
67
|
end
|
178
68
|
end
|
@@ -1,18 +1,29 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module AttributeMethods
|
3
3
|
module TimeZoneConversion
|
4
|
-
class Type # :nodoc:
|
5
|
-
|
6
|
-
|
4
|
+
class TimeZoneConverter < DelegateClass(Type::Value) # :nodoc:
|
5
|
+
include Type::Decorator
|
6
|
+
|
7
|
+
def type_cast_from_database(value)
|
8
|
+
convert_time_to_time_zone(super)
|
7
9
|
end
|
8
10
|
|
9
|
-
def
|
10
|
-
value
|
11
|
-
|
11
|
+
def type_cast_from_user(value)
|
12
|
+
if value.is_a?(Array)
|
13
|
+
value.map { |v| type_cast_from_user(v) }
|
14
|
+
elsif value.respond_to?(:in_time_zone)
|
15
|
+
value.in_time_zone || super
|
16
|
+
end
|
12
17
|
end
|
13
18
|
|
14
|
-
def
|
15
|
-
|
19
|
+
def convert_time_to_time_zone(value)
|
20
|
+
if value.is_a?(Array)
|
21
|
+
value.map { |v| convert_time_to_time_zone(v) }
|
22
|
+
elsif value.acts_like?(:time)
|
23
|
+
value.in_time_zone
|
24
|
+
else
|
25
|
+
value
|
26
|
+
end
|
16
27
|
end
|
17
28
|
end
|
18
29
|
|
@@ -27,31 +38,26 @@ module ActiveRecord
|
|
27
38
|
end
|
28
39
|
|
29
40
|
module ClassMethods
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
43
|
-
EOV
|
44
|
-
generated_attribute_methods.module_eval(method_body, __FILE__, line)
|
45
|
-
else
|
46
|
-
super
|
41
|
+
private
|
42
|
+
|
43
|
+
def inherited(subclass)
|
44
|
+
# We need to apply this decorator here, rather than on module inclusion. The closure
|
45
|
+
# created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
|
46
|
+
# sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
|
47
|
+
# `skip_time_zone_conversion_for_attributes` would not be picked up.
|
48
|
+
subclass.class_eval do
|
49
|
+
matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
|
50
|
+
decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type|
|
51
|
+
TimeZoneConverter.new(type)
|
52
|
+
end
|
47
53
|
end
|
54
|
+
super
|
48
55
|
end
|
49
56
|
|
50
|
-
|
51
|
-
def create_time_zone_conversion_attribute?(name, column)
|
57
|
+
def create_time_zone_conversion_attribute?(name, cast_type)
|
52
58
|
time_zone_aware_attributes &&
|
53
59
|
!self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) &&
|
54
|
-
(:datetime ==
|
60
|
+
(:datetime == cast_type.type)
|
55
61
|
end
|
56
62
|
end
|
57
63
|
end
|
@@ -26,8 +26,6 @@ module ActiveRecord
|
|
26
26
|
protected
|
27
27
|
|
28
28
|
if Module.methods_transplantable?
|
29
|
-
# See define_method_attribute in read.rb for an explanation of
|
30
|
-
# this code.
|
31
29
|
def define_method_attribute=(name)
|
32
30
|
method = WriterMethodCache[name]
|
33
31
|
generated_attribute_methods.module_eval {
|
@@ -55,24 +53,12 @@ module ActiveRecord
|
|
55
53
|
# specified +value+. Empty strings for fixnum and float columns are
|
56
54
|
# turned into +nil+.
|
57
55
|
def write_attribute(attr_name, value)
|
58
|
-
attr_name
|
59
|
-
|
60
|
-
@attributes_cache.delete(attr_name)
|
61
|
-
column = column_for_attribute(attr_name)
|
62
|
-
|
63
|
-
# If we're dealing with a binary column, write the data to the cache
|
64
|
-
# so we don't attempt to typecast multiple times.
|
65
|
-
if column && column.binary?
|
66
|
-
@attributes_cache[attr_name] = value
|
67
|
-
end
|
56
|
+
write_attribute_with_type_cast(attr_name, value, true)
|
57
|
+
end
|
68
58
|
|
69
|
-
|
70
|
-
|
71
|
-
else
|
72
|
-
raise ActiveModel::MissingAttributeError, "can't write unknown attribute `#{attr_name}'"
|
73
|
-
end
|
59
|
+
def raw_write_attribute(attr_name, value)
|
60
|
+
write_attribute_with_type_cast(attr_name, value, false)
|
74
61
|
end
|
75
|
-
alias_method :raw_write_attribute, :write_attribute
|
76
62
|
|
77
63
|
private
|
78
64
|
# Handle *= for method_missing.
|
@@ -80,10 +66,17 @@ module ActiveRecord
|
|
80
66
|
write_attribute(attribute_name, value)
|
81
67
|
end
|
82
68
|
|
83
|
-
def
|
84
|
-
|
69
|
+
def write_attribute_with_type_cast(attr_name, value, should_type_cast)
|
70
|
+
attr_name = attr_name.to_s
|
71
|
+
attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
|
72
|
+
|
73
|
+
if should_type_cast
|
74
|
+
@attributes.write_from_user(attr_name, value)
|
75
|
+
else
|
76
|
+
@attributes.write_cast_value(attr_name, value)
|
77
|
+
end
|
85
78
|
|
86
|
-
|
79
|
+
value
|
87
80
|
end
|
88
81
|
end
|
89
82
|
end
|
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'active_support/core_ext/enumerable'
|
2
|
+
require 'active_support/core_ext/string/filters'
|
2
3
|
require 'mutex_m'
|
3
4
|
require 'thread_safe'
|
4
5
|
|
@@ -18,6 +19,8 @@ module ActiveRecord
|
|
18
19
|
include TimeZoneConversion
|
19
20
|
include Dirty
|
20
21
|
include Serialization
|
22
|
+
|
23
|
+
delegate :column_for_attribute, to: :class
|
21
24
|
end
|
22
25
|
|
23
26
|
AttrNames = Module.new {
|
@@ -29,7 +32,7 @@ module ActiveRecord
|
|
29
32
|
end
|
30
33
|
}
|
31
34
|
|
32
|
-
BLACKLISTED_CLASS_METHODS = %w(private public protected)
|
35
|
+
BLACKLISTED_CLASS_METHODS = %w(private public protected allocate new name parent superclass)
|
33
36
|
|
34
37
|
class AttributeMethodCache
|
35
38
|
def initialize
|
@@ -48,9 +51,15 @@ module ActiveRecord
|
|
48
51
|
end
|
49
52
|
|
50
53
|
private
|
51
|
-
|
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
|
52
59
|
end
|
53
60
|
|
61
|
+
class GeneratedAttributeMethods < Module; end # :nodoc:
|
62
|
+
|
54
63
|
module ClassMethods
|
55
64
|
def inherited(child_class) #:nodoc:
|
56
65
|
child_class.initialize_generated_modules
|
@@ -58,15 +67,18 @@ module ActiveRecord
|
|
58
67
|
end
|
59
68
|
|
60
69
|
def initialize_generated_modules # :nodoc:
|
61
|
-
@generated_attribute_methods =
|
70
|
+
@generated_attribute_methods = GeneratedAttributeMethods.new { extend Mutex_m }
|
62
71
|
@attribute_methods_generated = false
|
63
72
|
include @generated_attribute_methods
|
73
|
+
|
74
|
+
super
|
64
75
|
end
|
65
76
|
|
66
77
|
# Generates all the attribute related methods for columns in the database
|
67
78
|
# accessors, mutators and query methods.
|
68
79
|
def define_attribute_methods # :nodoc:
|
69
|
-
|
80
|
+
return false if @attribute_methods_generated
|
81
|
+
# Use a mutex; we don't want two threads simultaneously trying to define
|
70
82
|
# attribute methods.
|
71
83
|
generated_attribute_methods.synchronize do
|
72
84
|
return false if @attribute_methods_generated
|
@@ -79,7 +91,7 @@ module ActiveRecord
|
|
79
91
|
|
80
92
|
def undefine_attribute_methods # :nodoc:
|
81
93
|
generated_attribute_methods.synchronize do
|
82
|
-
super if @attribute_methods_generated
|
94
|
+
super if defined?(@attribute_methods_generated) && @attribute_methods_generated
|
83
95
|
@attribute_methods_generated = false
|
84
96
|
end
|
85
97
|
end
|
@@ -100,16 +112,17 @@ module ActiveRecord
|
|
100
112
|
# # => false
|
101
113
|
def instance_method_already_implemented?(method_name)
|
102
114
|
if dangerous_attribute_method?(method_name)
|
103
|
-
raise DangerousAttributeError, "#{method_name} is defined by Active Record"
|
115
|
+
raise DangerousAttributeError, "#{method_name} is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name."
|
104
116
|
end
|
105
117
|
|
106
118
|
if superclass == Base
|
107
119
|
super
|
108
120
|
else
|
109
|
-
# If
|
110
|
-
|
111
|
-
|
112
|
-
|
121
|
+
# If ThisClass < ... < SomeSuperClass < ... < Base and SomeSuperClass
|
122
|
+
# defines its own attribute method, then we don't want to overwrite that.
|
123
|
+
defined = method_defined_within?(method_name, superclass, Base) &&
|
124
|
+
! superclass.instance_method(method_name).owner.is_a?(GeneratedAttributeMethods)
|
125
|
+
defined || super
|
113
126
|
end
|
114
127
|
end
|
115
128
|
|
@@ -149,16 +162,6 @@ module ActiveRecord
|
|
149
162
|
end
|
150
163
|
end
|
151
164
|
|
152
|
-
def find_generated_attribute_method(method_name) # :nodoc:
|
153
|
-
klass = self
|
154
|
-
until klass == Base
|
155
|
-
gen_methods = klass.generated_attribute_methods
|
156
|
-
return gen_methods.instance_method(method_name) if method_defined_within?(method_name, gen_methods, Object)
|
157
|
-
klass = klass.superclass
|
158
|
-
end
|
159
|
-
nil
|
160
|
-
end
|
161
|
-
|
162
165
|
# Returns +true+ if +attribute+ is an attribute method and table exists,
|
163
166
|
# +false+ otherwise.
|
164
167
|
#
|
@@ -187,23 +190,29 @@ module ActiveRecord
|
|
187
190
|
[]
|
188
191
|
end
|
189
192
|
end
|
190
|
-
end
|
191
193
|
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
194
|
+
# Returns the column object for the named attribute.
|
195
|
+
# Returns nil if the named attribute does not exist.
|
196
|
+
#
|
197
|
+
# class Person < ActiveRecord::Base
|
198
|
+
# end
|
199
|
+
#
|
200
|
+
# person = Person.new
|
201
|
+
# person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
|
202
|
+
# # => #<ActiveRecord::ConnectionAdapters::Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
|
203
|
+
#
|
204
|
+
# person.column_for_attribute(:nothing)
|
205
|
+
# # => nil
|
206
|
+
def column_for_attribute(name)
|
207
|
+
column = columns_hash[name.to_s]
|
208
|
+
if column.nil?
|
209
|
+
ActiveSupport::Deprecation.warn(<<-MSG.squish)
|
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
|
204
214
|
end
|
205
|
-
|
206
|
-
super
|
215
|
+
column
|
207
216
|
end
|
208
217
|
end
|
209
218
|
|
@@ -224,18 +233,14 @@ module ActiveRecord
|
|
224
233
|
# person.respond_to('age?') # => true
|
225
234
|
# person.respond_to(:nothing) # => false
|
226
235
|
def respond_to?(name, include_private = false)
|
236
|
+
return false unless super
|
227
237
|
name = name.to_s
|
228
|
-
self.class.define_attribute_methods
|
229
|
-
result = super
|
230
|
-
|
231
|
-
# If the result is false the answer is false.
|
232
|
-
return false unless result
|
233
238
|
|
234
239
|
# If the result is true then check for the select case.
|
235
240
|
# For queries selecting a subset of columns, return false for unselected columns.
|
236
241
|
# We check defined?(@attributes) not to issue warnings if called on objects that
|
237
242
|
# have been allocated but not yet initialized.
|
238
|
-
if defined?(@attributes) &&
|
243
|
+
if defined?(@attributes) && self.class.column_names.include?(name)
|
239
244
|
return has_attribute?(name)
|
240
245
|
end
|
241
246
|
|
@@ -252,7 +257,7 @@ module ActiveRecord
|
|
252
257
|
# person.has_attribute?('age') # => true
|
253
258
|
# person.has_attribute?(:nothing) # => false
|
254
259
|
def has_attribute?(attr_name)
|
255
|
-
@attributes.
|
260
|
+
@attributes.key?(attr_name.to_s)
|
256
261
|
end
|
257
262
|
|
258
263
|
# Returns an array of names for the attributes available on this object.
|
@@ -276,20 +281,13 @@ module ActiveRecord
|
|
276
281
|
# person.attributes
|
277
282
|
# # => {"id"=>3, "created_at"=>Sun, 21 Oct 2012 04:53:04, "updated_at"=>Sun, 21 Oct 2012 04:53:04, "name"=>"Francesco", "age"=>22}
|
278
283
|
def attributes
|
279
|
-
|
280
|
-
attrs[name] = read_attribute(name)
|
281
|
-
}
|
282
|
-
end
|
283
|
-
|
284
|
-
# Placeholder so it can be overriden when needed by serialization
|
285
|
-
def attributes_for_coder # :nodoc:
|
286
|
-
attributes
|
284
|
+
@attributes.to_hash
|
287
285
|
end
|
288
286
|
|
289
287
|
# Returns an <tt>#inspect</tt>-like string for the value of the
|
290
|
-
# attribute +attr_name+. String attributes are truncated
|
288
|
+
# attribute +attr_name+. String attributes are truncated up to 50
|
291
289
|
# characters, Date and Time attributes are returned in the
|
292
|
-
# <tt>:db</tt> format, Array attributes are truncated
|
290
|
+
# <tt>:db</tt> format, Array attributes are truncated up to 10 values.
|
293
291
|
# Other attributes return the value of <tt>#inspect</tt> without
|
294
292
|
# modification.
|
295
293
|
#
|
@@ -326,39 +324,24 @@ module ActiveRecord
|
|
326
324
|
# class Task < ActiveRecord::Base
|
327
325
|
# end
|
328
326
|
#
|
329
|
-
#
|
330
|
-
#
|
331
|
-
#
|
332
|
-
#
|
333
|
-
#
|
334
|
-
#
|
335
|
-
#
|
327
|
+
# task = Task.new(title: '', is_done: false)
|
328
|
+
# task.attribute_present?(:title) # => false
|
329
|
+
# task.attribute_present?(:is_done) # => true
|
330
|
+
# task.title = 'Buy milk'
|
331
|
+
# task.is_done = true
|
332
|
+
# task.attribute_present?(:title) # => true
|
333
|
+
# task.attribute_present?(:is_done) # => true
|
336
334
|
def attribute_present?(attribute)
|
337
|
-
value =
|
335
|
+
value = _read_attribute(attribute)
|
338
336
|
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
|
339
337
|
end
|
340
338
|
|
341
|
-
# Returns the column object for the named attribute. Returns +nil+ if the
|
342
|
-
# named attribute not exists.
|
343
|
-
#
|
344
|
-
# class Person < ActiveRecord::Base
|
345
|
-
# end
|
346
|
-
#
|
347
|
-
# person = Person.new
|
348
|
-
# person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
|
349
|
-
# # => #<ActiveRecord::ConnectionAdapters::SQLite3Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
|
350
|
-
#
|
351
|
-
# person.column_for_attribute(:nothing)
|
352
|
-
# # => nil
|
353
|
-
def column_for_attribute(name)
|
354
|
-
# FIXME: should this return a null object for columns that don't exist?
|
355
|
-
self.class.columns_hash[name.to_s]
|
356
|
-
end
|
357
|
-
|
358
339
|
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
|
359
340
|
# "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
|
360
341
|
# <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
|
361
342
|
#
|
343
|
+
# Note: +:id+ is always present.
|
344
|
+
#
|
362
345
|
# Alias for the <tt>read_attribute</tt> method.
|
363
346
|
#
|
364
347
|
# class Person < ActiveRecord::Base
|
@@ -392,13 +375,6 @@ module ActiveRecord
|
|
392
375
|
|
393
376
|
protected
|
394
377
|
|
395
|
-
def clone_attributes(reader_method = :read_attribute, attributes = {}) # :nodoc:
|
396
|
-
attribute_names.each do |name|
|
397
|
-
attributes[name] = clone_attribute_value(reader_method, name)
|
398
|
-
end
|
399
|
-
attributes
|
400
|
-
end
|
401
|
-
|
402
378
|
def clone_attribute_value(reader_method, attribute_name) # :nodoc:
|
403
379
|
value = send(reader_method, attribute_name)
|
404
380
|
value.duplicable? ? value.clone : value
|
@@ -416,7 +392,7 @@ module ActiveRecord
|
|
416
392
|
|
417
393
|
def attribute_method?(attr_name) # :nodoc:
|
418
394
|
# We check defined? because Syck calls respond_to? before actually calling initialize.
|
419
|
-
defined?(@attributes) && @attributes.
|
395
|
+
defined?(@attributes) && @attributes.key?(attr_name)
|
420
396
|
end
|
421
397
|
|
422
398
|
private
|
@@ -435,16 +411,16 @@ module ActiveRecord
|
|
435
411
|
|
436
412
|
# Filters the primary keys and readonly attributes from the attribute names.
|
437
413
|
def attributes_for_update(attribute_names)
|
438
|
-
attribute_names.
|
439
|
-
|
414
|
+
attribute_names.reject do |name|
|
415
|
+
readonly_attribute?(name)
|
440
416
|
end
|
441
417
|
end
|
442
418
|
|
443
419
|
# Filters out the primary keys, from the attribute names, when the primary
|
444
420
|
# key is to be generated (e.g. the id attribute has no value).
|
445
421
|
def attributes_for_create(attribute_names)
|
446
|
-
attribute_names.
|
447
|
-
|
422
|
+
attribute_names.reject do |name|
|
423
|
+
pk_attribute?(name) && id.nil?
|
448
424
|
end
|
449
425
|
end
|
450
426
|
|
@@ -453,14 +429,11 @@ module ActiveRecord
|
|
453
429
|
end
|
454
430
|
|
455
431
|
def pk_attribute?(name)
|
456
|
-
|
432
|
+
name == self.class.primary_key
|
457
433
|
end
|
458
434
|
|
459
435
|
def typecasted_attribute_value(name)
|
460
|
-
|
461
|
-
# If the values stored in @attributes were already typecasted, this code
|
462
|
-
# could be simplified
|
463
|
-
read_attribute(name)
|
436
|
+
_read_attribute(name)
|
464
437
|
end
|
465
438
|
end
|
466
439
|
end
|