activerecord 4.1.15 → 4.2.11.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (185) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +1162 -1792
  3. data/README.rdoc +15 -10
  4. data/lib/active_record.rb +4 -0
  5. data/lib/active_record/aggregations.rb +15 -8
  6. data/lib/active_record/association_relation.rb +13 -0
  7. data/lib/active_record/associations.rb +158 -49
  8. data/lib/active_record/associations/alias_tracker.rb +3 -12
  9. data/lib/active_record/associations/association.rb +16 -4
  10. data/lib/active_record/associations/association_scope.rb +83 -38
  11. data/lib/active_record/associations/belongs_to_association.rb +28 -10
  12. data/lib/active_record/associations/builder/association.rb +15 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +7 -29
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -1
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +8 -13
  16. data/lib/active_record/associations/builder/has_many.rb +1 -1
  17. data/lib/active_record/associations/builder/has_one.rb +2 -2
  18. data/lib/active_record/associations/builder/singular_association.rb +8 -1
  19. data/lib/active_record/associations/collection_association.rb +63 -27
  20. data/lib/active_record/associations/collection_proxy.rb +29 -35
  21. data/lib/active_record/associations/foreign_association.rb +11 -0
  22. data/lib/active_record/associations/has_many_association.rb +83 -22
  23. data/lib/active_record/associations/has_many_through_association.rb +49 -26
  24. data/lib/active_record/associations/has_one_association.rb +1 -1
  25. data/lib/active_record/associations/join_dependency.rb +26 -13
  26. data/lib/active_record/associations/join_dependency/join_association.rb +25 -15
  27. data/lib/active_record/associations/join_dependency/join_part.rb +0 -1
  28. data/lib/active_record/associations/preloader.rb +36 -26
  29. data/lib/active_record/associations/preloader/association.rb +14 -11
  30. data/lib/active_record/associations/preloader/through_association.rb +4 -3
  31. data/lib/active_record/associations/singular_association.rb +17 -2
  32. data/lib/active_record/associations/through_association.rb +5 -12
  33. data/lib/active_record/attribute.rb +163 -0
  34. data/lib/active_record/attribute_assignment.rb +19 -11
  35. data/lib/active_record/attribute_decorators.rb +66 -0
  36. data/lib/active_record/attribute_methods.rb +56 -94
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +7 -2
  38. data/lib/active_record/attribute_methods/dirty.rb +107 -43
  39. data/lib/active_record/attribute_methods/primary_key.rb +7 -8
  40. data/lib/active_record/attribute_methods/query.rb +1 -1
  41. data/lib/active_record/attribute_methods/read.rb +22 -59
  42. data/lib/active_record/attribute_methods/serialization.rb +16 -150
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +38 -40
  44. data/lib/active_record/attribute_methods/write.rb +9 -24
  45. data/lib/active_record/attribute_set.rb +81 -0
  46. data/lib/active_record/attribute_set/builder.rb +106 -0
  47. data/lib/active_record/attributes.rb +147 -0
  48. data/lib/active_record/autosave_association.rb +19 -12
  49. data/lib/active_record/base.rb +13 -24
  50. data/lib/active_record/callbacks.rb +6 -6
  51. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +84 -52
  52. data/lib/active_record/connection_adapters/abstract/database_statements.rb +52 -50
  53. data/lib/active_record/connection_adapters/abstract/query_cache.rb +1 -1
  54. data/lib/active_record/connection_adapters/abstract/quoting.rb +60 -60
  55. data/lib/active_record/connection_adapters/abstract/savepoints.rb +1 -1
  56. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +39 -4
  57. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +138 -56
  58. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +14 -34
  59. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +268 -71
  60. data/lib/active_record/connection_adapters/abstract/transaction.rb +125 -118
  61. data/lib/active_record/connection_adapters/abstract_adapter.rb +171 -59
  62. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +293 -139
  63. data/lib/active_record/connection_adapters/column.rb +29 -240
  64. data/lib/active_record/connection_adapters/connection_specification.rb +15 -24
  65. data/lib/active_record/connection_adapters/mysql2_adapter.rb +16 -32
  66. data/lib/active_record/connection_adapters/mysql_adapter.rb +67 -144
  67. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +15 -27
  68. data/lib/active_record/connection_adapters/postgresql/column.rb +20 -0
  69. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +40 -25
  70. data/lib/active_record/connection_adapters/postgresql/oid.rb +29 -388
  71. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +100 -0
  72. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  73. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  74. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  75. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +46 -0
  76. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +11 -0
  77. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +36 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  79. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  80. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +21 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  82. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +13 -0
  84. data/lib/active_record/connection_adapters/postgresql/oid/integer.rb +11 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +35 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +43 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +79 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +19 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +11 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  96. data/lib/active_record/connection_adapters/postgresql/quoting.rb +46 -136
  97. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +4 -4
  98. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +152 -0
  99. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +131 -43
  100. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  101. data/lib/active_record/connection_adapters/postgresql_adapter.rb +224 -477
  102. data/lib/active_record/connection_adapters/schema_cache.rb +14 -28
  103. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +61 -75
  104. data/lib/active_record/connection_handling.rb +1 -1
  105. data/lib/active_record/core.rb +163 -39
  106. data/lib/active_record/counter_cache.rb +60 -6
  107. data/lib/active_record/enum.rb +9 -11
  108. data/lib/active_record/errors.rb +53 -30
  109. data/lib/active_record/explain.rb +1 -1
  110. data/lib/active_record/explain_subscriber.rb +1 -1
  111. data/lib/active_record/fixtures.rb +55 -69
  112. data/lib/active_record/gem_version.rb +4 -4
  113. data/lib/active_record/inheritance.rb +35 -10
  114. data/lib/active_record/integration.rb +4 -4
  115. data/lib/active_record/legacy_yaml_adapter.rb +30 -0
  116. data/lib/active_record/locking/optimistic.rb +46 -26
  117. data/lib/active_record/migration.rb +71 -46
  118. data/lib/active_record/migration/command_recorder.rb +19 -2
  119. data/lib/active_record/migration/join_table.rb +1 -1
  120. data/lib/active_record/model_schema.rb +52 -58
  121. data/lib/active_record/nested_attributes.rb +5 -5
  122. data/lib/active_record/no_touching.rb +1 -1
  123. data/lib/active_record/persistence.rb +46 -26
  124. data/lib/active_record/query_cache.rb +3 -3
  125. data/lib/active_record/querying.rb +10 -7
  126. data/lib/active_record/railtie.rb +18 -11
  127. data/lib/active_record/railties/databases.rake +50 -51
  128. data/lib/active_record/readonly_attributes.rb +0 -1
  129. data/lib/active_record/reflection.rb +273 -114
  130. data/lib/active_record/relation.rb +57 -25
  131. data/lib/active_record/relation/batches.rb +0 -2
  132. data/lib/active_record/relation/calculations.rb +41 -37
  133. data/lib/active_record/relation/finder_methods.rb +70 -47
  134. data/lib/active_record/relation/merger.rb +39 -29
  135. data/lib/active_record/relation/predicate_builder.rb +16 -8
  136. data/lib/active_record/relation/predicate_builder/array_handler.rb +32 -13
  137. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -5
  138. data/lib/active_record/relation/query_methods.rb +114 -65
  139. data/lib/active_record/relation/spawn_methods.rb +3 -0
  140. data/lib/active_record/result.rb +18 -7
  141. data/lib/active_record/sanitization.rb +12 -2
  142. data/lib/active_record/schema.rb +0 -1
  143. data/lib/active_record/schema_dumper.rb +59 -28
  144. data/lib/active_record/schema_migration.rb +5 -4
  145. data/lib/active_record/scoping/default.rb +6 -4
  146. data/lib/active_record/scoping/named.rb +4 -0
  147. data/lib/active_record/serializers/xml_serializer.rb +3 -7
  148. data/lib/active_record/statement_cache.rb +95 -10
  149. data/lib/active_record/store.rb +5 -5
  150. data/lib/active_record/tasks/database_tasks.rb +61 -6
  151. data/lib/active_record/tasks/mysql_database_tasks.rb +32 -17
  152. data/lib/active_record/tasks/postgresql_database_tasks.rb +20 -9
  153. data/lib/active_record/timestamp.rb +9 -7
  154. data/lib/active_record/transactions.rb +53 -27
  155. data/lib/active_record/type.rb +23 -0
  156. data/lib/active_record/type/big_integer.rb +13 -0
  157. data/lib/active_record/type/binary.rb +50 -0
  158. data/lib/active_record/type/boolean.rb +31 -0
  159. data/lib/active_record/type/date.rb +50 -0
  160. data/lib/active_record/type/date_time.rb +54 -0
  161. data/lib/active_record/type/decimal.rb +64 -0
  162. data/lib/active_record/type/decimal_without_scale.rb +11 -0
  163. data/lib/active_record/type/decorator.rb +14 -0
  164. data/lib/active_record/type/float.rb +19 -0
  165. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  166. data/lib/active_record/type/integer.rb +59 -0
  167. data/lib/active_record/type/mutable.rb +16 -0
  168. data/lib/active_record/type/numeric.rb +36 -0
  169. data/lib/active_record/type/serialized.rb +62 -0
  170. data/lib/active_record/type/string.rb +40 -0
  171. data/lib/active_record/type/text.rb +11 -0
  172. data/lib/active_record/type/time.rb +26 -0
  173. data/lib/active_record/type/time_value.rb +38 -0
  174. data/lib/active_record/type/type_map.rb +64 -0
  175. data/lib/active_record/type/unsigned_integer.rb +15 -0
  176. data/lib/active_record/type/value.rb +110 -0
  177. data/lib/active_record/validations.rb +25 -19
  178. data/lib/active_record/validations/associated.rb +5 -3
  179. data/lib/active_record/validations/presence.rb +5 -3
  180. data/lib/active_record/validations/uniqueness.rb +25 -29
  181. data/lib/rails/generators/active_record/migration/migration_generator.rb +8 -4
  182. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +1 -1
  183. data/lib/rails/generators/active_record/model/templates/model.rb +1 -1
  184. metadata +66 -11
  185. data/lib/active_record/connection_adapters/postgresql/cast.rb +0 -168
@@ -15,9 +15,10 @@ module ActiveRecord
15
15
 
16
16
  # Returns the primary key value.
17
17
  def id
18
- return unless self.class.primary_key
19
- sync_with_transaction_state
20
- read_attribute(self.class.primary_key)
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
- return 'id' if base_name.blank?
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
- when :table_name_with_underscore
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?
@@ -122,6 +120,7 @@ module ActiveRecord
122
120
  def primary_key=(value)
123
121
  @primary_key = value && value.to_s
124
122
  @quoted_primary_key = nil
123
+ @attributes_builder = nil
125
124
  end
126
125
  end
127
126
  end
@@ -8,7 +8,7 @@ module ActiveRecord
8
8
  end
9
9
 
10
10
  def query_attribute(attr_name)
11
- value = read_attribute(attr_name) { |n| missing_attribute(n, caller) }
11
+ value = self[attr_name]
12
12
 
13
13
  case value
14
14
  when true then true
@@ -22,12 +22,12 @@ module ActiveRecord
22
22
  # the attribute name. Using a constant means that we do not have
23
23
  # to allocate an object on each call to the attribute method.
24
24
  # Making it frozen means that it doesn't get duped when used to
25
- # key the @attributes_cache in read_attribute.
25
+ # key the @attributes in read_attribute.
26
26
  def method_body(method_name, const_name)
27
27
  <<-EOMETHOD
28
28
  def #{method_name}
29
29
  name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
30
- read_attribute(name) { |n| missing_attribute(n, caller) }
30
+ _read_attribute(name) { |n| missing_attribute(n, caller) }
31
31
  end
32
32
  EOMETHOD
33
33
  end
@@ -35,35 +35,20 @@ module ActiveRecord
35
35
 
36
36
  extend ActiveSupport::Concern
37
37
 
38
- ATTRIBUTE_TYPES_CACHED_BY_DEFAULT = [:datetime, :timestamp, :time, :date]
39
-
40
- included do
41
- class_attribute :attribute_types_cached_by_default, instance_writer: false
42
- self.attribute_types_cached_by_default = ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
43
- end
44
-
45
38
  module ClassMethods
46
- # +cache_attributes+ allows you to declare which converted attribute
47
- # values should be cached. Usually caching only pays off for attributes
48
- # with expensive conversion methods, like time related columns (e.g.
49
- # +created_at+, +updated_at+).
50
- def cache_attributes(*attribute_names)
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
- # Returns the attributes which are cached. By default time related columns
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
- # Returns +true+ if the provided attribute is being cached.
61
- def cache_attribute?(attr_name)
62
- cached_attributes.include?(attr_name)
48
+ def cached_attributes_deprecation_warning(method_name)
49
+ ActiveSupport::Deprecation.warn "Calling `#{method_name}` is no longer necessary. All attributes are cached."
63
50
  end
64
51
 
65
- protected
66
-
67
52
  if Module.methods_transplantable?
68
53
  def define_method_attribute(name)
69
54
  method = ReaderMethodCache[name]
@@ -79,7 +64,7 @@ module ActiveRecord
79
64
  generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
80
65
  def #{temp_method}
81
66
  name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
82
- read_attribute(name) { |n| missing_attribute(n, caller) }
67
+ _read_attribute(name) { |n| missing_attribute(n, caller) }
83
68
  end
84
69
  STR
85
70
 
@@ -89,51 +74,29 @@ module ActiveRecord
89
74
  end
90
75
  end
91
76
  end
92
-
93
- private
94
-
95
- def cacheable_column?(column)
96
- if attribute_types_cached_by_default == ATTRIBUTE_TYPES_CACHED_BY_DEFAULT
97
- ! serialized_attributes.include? column.name
98
- else
99
- attribute_types_cached_by_default.include?(column.type)
100
- end
101
- end
102
77
  end
103
78
 
79
+ ID = 'id'.freeze
80
+
104
81
  # Returns the value of the attribute identified by <tt>attr_name</tt> after
105
82
  # it has been typecast (for example, "2004-12-12" in a date column is cast
106
83
  # to a date object, like Date.new(2004, 12, 12)).
107
- def read_attribute(attr_name)
108
- # If it's cached, just return it
109
- # We use #[] first as a perf optimization for non-nil values. See https://gist.github.com/jonleighton/3552829.
84
+ def read_attribute(attr_name, &block)
110
85
  name = attr_name.to_s
111
- @attributes_cache[name] || @attributes_cache.fetch(name) {
112
- column = @column_types_override[name] if @column_types_override
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
- }
86
+ name = self.class.primary_key if name == ID
87
+ _read_attribute(name, &block)
88
+ end
124
89
 
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
- }
90
+ # This method exists to avoid the expensive primary_key check internally, without
91
+ # breaking compatibility with the read_attribute API
92
+ def _read_attribute(attr_name) # :nodoc:
93
+ @attributes.fetch_value(attr_name.to_s) { |n| yield n if block_given? }
131
94
  end
132
95
 
133
96
  private
134
97
 
135
98
  def attribute(attribute_name)
136
- read_attribute(attribute_name)
99
+ _read_attribute(attribute_name)
137
100
  end
138
101
  end
139
102
  end
@@ -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 or
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
- # merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy
67
- # has its own hash of own serialized attributes
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 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
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
- 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
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
- def initialize(column)
6
- @column = column
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 type_cast(value)
10
- value = @column.type_cast(value)
11
- value.acts_like?(:time) ? value.in_time_zone : value
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 type
15
- @column.type
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
- protected
31
- # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
32
- # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
33
- def define_method_attribute=(attr_name)
34
- if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
35
- method_body, line = <<-EOV, __LINE__ + 1
36
- def #{attr_name}=(time)
37
- time_with_zone = convert_value_to_time_zone("#{attr_name}", time)
38
- previous_time = attribute_changed?("#{attr_name}") ? changed_attributes["#{attr_name}"] : read_attribute(:#{attr_name})
39
- write_attribute(:#{attr_name}, time)
40
- #{attr_name}_will_change! if previous_time != time_with_zone
41
- @attributes_cache["#{attr_name}"] = time_with_zone
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
- private
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 == column.type || :timestamp == column.type)
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