activerecord 4.1.16 → 4.2.11.3
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 +1162 -1801
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +15 -8
- data/lib/active_record/association_relation.rb +13 -0
- data/lib/active_record/associations/alias_tracker.rb +3 -12
- data/lib/active_record/associations/association.rb +16 -4
- data/lib/active_record/associations/association_scope.rb +83 -38
- data/lib/active_record/associations/belongs_to_association.rb +28 -10
- 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/collection_association.rb +5 -1
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
- 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 +63 -27
- data/lib/active_record/associations/collection_proxy.rb +29 -35
- data/lib/active_record/associations/foreign_association.rb +11 -0
- data/lib/active_record/associations/has_many_association.rb +83 -22
- data/lib/active_record/associations/has_many_through_association.rb +49 -26
- data/lib/active_record/associations/has_one_association.rb +1 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
- data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
- data/lib/active_record/associations/join_dependency.rb +26 -13
- data/lib/active_record/associations/preloader/association.rb +14 -11
- data/lib/active_record/associations/preloader/through_association.rb +4 -3
- data/lib/active_record/associations/preloader.rb +36 -26
- data/lib/active_record/associations/singular_association.rb +17 -2
- data/lib/active_record/associations/through_association.rb +5 -12
- data/lib/active_record/associations.rb +158 -49
- data/lib/active_record/attribute.rb +163 -0
- data/lib/active_record/attribute_assignment.rb +19 -11
- data/lib/active_record/attribute_decorators.rb +66 -0
- data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
- data/lib/active_record/attribute_methods/dirty.rb +107 -43
- data/lib/active_record/attribute_methods/primary_key.rb +7 -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 +16 -150
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
- data/lib/active_record/attribute_methods/write.rb +9 -24
- data/lib/active_record/attribute_methods.rb +56 -94
- data/lib/active_record/attribute_set/builder.rb +106 -0
- data/lib/active_record/attribute_set.rb +81 -0
- data/lib/active_record/attributes.rb +147 -0
- data/lib/active_record/autosave_association.rb +19 -12
- data/lib/active_record/base.rb +13 -24
- data/lib/active_record/callbacks.rb +6 -6
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
- data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
- data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
- data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
- data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
- data/lib/active_record/connection_adapters/column.rb +29 -240
- data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
- data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
- 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 +40 -25
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -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 +15 -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 +36 -0
- data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
- data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -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 +19 -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 +109 -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 -388
- data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
- 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 +131 -43
- data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +163 -39
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -11
- data/lib/active_record/errors.rb +53 -30
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/explain_subscriber.rb +1 -1
- data/lib/active_record/fixtures.rb +55 -69
- data/lib/active_record/gem_version.rb +4 -4
- data/lib/active_record/inheritance.rb +35 -10
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/legacy_yaml_adapter.rb +30 -0
- data/lib/active_record/locking/optimistic.rb +46 -26
- 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 +71 -46
- data/lib/active_record/model_schema.rb +52 -58
- data/lib/active_record/nested_attributes.rb +5 -5
- data/lib/active_record/no_touching.rb +1 -1
- data/lib/active_record/persistence.rb +46 -26
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +10 -7
- data/lib/active_record/railtie.rb +18 -11
- data/lib/active_record/railties/databases.rake +50 -51
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +273 -114
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +41 -37
- data/lib/active_record/relation/finder_methods.rb +70 -47
- data/lib/active_record/relation/merger.rb +39 -29
- data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
- data/lib/active_record/relation/predicate_builder.rb +16 -8
- data/lib/active_record/relation/query_methods.rb +114 -65
- data/lib/active_record/relation/spawn_methods.rb +3 -0
- data/lib/active_record/relation.rb +57 -25
- 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 +59 -28
- data/lib/active_record/schema_migration.rb +5 -4
- data/lib/active_record/scoping/default.rb +6 -4
- 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 +5 -5
- data/lib/active_record/tasks/database_tasks.rb +61 -6
- data/lib/active_record/tasks/mysql_database_tasks.rb +20 -11
- data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +53 -27
- 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 +31 -0
- data/lib/active_record/type/date.rb +50 -0
- data/lib/active_record/type/date_time.rb +54 -0
- data/lib/active_record/type/decimal.rb +64 -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 +23 -0
- data/lib/active_record/type/integer.rb +59 -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 +62 -0
- data/lib/active_record/type/string.rb +40 -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 +110 -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 +5 -3
- data/lib/active_record/validations/uniqueness.rb +25 -29
- data/lib/active_record/validations.rb +25 -19
- data/lib/active_record.rb +4 -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 +1 -1
- data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
- metadata +66 -11
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -1,31 +1,17 @@
|
|
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
|
#
|
@@ -50,8 +36,6 @@ module ActiveRecord
|
|
50
36
|
# serialize :preferences, Hash
|
51
37
|
# end
|
52
38
|
def serialize(attr_name, class_name_or_coder = Object)
|
53
|
-
include Behavior
|
54
|
-
|
55
39
|
# When ::JSON is used, force it to go through the Active Support JSON encoder
|
56
40
|
# to ensure special objects (e.g. Active Record models) are dumped correctly
|
57
41
|
# using the #as_json hook.
|
@@ -63,140 +47,22 @@ module ActiveRecord
|
|
63
47
|
Coders::YAMLColumn.new(class_name_or_coder)
|
64
48
|
end
|
65
49
|
|
66
|
-
|
67
|
-
|
68
|
-
self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
class Type # :nodoc:
|
73
|
-
def initialize(column)
|
74
|
-
@column = column
|
75
|
-
end
|
76
|
-
|
77
|
-
def type_cast(value)
|
78
|
-
if value.state == :serialized
|
79
|
-
value.unserialized_value @column.type_cast value.value
|
80
|
-
else
|
81
|
-
value.unserialized_value
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
def type
|
86
|
-
@column.type
|
87
|
-
end
|
88
|
-
|
89
|
-
def accessor
|
90
|
-
ActiveRecord::Store::IndifferentHashAccessor
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
class Attribute < Struct.new(:coder, :value, :state) # :nodoc:
|
95
|
-
def unserialized_value(v = value)
|
96
|
-
state == :serialized ? unserialize(v) : value
|
97
|
-
end
|
98
|
-
|
99
|
-
def serialized_value
|
100
|
-
state == :unserialized ? serialize : value
|
101
|
-
end
|
102
|
-
|
103
|
-
def unserialize(v)
|
104
|
-
self.state = :unserialized
|
105
|
-
self.value = coder.load(v)
|
106
|
-
end
|
107
|
-
|
108
|
-
def serialize
|
109
|
-
self.state = :serialized
|
110
|
-
self.value = coder.dump(value)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
# This is only added to the model when serialize is called, which
|
115
|
-
# ensures we do not make things slower when serialization is not used.
|
116
|
-
module Behavior # :nodoc:
|
117
|
-
extend ActiveSupport::Concern
|
118
|
-
|
119
|
-
module ClassMethods # :nodoc:
|
120
|
-
def initialize_attributes(attributes, options = {})
|
121
|
-
serialized = (options.delete(:serialized) { true }) ? :serialized : :unserialized
|
122
|
-
super(attributes, options)
|
123
|
-
|
124
|
-
serialized_attributes.each do |key, coder|
|
125
|
-
if attributes.key?(key)
|
126
|
-
attributes[key] = Attribute.new(coder, attributes[key], serialized)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
attributes
|
131
|
-
end
|
132
|
-
end
|
133
|
-
|
134
|
-
def should_record_timestamps?
|
135
|
-
super || (self.record_timestamps && (attributes.keys & self.class.serialized_attributes.keys).present?)
|
136
|
-
end
|
137
|
-
|
138
|
-
def keys_for_partial_write
|
139
|
-
super | (attributes.keys & self.class.serialized_attributes.keys)
|
140
|
-
end
|
141
|
-
|
142
|
-
def type_cast_attribute_for_write(column, value)
|
143
|
-
if column && coder = self.class.serialized_attributes[column.name]
|
144
|
-
Attribute.new(coder, value, :unserialized)
|
145
|
-
else
|
146
|
-
super
|
50
|
+
decorate_attribute_type(attr_name, :serialize) do |type|
|
51
|
+
Type::Serialized.new(type, coder)
|
147
52
|
end
|
148
53
|
end
|
149
54
|
|
150
|
-
def
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
end
|
156
|
-
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
|
157
60
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
end
|
164
|
-
end
|
165
|
-
|
166
|
-
def read_attribute_before_type_cast(attr_name)
|
167
|
-
if self.class.serialized_attributes.include?(attr_name)
|
168
|
-
super.unserialized_value
|
169
|
-
else
|
170
|
-
super
|
171
|
-
end
|
172
|
-
end
|
173
|
-
|
174
|
-
def attributes_before_type_cast
|
175
|
-
super.dup.tap do |attributes|
|
176
|
-
self.class.serialized_attributes.each_key do |key|
|
177
|
-
if attributes.key?(key)
|
178
|
-
attributes[key] = attributes[key].unserialized_value
|
179
|
-
end
|
180
|
-
end
|
181
|
-
end
|
182
|
-
end
|
183
|
-
|
184
|
-
def typecasted_attribute_value(name)
|
185
|
-
if self.class.serialized_attributes.include?(name)
|
186
|
-
@attributes[name].serialized_value
|
187
|
-
else
|
188
|
-
super
|
189
|
-
end
|
190
|
-
end
|
191
|
-
|
192
|
-
def attributes_for_coder
|
193
|
-
attribute_names.each_with_object({}) do |name, attrs|
|
194
|
-
attrs[name] = if self.class.serialized_attributes.include?(name)
|
195
|
-
@attributes[name].serialized_value
|
196
|
-
else
|
197
|
-
read_attribute(name)
|
198
|
-
end
|
199
|
-
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
|
+
]
|
200
66
|
end
|
201
67
|
end
|
202
68
|
end
|
@@ -1,18 +1,33 @@
|
|
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
|
+
begin
|
16
|
+
value.in_time_zone || super
|
17
|
+
rescue ArgumentError
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
end
|
12
21
|
end
|
13
22
|
|
14
|
-
def
|
15
|
-
|
23
|
+
def convert_time_to_time_zone(value)
|
24
|
+
if value.is_a?(Array)
|
25
|
+
value.map { |v| convert_time_to_time_zone(v) }
|
26
|
+
elsif value.acts_like?(:time)
|
27
|
+
value.in_time_zone
|
28
|
+
else
|
29
|
+
value
|
30
|
+
end
|
16
31
|
end
|
17
32
|
end
|
18
33
|
|
@@ -27,43 +42,26 @@ module ActiveRecord
|
|
27
42
|
end
|
28
43
|
|
29
44
|
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
|
45
|
+
private
|
46
|
+
|
47
|
+
def inherited(subclass)
|
48
|
+
# We need to apply this decorator here, rather than on module inclusion. The closure
|
49
|
+
# created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
|
50
|
+
# sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
|
51
|
+
# `skip_time_zone_conversion_for_attributes` would not be picked up.
|
52
|
+
subclass.class_eval do
|
53
|
+
matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
|
54
|
+
decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type|
|
55
|
+
TimeZoneConverter.new(type)
|
56
|
+
end
|
47
57
|
end
|
58
|
+
super
|
48
59
|
end
|
49
60
|
|
50
|
-
|
51
|
-
def create_time_zone_conversion_attribute?(name, column)
|
61
|
+
def create_time_zone_conversion_attribute?(name, cast_type)
|
52
62
|
time_zone_aware_attributes &&
|
53
63
|
!self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) &&
|
54
|
-
(:datetime ==
|
55
|
-
end
|
56
|
-
end
|
57
|
-
|
58
|
-
private
|
59
|
-
|
60
|
-
def convert_value_to_time_zone(attr_name, value)
|
61
|
-
if value.is_a?(Array)
|
62
|
-
value.map { |v| convert_value_to_time_zone(attr_name, v) }
|
63
|
-
elsif value.respond_to?(:in_time_zone)
|
64
|
-
value.in_time_zone || self.class.columns_hash[attr_name].type_cast(value)
|
65
|
-
else
|
66
|
-
nil
|
64
|
+
(:datetime == cast_type.type)
|
67
65
|
end
|
68
66
|
end
|
69
67
|
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 {
|
@@ -52,14 +50,14 @@ module ActiveRecord
|
|
52
50
|
end
|
53
51
|
|
54
52
|
# Updates the attribute identified by <tt>attr_name</tt> with the
|
55
|
-
# specified +value+. Empty strings for
|
53
|
+
# specified +value+. Empty strings for Integer and Float columns are
|
56
54
|
# turned into +nil+.
|
57
55
|
def write_attribute(attr_name, value)
|
58
|
-
write_attribute_with_type_cast(attr_name, value,
|
56
|
+
write_attribute_with_type_cast(attr_name, value, true)
|
59
57
|
end
|
60
58
|
|
61
59
|
def raw_write_attribute(attr_name, value)
|
62
|
-
write_attribute_with_type_cast(attr_name, value,
|
60
|
+
write_attribute_with_type_cast(attr_name, value, false)
|
63
61
|
end
|
64
62
|
|
65
63
|
private
|
@@ -68,30 +66,17 @@ module ActiveRecord
|
|
68
66
|
write_attribute(attribute_name, value)
|
69
67
|
end
|
70
68
|
|
71
|
-
def
|
72
|
-
return value unless column
|
73
|
-
|
74
|
-
column.type_cast_for_write value
|
75
|
-
end
|
76
|
-
alias_method :raw_type_cast_attribute_for_write, :type_cast_attribute_for_write
|
77
|
-
|
78
|
-
def write_attribute_with_type_cast(attr_name, value, type_cast_method)
|
69
|
+
def write_attribute_with_type_cast(attr_name, value, should_type_cast)
|
79
70
|
attr_name = attr_name.to_s
|
80
71
|
attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
|
81
|
-
@attributes_cache.delete(attr_name)
|
82
|
-
column = column_for_attribute(attr_name)
|
83
72
|
|
84
|
-
|
85
|
-
|
86
|
-
if column && column.binary?
|
87
|
-
@attributes_cache[attr_name] = value
|
88
|
-
end
|
89
|
-
|
90
|
-
if column || @attributes.has_key?(attr_name)
|
91
|
-
@attributes[attr_name] = send(type_cast_method, column, value)
|
73
|
+
if should_type_cast
|
74
|
+
@attributes.write_from_user(attr_name, value)
|
92
75
|
else
|
93
|
-
|
76
|
+
@attributes.write_cast_value(attr_name, value)
|
94
77
|
end
|
78
|
+
|
79
|
+
value
|
95
80
|
end
|
96
81
|
end
|
97
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 {
|
@@ -48,7 +51,11 @@ 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
|
|
54
61
|
class GeneratedAttributeMethods < Module; end # :nodoc:
|
@@ -71,7 +78,7 @@ module ActiveRecord
|
|
71
78
|
# accessors, mutators and query methods.
|
72
79
|
def define_attribute_methods # :nodoc:
|
73
80
|
return false if @attribute_methods_generated
|
74
|
-
# Use a mutex; we don't want two
|
81
|
+
# Use a mutex; we don't want two threads simultaneously trying to define
|
75
82
|
# attribute methods.
|
76
83
|
generated_attribute_methods.synchronize do
|
77
84
|
return false if @attribute_methods_generated
|
@@ -155,16 +162,6 @@ module ActiveRecord
|
|
155
162
|
end
|
156
163
|
end
|
157
164
|
|
158
|
-
def find_generated_attribute_method(method_name) # :nodoc:
|
159
|
-
klass = self
|
160
|
-
until klass == Base
|
161
|
-
gen_methods = klass.generated_attribute_methods
|
162
|
-
return gen_methods.instance_method(method_name) if method_defined_within?(method_name, gen_methods, Object)
|
163
|
-
klass = klass.superclass
|
164
|
-
end
|
165
|
-
nil
|
166
|
-
end
|
167
|
-
|
168
165
|
# Returns +true+ if +attribute+ is an attribute method and table exists,
|
169
166
|
# +false+ otherwise.
|
170
167
|
#
|
@@ -193,24 +190,29 @@ module ActiveRecord
|
|
193
190
|
[]
|
194
191
|
end
|
195
192
|
end
|
196
|
-
end
|
197
193
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
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
|
211
214
|
end
|
212
|
-
|
213
|
-
super
|
215
|
+
column
|
214
216
|
end
|
215
217
|
end
|
216
218
|
|
@@ -231,18 +233,14 @@ module ActiveRecord
|
|
231
233
|
# person.respond_to('age?') # => true
|
232
234
|
# person.respond_to(:nothing) # => false
|
233
235
|
def respond_to?(name, include_private = false)
|
236
|
+
return false unless super
|
234
237
|
name = name.to_s
|
235
|
-
self.class.define_attribute_methods
|
236
|
-
result = super
|
237
|
-
|
238
|
-
# If the result is false the answer is false.
|
239
|
-
return false unless result
|
240
238
|
|
241
239
|
# If the result is true then check for the select case.
|
242
240
|
# For queries selecting a subset of columns, return false for unselected columns.
|
243
241
|
# We check defined?(@attributes) not to issue warnings if called on objects that
|
244
242
|
# have been allocated but not yet initialized.
|
245
|
-
if defined?(@attributes) &&
|
243
|
+
if defined?(@attributes) && self.class.column_names.include?(name)
|
246
244
|
return has_attribute?(name)
|
247
245
|
end
|
248
246
|
|
@@ -259,7 +257,7 @@ module ActiveRecord
|
|
259
257
|
# person.has_attribute?('age') # => true
|
260
258
|
# person.has_attribute?(:nothing) # => false
|
261
259
|
def has_attribute?(attr_name)
|
262
|
-
@attributes.
|
260
|
+
@attributes.key?(attr_name.to_s)
|
263
261
|
end
|
264
262
|
|
265
263
|
# Returns an array of names for the attributes available on this object.
|
@@ -283,22 +281,14 @@ module ActiveRecord
|
|
283
281
|
# person.attributes
|
284
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}
|
285
283
|
def attributes
|
286
|
-
|
287
|
-
attrs[name] = read_attribute(name)
|
288
|
-
}
|
289
|
-
end
|
290
|
-
|
291
|
-
# Placeholder so it can be overriden when needed by serialization
|
292
|
-
def attributes_for_coder # :nodoc:
|
293
|
-
attributes
|
284
|
+
@attributes.to_hash
|
294
285
|
end
|
295
286
|
|
296
287
|
# Returns an <tt>#inspect</tt>-like string for the value of the
|
297
|
-
# attribute +attr_name+. String attributes are truncated
|
288
|
+
# attribute +attr_name+. String attributes are truncated up to 50
|
298
289
|
# characters, Date and Time attributes are returned in the
|
299
|
-
# <tt>:db</tt> format
|
300
|
-
#
|
301
|
-
# modification.
|
290
|
+
# <tt>:db</tt> format. Other attributes return the value of
|
291
|
+
# <tt>#inspect</tt> without modification.
|
302
292
|
#
|
303
293
|
# person = Person.create!(name: 'David Heinemeier Hansson ' * 3)
|
304
294
|
#
|
@@ -309,7 +299,7 @@ module ActiveRecord
|
|
309
299
|
# # => "\"2012-10-22 00:15:07\""
|
310
300
|
#
|
311
301
|
# person.attribute_for_inspect(:tag_ids)
|
312
|
-
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10,
|
302
|
+
# # => "[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]"
|
313
303
|
def attribute_for_inspect(attr_name)
|
314
304
|
value = read_attribute(attr_name)
|
315
305
|
|
@@ -317,9 +307,6 @@ module ActiveRecord
|
|
317
307
|
"#{value[0, 50]}...".inspect
|
318
308
|
elsif value.is_a?(Date) || value.is_a?(Time)
|
319
309
|
%("#{value.to_s(:db)}")
|
320
|
-
elsif value.is_a?(Array) && value.size > 10
|
321
|
-
inspected = value.first(10).inspect
|
322
|
-
%(#{inspected[0...-1]}, ...])
|
323
310
|
else
|
324
311
|
value.inspect
|
325
312
|
end
|
@@ -333,39 +320,24 @@ module ActiveRecord
|
|
333
320
|
# class Task < ActiveRecord::Base
|
334
321
|
# end
|
335
322
|
#
|
336
|
-
#
|
337
|
-
#
|
338
|
-
#
|
339
|
-
#
|
340
|
-
#
|
341
|
-
#
|
342
|
-
#
|
323
|
+
# task = Task.new(title: '', is_done: false)
|
324
|
+
# task.attribute_present?(:title) # => false
|
325
|
+
# task.attribute_present?(:is_done) # => true
|
326
|
+
# task.title = 'Buy milk'
|
327
|
+
# task.is_done = true
|
328
|
+
# task.attribute_present?(:title) # => true
|
329
|
+
# task.attribute_present?(:is_done) # => true
|
343
330
|
def attribute_present?(attribute)
|
344
|
-
value =
|
331
|
+
value = _read_attribute(attribute)
|
345
332
|
!value.nil? && !(value.respond_to?(:empty?) && value.empty?)
|
346
333
|
end
|
347
334
|
|
348
|
-
# Returns the column object for the named attribute. Returns +nil+ if the
|
349
|
-
# named attribute not exists.
|
350
|
-
#
|
351
|
-
# class Person < ActiveRecord::Base
|
352
|
-
# end
|
353
|
-
#
|
354
|
-
# person = Person.new
|
355
|
-
# person.column_for_attribute(:name) # the result depends on the ConnectionAdapter
|
356
|
-
# # => #<ActiveRecord::ConnectionAdapters::SQLite3Column:0x007ff4ab083980 @name="name", @sql_type="varchar(255)", @null=true, ...>
|
357
|
-
#
|
358
|
-
# person.column_for_attribute(:nothing)
|
359
|
-
# # => nil
|
360
|
-
def column_for_attribute(name)
|
361
|
-
# FIXME: should this return a null object for columns that don't exist?
|
362
|
-
self.class.columns_hash[name.to_s]
|
363
|
-
end
|
364
|
-
|
365
335
|
# Returns the value of the attribute identified by <tt>attr_name</tt> after it has been typecast (for example,
|
366
336
|
# "2004-12-12" in a date column is cast to a date object, like Date.new(2004, 12, 12)). It raises
|
367
337
|
# <tt>ActiveModel::MissingAttributeError</tt> if the identified attribute is missing.
|
368
338
|
#
|
339
|
+
# Note: +:id+ is always present.
|
340
|
+
#
|
369
341
|
# Alias for the <tt>read_attribute</tt> method.
|
370
342
|
#
|
371
343
|
# class Person < ActiveRecord::Base
|
@@ -392,20 +364,13 @@ module ActiveRecord
|
|
392
364
|
# person = Person.new
|
393
365
|
# person[:age] = '22'
|
394
366
|
# person[:age] # => 22
|
395
|
-
# person[:age] # =>
|
367
|
+
# person[:age].class # => Integer
|
396
368
|
def []=(attr_name, value)
|
397
369
|
write_attribute(attr_name, value)
|
398
370
|
end
|
399
371
|
|
400
372
|
protected
|
401
373
|
|
402
|
-
def clone_attributes(reader_method = :read_attribute, attributes = {}) # :nodoc:
|
403
|
-
attribute_names.each do |name|
|
404
|
-
attributes[name] = clone_attribute_value(reader_method, name)
|
405
|
-
end
|
406
|
-
attributes
|
407
|
-
end
|
408
|
-
|
409
374
|
def clone_attribute_value(reader_method, attribute_name) # :nodoc:
|
410
375
|
value = send(reader_method, attribute_name)
|
411
376
|
value.duplicable? ? value.clone : value
|
@@ -423,7 +388,7 @@ module ActiveRecord
|
|
423
388
|
|
424
389
|
def attribute_method?(attr_name) # :nodoc:
|
425
390
|
# We check defined? because Syck calls respond_to? before actually calling initialize.
|
426
|
-
defined?(@attributes) && @attributes.
|
391
|
+
defined?(@attributes) && @attributes.key?(attr_name)
|
427
392
|
end
|
428
393
|
|
429
394
|
private
|
@@ -442,16 +407,16 @@ module ActiveRecord
|
|
442
407
|
|
443
408
|
# Filters the primary keys and readonly attributes from the attribute names.
|
444
409
|
def attributes_for_update(attribute_names)
|
445
|
-
attribute_names.
|
446
|
-
|
410
|
+
attribute_names.reject do |name|
|
411
|
+
readonly_attribute?(name)
|
447
412
|
end
|
448
413
|
end
|
449
414
|
|
450
415
|
# Filters out the primary keys, from the attribute names, when the primary
|
451
416
|
# key is to be generated (e.g. the id attribute has no value).
|
452
417
|
def attributes_for_create(attribute_names)
|
453
|
-
attribute_names.
|
454
|
-
|
418
|
+
attribute_names.reject do |name|
|
419
|
+
pk_attribute?(name) && id.nil?
|
455
420
|
end
|
456
421
|
end
|
457
422
|
|
@@ -460,14 +425,11 @@ module ActiveRecord
|
|
460
425
|
end
|
461
426
|
|
462
427
|
def pk_attribute?(name)
|
463
|
-
|
428
|
+
name == self.class.primary_key
|
464
429
|
end
|
465
430
|
|
466
431
|
def typecasted_attribute_value(name)
|
467
|
-
|
468
|
-
# If the values stored in @attributes were already typecasted, this code
|
469
|
-
# could be simplified
|
470
|
-
read_attribute(name)
|
432
|
+
_read_attribute(name)
|
471
433
|
end
|
472
434
|
end
|
473
435
|
end
|