activerecord 4.1.15 → 4.2.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of activerecord might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +634 -2176
- data/README.rdoc +15 -10
- data/lib/active_record/aggregations.rb +12 -8
- data/lib/active_record/associations/association.rb +1 -1
- data/lib/active_record/associations/association_scope.rb +53 -21
- data/lib/active_record/associations/belongs_to_association.rb +15 -5
- data/lib/active_record/associations/builder/association.rb +16 -5
- data/lib/active_record/associations/builder/belongs_to.rb +7 -29
- data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +2 -11
- 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 +32 -44
- data/lib/active_record/associations/collection_proxy.rb +1 -10
- data/lib/active_record/associations/has_many_association.rb +60 -14
- data/lib/active_record/associations/has_many_through_association.rb +34 -23
- data/lib/active_record/associations/has_one_association.rb +0 -1
- data/lib/active_record/associations/join_dependency/join_association.rb +18 -14
- data/lib/active_record/associations/join_dependency.rb +7 -9
- data/lib/active_record/associations/preloader/association.rb +9 -5
- data/lib/active_record/associations/preloader/through_association.rb +3 -3
- data/lib/active_record/associations/preloader.rb +2 -2
- data/lib/active_record/associations/singular_association.rb +16 -1
- data/lib/active_record/associations/through_association.rb +6 -22
- data/lib/active_record/associations.rb +58 -33
- data/lib/active_record/attribute.rb +131 -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 +2 -2
- data/lib/active_record/attribute_methods/dirty.rb +85 -42
- data/lib/active_record/attribute_methods/primary_key.rb +6 -8
- data/lib/active_record/attribute_methods/read.rb +14 -57
- data/lib/active_record/attribute_methods/serialization.rb +12 -146
- data/lib/active_record/attribute_methods/time_zone_conversion.rb +32 -40
- data/lib/active_record/attribute_methods/write.rb +8 -23
- data/lib/active_record/attribute_methods.rb +53 -90
- data/lib/active_record/attribute_set/builder.rb +32 -0
- data/lib/active_record/attribute_set.rb +77 -0
- data/lib/active_record/attributes.rb +122 -0
- data/lib/active_record/autosave_association.rb +11 -21
- data/lib/active_record/base.rb +9 -19
- data/lib/active_record/connection_adapters/abstract/connection_pool.rb +69 -45
- data/lib/active_record/connection_adapters/abstract/database_statements.rb +22 -42
- data/lib/active_record/connection_adapters/abstract/quoting.rb +59 -60
- data/lib/active_record/connection_adapters/abstract/schema_creation.rb +37 -2
- data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +102 -21
- data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +9 -33
- data/lib/active_record/connection_adapters/abstract/schema_statements.rb +178 -55
- data/lib/active_record/connection_adapters/abstract/transaction.rb +120 -115
- data/lib/active_record/connection_adapters/abstract_adapter.rb +143 -57
- data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +156 -107
- data/lib/active_record/connection_adapters/column.rb +13 -244
- data/lib/active_record/connection_adapters/connection_specification.rb +6 -20
- data/lib/active_record/connection_adapters/mysql2_adapter.rb +12 -15
- data/lib/active_record/connection_adapters/mysql_adapter.rb +55 -143
- 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 -20
- data/lib/active_record/connection_adapters/postgresql/oid/array.rb +96 -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 +76 -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 +85 -0
- data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +26 -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 +42 -122
- data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
- data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +154 -0
- data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +86 -34
- data/lib/active_record/connection_adapters/postgresql/utils.rb +66 -0
- data/lib/active_record/connection_adapters/postgresql_adapter.rb +188 -452
- data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
- data/lib/active_record/connection_adapters/sqlite3_adapter.rb +54 -47
- data/lib/active_record/connection_handling.rb +1 -1
- data/lib/active_record/core.rb +119 -22
- data/lib/active_record/counter_cache.rb +60 -6
- data/lib/active_record/enum.rb +9 -10
- data/lib/active_record/errors.rb +27 -26
- data/lib/active_record/explain.rb +1 -1
- data/lib/active_record/fixtures.rb +52 -45
- data/lib/active_record/gem_version.rb +3 -3
- data/lib/active_record/inheritance.rb +33 -8
- data/lib/active_record/integration.rb +4 -4
- data/lib/active_record/locking/optimistic.rb +34 -16
- 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 +22 -32
- data/lib/active_record/model_schema.rb +39 -48
- data/lib/active_record/nested_attributes.rb +8 -18
- data/lib/active_record/persistence.rb +39 -22
- data/lib/active_record/query_cache.rb +3 -3
- data/lib/active_record/querying.rb +1 -8
- data/lib/active_record/railtie.rb +17 -10
- data/lib/active_record/railties/databases.rake +47 -42
- data/lib/active_record/readonly_attributes.rb +0 -1
- data/lib/active_record/reflection.rb +225 -92
- data/lib/active_record/relation/batches.rb +0 -2
- data/lib/active_record/relation/calculations.rb +28 -32
- data/lib/active_record/relation/delegation.rb +1 -1
- data/lib/active_record/relation/finder_methods.rb +42 -20
- data/lib/active_record/relation/merger.rb +0 -1
- data/lib/active_record/relation/predicate_builder/array_handler.rb +16 -11
- data/lib/active_record/relation/predicate_builder/relation_handler.rb +0 -4
- data/lib/active_record/relation/predicate_builder.rb +1 -22
- data/lib/active_record/relation/query_methods.rb +98 -62
- data/lib/active_record/relation/spawn_methods.rb +6 -7
- data/lib/active_record/relation.rb +35 -11
- data/lib/active_record/result.rb +16 -9
- data/lib/active_record/sanitization.rb +8 -1
- data/lib/active_record/schema.rb +0 -1
- data/lib/active_record/schema_dumper.rb +51 -9
- data/lib/active_record/schema_migration.rb +4 -0
- data/lib/active_record/scoping/default.rb +5 -4
- data/lib/active_record/serializers/xml_serializer.rb +3 -7
- data/lib/active_record/statement_cache.rb +79 -5
- data/lib/active_record/store.rb +5 -5
- data/lib/active_record/tasks/database_tasks.rb +37 -5
- data/lib/active_record/tasks/mysql_database_tasks.rb +1 -1
- data/lib/active_record/tasks/postgresql_database_tasks.rb +2 -2
- data/lib/active_record/timestamp.rb +9 -7
- data/lib/active_record/transactions.rb +35 -21
- data/lib/active_record/type/binary.rb +40 -0
- data/lib/active_record/type/boolean.rb +19 -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/float.rb +19 -0
- data/lib/active_record/type/hash_lookup_type_map.rb +19 -0
- data/lib/active_record/type/integer.rb +23 -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 +51 -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 +48 -0
- data/lib/active_record/type/value.rb +101 -0
- data/lib/active_record/type.rb +20 -0
- data/lib/active_record/validations/uniqueness.rb +9 -23
- data/lib/active_record/validations.rb +21 -16
- data/lib/active_record.rb +2 -1
- 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 +71 -14
- data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -34,28 +34,43 @@ 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
|
+
|
46
|
+
def changed?
|
47
|
+
super || changed_in_place.any?
|
48
|
+
end
|
49
|
+
|
50
|
+
def changed
|
51
|
+
super | changed_in_place
|
52
|
+
end
|
53
|
+
|
54
|
+
def changes_applied
|
55
|
+
super
|
56
|
+
store_original_raw_attributes
|
57
|
+
end
|
45
58
|
|
46
|
-
|
47
|
-
def initialize_internals_callback
|
59
|
+
def clear_changes_information
|
48
60
|
super
|
49
|
-
|
61
|
+
original_raw_attributes.clear
|
62
|
+
end
|
63
|
+
|
64
|
+
def changed_attributes
|
65
|
+
super.reverse_merge(attributes_changed_in_place).freeze
|
50
66
|
end
|
51
67
|
|
52
|
-
|
68
|
+
private
|
69
|
+
|
70
|
+
def calculate_changes_from_defaults
|
53
71
|
@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])
|
72
|
+
self.class.column_defaults.each do |attr, orig_value|
|
73
|
+
set_attribute_was(attr, orig_value) if _field_changed?(attr, orig_value)
|
59
74
|
end
|
60
75
|
end
|
61
76
|
|
@@ -63,19 +78,35 @@ module ActiveRecord
|
|
63
78
|
def write_attribute(attr, value)
|
64
79
|
attr = attr.to_s
|
65
80
|
|
66
|
-
|
81
|
+
old_value = old_attribute_value(attr)
|
82
|
+
|
83
|
+
result = super
|
84
|
+
store_original_raw_attribute(attr)
|
85
|
+
save_changed_attribute(attr, old_value)
|
86
|
+
result
|
87
|
+
end
|
88
|
+
|
89
|
+
def raw_write_attribute(attr, value)
|
90
|
+
attr = attr.to_s
|
91
|
+
|
92
|
+
result = super
|
93
|
+
original_raw_attributes[attr] = value
|
94
|
+
result
|
95
|
+
end
|
67
96
|
|
68
|
-
|
97
|
+
def save_changed_attribute(attr, old_value)
|
98
|
+
if attribute_changed?(attr)
|
99
|
+
clear_attribute_changes(attr) unless _field_changed?(attr, old_value)
|
100
|
+
else
|
101
|
+
set_attribute_was(attr, old_value) if _field_changed?(attr, old_value)
|
102
|
+
end
|
69
103
|
end
|
70
104
|
|
71
|
-
def
|
72
|
-
# The attribute already has an unsaved change.
|
105
|
+
def old_attribute_value(attr)
|
73
106
|
if attribute_changed?(attr)
|
74
|
-
|
75
|
-
changed_attributes.delete(attr) unless _field_changed?(attr, old, value)
|
107
|
+
changed_attributes[attr]
|
76
108
|
else
|
77
|
-
|
78
|
-
changed_attributes[attr] = old if _field_changed?(attr, old, value)
|
109
|
+
clone_attribute_value(:read_attribute, attr)
|
79
110
|
end
|
80
111
|
end
|
81
112
|
|
@@ -93,34 +124,46 @@ module ActiveRecord
|
|
93
124
|
changed
|
94
125
|
end
|
95
126
|
|
96
|
-
def _field_changed?(attr,
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
127
|
+
def _field_changed?(attr, old_value)
|
128
|
+
@attributes[attr].changed_from?(old_value)
|
129
|
+
end
|
130
|
+
|
131
|
+
def attributes_changed_in_place
|
132
|
+
changed_in_place.each_with_object({}) do |attr_name, h|
|
133
|
+
orig = @attributes[attr_name].original_value
|
134
|
+
h[attr_name] = orig
|
104
135
|
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def changed_in_place
|
139
|
+
self.class.attribute_names.select do |attr_name|
|
140
|
+
changed_in_place?(attr_name)
|
141
|
+
end
|
142
|
+
end
|
105
143
|
|
106
|
-
|
144
|
+
def changed_in_place?(attr_name)
|
145
|
+
old_value = original_raw_attribute(attr_name)
|
146
|
+
@attributes[attr_name].changed_in_place_from?(old_value)
|
107
147
|
end
|
108
148
|
|
109
|
-
def
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
149
|
+
def original_raw_attribute(attr_name)
|
150
|
+
original_raw_attributes.fetch(attr_name) do
|
151
|
+
read_attribute_before_type_cast(attr_name)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
def original_raw_attributes
|
156
|
+
@original_raw_attributes ||= {}
|
115
157
|
end
|
116
158
|
|
117
|
-
def
|
118
|
-
|
119
|
-
old == 0 && value.is_a?(String) && value.present? && non_zero?(value)
|
159
|
+
def store_original_raw_attribute(attr_name)
|
160
|
+
original_raw_attributes[attr_name] = @attributes[attr_name].value_for_database
|
120
161
|
end
|
121
162
|
|
122
|
-
def
|
123
|
-
|
163
|
+
def store_original_raw_attributes
|
164
|
+
attribute_names.each do |attr|
|
165
|
+
store_original_raw_attribute(attr)
|
166
|
+
end
|
124
167
|
end
|
125
168
|
end
|
126
169
|
end
|
@@ -15,9 +15,10 @@ module ActiveRecord
|
|
15
15
|
|
16
16
|
# Returns the primary key value.
|
17
17
|
def id
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
if pk = self.class.primary_key
|
19
|
+
sync_with_transaction_state
|
20
|
+
read_attribute(pk)
|
21
|
+
end
|
21
22
|
end
|
22
23
|
|
23
24
|
# Sets the primary key value.
|
@@ -88,12 +89,9 @@ module ActiveRecord
|
|
88
89
|
end
|
89
90
|
|
90
91
|
def get_primary_key(base_name) #:nodoc:
|
91
|
-
|
92
|
-
|
93
|
-
case primary_key_prefix_type
|
94
|
-
when :table_name
|
92
|
+
if base_name && primary_key_prefix_type == :table_name
|
95
93
|
base_name.foreign_key(false)
|
96
|
-
|
94
|
+
elsif base_name && primary_key_prefix_type == :table_name_with_underscore
|
97
95
|
base_name.foreign_key
|
98
96
|
else
|
99
97
|
if ActiveRecord::Base != self && table_exists?
|
@@ -22,7 +22,7 @@ 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}
|
@@ -35,35 +35,22 @@ 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
|
-
|
48
|
+
def cached_attributes_deprecation_warning(method_name)
|
49
|
+
ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
|
50
|
+
Calling `#{method_name}` is no longer necessary. All attributes are cached.
|
51
|
+
MESSAGE
|
63
52
|
end
|
64
53
|
|
65
|
-
protected
|
66
|
-
|
67
54
|
if Module.methods_transplantable?
|
68
55
|
def define_method_attribute(name)
|
69
56
|
method = ReaderMethodCache[name]
|
@@ -89,45 +76,15 @@ module ActiveRecord
|
|
89
76
|
end
|
90
77
|
end
|
91
78
|
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
79
|
end
|
103
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
|
-
column ||= @column_types[name]
|
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
|
-
}
|
124
|
-
|
125
|
-
if self.class.cache_attribute?(name)
|
126
|
-
@attributes_cache[name] = column.type_cast(value)
|
127
|
-
else
|
128
|
-
column.type_cast value
|
129
|
-
end
|
130
|
-
}
|
86
|
+
name = self.class.primary_key if name == 'id'
|
87
|
+
@attributes.fetch_value(name, &block)
|
131
88
|
end
|
132
89
|
|
133
90
|
private
|
@@ -3,20 +3,7 @@ module ActiveRecord
|
|
3
3
|
module Serialization
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
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
6
|
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
7
|
# If you have an attribute that needs to be saved to the database as an
|
21
8
|
# object, and retrieved as the same object, then specify the name of that
|
22
9
|
# attribute using this method and it will be handled automatically. The
|
@@ -50,8 +37,6 @@ module ActiveRecord
|
|
50
37
|
# serialize :preferences, Hash
|
51
38
|
# end
|
52
39
|
def serialize(attr_name, class_name_or_coder = Object)
|
53
|
-
include Behavior
|
54
|
-
|
55
40
|
# When ::JSON is used, force it to go through the Active Support JSON encoder
|
56
41
|
# to ensure special objects (e.g. Active Record models) are dumped correctly
|
57
42
|
# using the #as_json hook.
|
@@ -63,140 +48,21 @@ module ActiveRecord
|
|
63
48
|
Coders::YAMLColumn.new(class_name_or_coder)
|
64
49
|
end
|
65
50
|
|
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
|
51
|
+
decorate_attribute_type(attr_name, :serialize) do |type|
|
52
|
+
Type::Serialized.new(type, coder)
|
82
53
|
end
|
83
54
|
end
|
84
55
|
|
85
|
-
def
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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
|
147
|
-
end
|
148
|
-
end
|
149
|
-
|
150
|
-
def raw_type_cast_attribute_for_write(column, value)
|
151
|
-
if column && coder = self.class.serialized_attributes[column.name]
|
152
|
-
Attribute.new(coder, value, :serialized)
|
153
|
-
else
|
154
|
-
super
|
155
|
-
end
|
156
|
-
end
|
157
|
-
|
158
|
-
def _field_changed?(attr, old, value)
|
159
|
-
if self.class.serialized_attributes.include?(attr)
|
160
|
-
old != value
|
161
|
-
else
|
162
|
-
super
|
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
|
56
|
+
def serialized_attributes
|
57
|
+
ActiveSupport::Deprecation.warn(<<-WARNING.strip_heredoc)
|
58
|
+
`serialized_attributes` is deprecated without replacement, and will
|
59
|
+
be removed in Rails 5.0.
|
60
|
+
WARNING
|
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,27 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module AttributeMethods
|
3
3
|
module TimeZoneConversion
|
4
|
-
class
|
5
|
-
def
|
6
|
-
|
4
|
+
class TimeZoneConverter < SimpleDelegator # :nodoc:
|
5
|
+
def type_cast_from_database(value)
|
6
|
+
convert_time_to_time_zone(super)
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
10
|
-
value
|
11
|
-
|
9
|
+
def type_cast_from_user(value)
|
10
|
+
if value.is_a?(Array)
|
11
|
+
value.map { |v| type_cast_from_user(v) }
|
12
|
+
elsif value.respond_to?(:in_time_zone)
|
13
|
+
value.in_time_zone
|
14
|
+
end
|
12
15
|
end
|
13
16
|
|
14
|
-
def
|
15
|
-
|
17
|
+
def convert_time_to_time_zone(value)
|
18
|
+
if value.is_a?(Array)
|
19
|
+
value.map { |v| convert_time_to_time_zone(v) }
|
20
|
+
elsif value.acts_like?(:time)
|
21
|
+
value.in_time_zone
|
22
|
+
else
|
23
|
+
value
|
24
|
+
end
|
16
25
|
end
|
17
26
|
end
|
18
27
|
|
@@ -27,43 +36,26 @@ module ActiveRecord
|
|
27
36
|
end
|
28
37
|
|
29
38
|
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
|
39
|
+
private
|
40
|
+
|
41
|
+
def inherited(subclass)
|
42
|
+
# We need to apply this decorator here, rather than on module inclusion. The closure
|
43
|
+
# created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
|
44
|
+
# sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
|
45
|
+
# `skip_time_zone_conversion_for_attributes` would not be picked up.
|
46
|
+
subclass.class_eval do
|
47
|
+
matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
|
48
|
+
decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type|
|
49
|
+
TimeZoneConverter.new(type)
|
50
|
+
end
|
47
51
|
end
|
52
|
+
super
|
48
53
|
end
|
49
54
|
|
50
|
-
|
51
|
-
def create_time_zone_conversion_attribute?(name, column)
|
55
|
+
def create_time_zone_conversion_attribute?(name, cast_type)
|
52
56
|
time_zone_aware_attributes &&
|
53
57
|
!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
|
58
|
+
(:datetime == cast_type.type)
|
67
59
|
end
|
68
60
|
end
|
69
61
|
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,11 +53,11 @@ 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
|
-
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_from_database(attr_name, value)
|
94
77
|
end
|
78
|
+
|
79
|
+
value
|
95
80
|
end
|
96
81
|
end
|
97
82
|
end
|