activerecord 3.2.19 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


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

Files changed (264) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1715 -604
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +40 -45
  5. data/examples/performance.rb +33 -22
  6. data/examples/simple.rb +3 -4
  7. data/lib/active_record/aggregations.rb +76 -51
  8. data/lib/active_record/association_relation.rb +35 -0
  9. data/lib/active_record/associations/alias_tracker.rb +54 -40
  10. data/lib/active_record/associations/association.rb +76 -56
  11. data/lib/active_record/associations/association_scope.rb +125 -93
  12. data/lib/active_record/associations/belongs_to_association.rb +57 -28
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +7 -2
  14. data/lib/active_record/associations/builder/association.rb +120 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +115 -62
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -53
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +117 -43
  18. data/lib/active_record/associations/builder/has_many.rb +9 -65
  19. data/lib/active_record/associations/builder/has_one.rb +18 -52
  20. data/lib/active_record/associations/builder/singular_association.rb +18 -19
  21. data/lib/active_record/associations/collection_association.rb +268 -186
  22. data/lib/active_record/associations/collection_proxy.rb +1003 -63
  23. data/lib/active_record/associations/foreign_association.rb +11 -0
  24. data/lib/active_record/associations/has_many_association.rb +81 -41
  25. data/lib/active_record/associations/has_many_through_association.rb +76 -55
  26. data/lib/active_record/associations/has_one_association.rb +51 -21
  27. data/lib/active_record/associations/has_one_through_association.rb +1 -1
  28. data/lib/active_record/associations/join_dependency/join_association.rb +83 -108
  29. data/lib/active_record/associations/join_dependency/join_base.rb +7 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +30 -37
  31. data/lib/active_record/associations/join_dependency.rb +239 -155
  32. data/lib/active_record/associations/preloader/association.rb +97 -62
  33. data/lib/active_record/associations/preloader/collection_association.rb +2 -8
  34. data/lib/active_record/associations/preloader/has_many_through.rb +7 -3
  35. data/lib/active_record/associations/preloader/has_one.rb +0 -8
  36. data/lib/active_record/associations/preloader/singular_association.rb +3 -3
  37. data/lib/active_record/associations/preloader/through_association.rb +75 -33
  38. data/lib/active_record/associations/preloader.rb +111 -79
  39. data/lib/active_record/associations/singular_association.rb +35 -13
  40. data/lib/active_record/associations/through_association.rb +41 -19
  41. data/lib/active_record/associations.rb +727 -501
  42. data/lib/active_record/attribute/user_provided_default.rb +28 -0
  43. data/lib/active_record/attribute.rb +213 -0
  44. data/lib/active_record/attribute_assignment.rb +32 -162
  45. data/lib/active_record/attribute_decorators.rb +67 -0
  46. data/lib/active_record/attribute_methods/before_type_cast.rb +52 -7
  47. data/lib/active_record/attribute_methods/dirty.rb +101 -61
  48. data/lib/active_record/attribute_methods/primary_key.rb +50 -36
  49. data/lib/active_record/attribute_methods/query.rb +7 -6
  50. data/lib/active_record/attribute_methods/read.rb +56 -117
  51. data/lib/active_record/attribute_methods/serialization.rb +43 -96
  52. data/lib/active_record/attribute_methods/time_zone_conversion.rb +93 -42
  53. data/lib/active_record/attribute_methods/write.rb +34 -45
  54. data/lib/active_record/attribute_methods.rb +333 -144
  55. data/lib/active_record/attribute_mutation_tracker.rb +70 -0
  56. data/lib/active_record/attribute_set/builder.rb +108 -0
  57. data/lib/active_record/attribute_set.rb +108 -0
  58. data/lib/active_record/attributes.rb +265 -0
  59. data/lib/active_record/autosave_association.rb +285 -223
  60. data/lib/active_record/base.rb +95 -490
  61. data/lib/active_record/callbacks.rb +95 -61
  62. data/lib/active_record/coders/json.rb +13 -0
  63. data/lib/active_record/coders/yaml_column.rb +28 -19
  64. data/lib/active_record/collection_cache_key.rb +40 -0
  65. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +724 -277
  66. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  67. data/lib/active_record/connection_adapters/abstract/database_statements.rb +199 -192
  68. data/lib/active_record/connection_adapters/abstract/query_cache.rb +31 -26
  69. data/lib/active_record/connection_adapters/abstract/quoting.rb +140 -57
  70. data/lib/active_record/connection_adapters/abstract/savepoints.rb +21 -0
  71. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +147 -0
  72. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +419 -276
  73. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +105 -0
  74. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +963 -276
  75. data/lib/active_record/connection_adapters/abstract/transaction.rb +232 -0
  76. data/lib/active_record/connection_adapters/abstract_adapter.rb +397 -106
  77. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +643 -342
  78. data/lib/active_record/connection_adapters/column.rb +30 -259
  79. data/lib/active_record/connection_adapters/connection_specification.rb +263 -0
  80. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +22 -0
  81. data/lib/active_record/connection_adapters/mysql/column.rb +50 -0
  82. data/lib/active_record/connection_adapters/mysql/database_statements.rb +125 -0
  83. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +70 -0
  84. data/lib/active_record/connection_adapters/mysql/quoting.rb +51 -0
  85. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +67 -0
  86. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +93 -0
  87. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +54 -0
  88. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +32 -0
  89. data/lib/active_record/connection_adapters/mysql2_adapter.rb +47 -196
  90. data/lib/active_record/connection_adapters/postgresql/column.rb +15 -0
  91. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +170 -0
  92. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +42 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +70 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +52 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +13 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +48 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +21 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +13 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +19 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +59 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +13 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +10 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +39 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +43 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid/rails_5_1_point.rb +50 -0
  108. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +93 -0
  109. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +15 -0
  110. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +109 -0
  111. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +21 -0
  112. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +26 -0
  113. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +28 -0
  114. data/lib/active_record/connection_adapters/postgresql/oid.rb +31 -0
  115. data/lib/active_record/connection_adapters/postgresql/quoting.rb +116 -0
  116. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +49 -0
  117. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +180 -0
  118. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +47 -0
  119. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +682 -0
  120. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +35 -0
  121. data/lib/active_record/connection_adapters/postgresql/utils.rb +77 -0
  122. data/lib/active_record/connection_adapters/postgresql_adapter.rb +558 -1039
  123. data/lib/active_record/connection_adapters/schema_cache.rb +74 -36
  124. data/lib/active_record/connection_adapters/sql_type_metadata.rb +32 -0
  125. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +19 -0
  126. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +48 -0
  127. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +22 -0
  128. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +538 -24
  129. data/lib/active_record/connection_adapters/statement_pool.rb +31 -12
  130. data/lib/active_record/connection_handling.rb +155 -0
  131. data/lib/active_record/core.rb +561 -0
  132. data/lib/active_record/counter_cache.rb +146 -105
  133. data/lib/active_record/dynamic_matchers.rb +101 -64
  134. data/lib/active_record/enum.rb +234 -0
  135. data/lib/active_record/errors.rb +153 -56
  136. data/lib/active_record/explain.rb +15 -63
  137. data/lib/active_record/explain_registry.rb +30 -0
  138. data/lib/active_record/explain_subscriber.rb +10 -6
  139. data/lib/active_record/fixture_set/file.rb +77 -0
  140. data/lib/active_record/fixtures.rb +355 -232
  141. data/lib/active_record/gem_version.rb +15 -0
  142. data/lib/active_record/inheritance.rb +144 -79
  143. data/lib/active_record/integration.rb +66 -13
  144. data/lib/active_record/internal_metadata.rb +56 -0
  145. data/lib/active_record/legacy_yaml_adapter.rb +46 -0
  146. data/lib/active_record/locale/en.yml +9 -1
  147. data/lib/active_record/locking/optimistic.rb +77 -56
  148. data/lib/active_record/locking/pessimistic.rb +6 -6
  149. data/lib/active_record/log_subscriber.rb +53 -28
  150. data/lib/active_record/migration/command_recorder.rb +166 -33
  151. data/lib/active_record/migration/compatibility.rb +126 -0
  152. data/lib/active_record/migration/join_table.rb +15 -0
  153. data/lib/active_record/migration.rb +792 -264
  154. data/lib/active_record/model_schema.rb +192 -130
  155. data/lib/active_record/nested_attributes.rb +238 -145
  156. data/lib/active_record/no_touching.rb +52 -0
  157. data/lib/active_record/null_relation.rb +89 -0
  158. data/lib/active_record/persistence.rb +357 -157
  159. data/lib/active_record/query_cache.rb +22 -43
  160. data/lib/active_record/querying.rb +34 -23
  161. data/lib/active_record/railtie.rb +88 -48
  162. data/lib/active_record/railties/console_sandbox.rb +3 -4
  163. data/lib/active_record/railties/controller_runtime.rb +5 -4
  164. data/lib/active_record/railties/databases.rake +170 -422
  165. data/lib/active_record/railties/jdbcmysql_error.rb +1 -1
  166. data/lib/active_record/readonly_attributes.rb +2 -5
  167. data/lib/active_record/reflection.rb +715 -189
  168. data/lib/active_record/relation/batches/batch_enumerator.rb +67 -0
  169. data/lib/active_record/relation/batches.rb +203 -50
  170. data/lib/active_record/relation/calculations.rb +203 -194
  171. data/lib/active_record/relation/delegation.rb +103 -25
  172. data/lib/active_record/relation/finder_methods.rb +457 -261
  173. data/lib/active_record/relation/from_clause.rb +32 -0
  174. data/lib/active_record/relation/merger.rb +167 -0
  175. data/lib/active_record/relation/predicate_builder/array_handler.rb +43 -0
  176. data/lib/active_record/relation/predicate_builder/association_query_handler.rb +88 -0
  177. data/lib/active_record/relation/predicate_builder/base_handler.rb +17 -0
  178. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +17 -0
  179. data/lib/active_record/relation/predicate_builder/class_handler.rb +27 -0
  180. data/lib/active_record/relation/predicate_builder/polymorphic_array_handler.rb +57 -0
  181. data/lib/active_record/relation/predicate_builder/range_handler.rb +33 -0
  182. data/lib/active_record/relation/predicate_builder/relation_handler.rb +13 -0
  183. data/lib/active_record/relation/predicate_builder.rb +153 -48
  184. data/lib/active_record/relation/query_attribute.rb +19 -0
  185. data/lib/active_record/relation/query_methods.rb +1019 -194
  186. data/lib/active_record/relation/record_fetch_warning.rb +49 -0
  187. data/lib/active_record/relation/spawn_methods.rb +46 -150
  188. data/lib/active_record/relation/where_clause.rb +174 -0
  189. data/lib/active_record/relation/where_clause_factory.rb +38 -0
  190. data/lib/active_record/relation.rb +450 -245
  191. data/lib/active_record/result.rb +104 -12
  192. data/lib/active_record/runtime_registry.rb +22 -0
  193. data/lib/active_record/sanitization.rb +120 -94
  194. data/lib/active_record/schema.rb +28 -18
  195. data/lib/active_record/schema_dumper.rb +141 -74
  196. data/lib/active_record/schema_migration.rb +50 -0
  197. data/lib/active_record/scoping/default.rb +64 -57
  198. data/lib/active_record/scoping/named.rb +93 -108
  199. data/lib/active_record/scoping.rb +73 -121
  200. data/lib/active_record/secure_token.rb +38 -0
  201. data/lib/active_record/serialization.rb +7 -5
  202. data/lib/active_record/statement_cache.rb +113 -0
  203. data/lib/active_record/store.rb +173 -15
  204. data/lib/active_record/suppressor.rb +58 -0
  205. data/lib/active_record/table_metadata.rb +68 -0
  206. data/lib/active_record/tasks/database_tasks.rb +313 -0
  207. data/lib/active_record/tasks/mysql_database_tasks.rb +151 -0
  208. data/lib/active_record/tasks/postgresql_database_tasks.rb +110 -0
  209. data/lib/active_record/tasks/sqlite_database_tasks.rb +59 -0
  210. data/lib/active_record/timestamp.rb +42 -24
  211. data/lib/active_record/touch_later.rb +58 -0
  212. data/lib/active_record/transactions.rb +233 -105
  213. data/lib/active_record/type/adapter_specific_registry.rb +130 -0
  214. data/lib/active_record/type/date.rb +7 -0
  215. data/lib/active_record/type/date_time.rb +7 -0
  216. data/lib/active_record/type/hash_lookup_type_map.rb +23 -0
  217. data/lib/active_record/type/internal/abstract_json.rb +29 -0
  218. data/lib/active_record/type/internal/timezone.rb +15 -0
  219. data/lib/active_record/type/serialized.rb +63 -0
  220. data/lib/active_record/type/time.rb +20 -0
  221. data/lib/active_record/type/type_map.rb +64 -0
  222. data/lib/active_record/type.rb +72 -0
  223. data/lib/active_record/type_caster/connection.rb +29 -0
  224. data/lib/active_record/type_caster/map.rb +19 -0
  225. data/lib/active_record/type_caster.rb +7 -0
  226. data/lib/active_record/validations/absence.rb +23 -0
  227. data/lib/active_record/validations/associated.rb +33 -18
  228. data/lib/active_record/validations/length.rb +24 -0
  229. data/lib/active_record/validations/presence.rb +66 -0
  230. data/lib/active_record/validations/uniqueness.rb +128 -68
  231. data/lib/active_record/validations.rb +48 -40
  232. data/lib/active_record/version.rb +5 -7
  233. data/lib/active_record.rb +71 -47
  234. data/lib/rails/generators/active_record/migration/migration_generator.rb +56 -8
  235. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +24 -0
  236. data/lib/rails/generators/active_record/migration/templates/migration.rb +28 -16
  237. data/lib/rails/generators/active_record/migration.rb +18 -8
  238. data/lib/rails/generators/active_record/model/model_generator.rb +38 -16
  239. data/lib/rails/generators/active_record/model/templates/application_record.rb +5 -0
  240. data/lib/rails/generators/active_record/model/templates/model.rb +7 -6
  241. data/lib/rails/generators/active_record/model/templates/module.rb +1 -1
  242. data/lib/rails/generators/active_record.rb +3 -11
  243. metadata +188 -134
  244. data/examples/associations.png +0 -0
  245. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  246. data/lib/active_record/associations/join_helper.rb +0 -55
  247. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  248. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  249. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  250. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  251. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  252. data/lib/active_record/dynamic_finder_match.rb +0 -68
  253. data/lib/active_record/dynamic_scope_match.rb +0 -23
  254. data/lib/active_record/fixtures/file.rb +0 -65
  255. data/lib/active_record/identity_map.rb +0 -162
  256. data/lib/active_record/observer.rb +0 -121
  257. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  258. data/lib/active_record/session_store.rb +0 -360
  259. data/lib/active_record/test_case.rb +0 -73
  260. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  261. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  262. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  263. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  264. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -3,115 +3,62 @@ 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 serialization as
8
- # keys and their class restriction as values.
9
- class_attribute :serialized_attributes
10
- self.serialized_attributes = {}
11
- end
12
-
13
- class Attribute < Struct.new(:coder, :value, :state)
14
- def unserialized_value
15
- state == :serialized ? unserialize : value
16
- end
17
-
18
- def serialized_value
19
- state == :unserialized ? serialize : value
20
- end
21
-
22
- def unserialize
23
- self.state = :unserialized
24
- self.value = coder.load(value)
25
- end
26
-
27
- def serialize
28
- self.state = :serialized
29
- self.value = coder.dump(value)
30
- end
31
- end
32
-
33
6
  module ClassMethods
34
- # If you have an attribute that needs to be saved to the database as an object, and retrieved as the same object,
35
- # then specify the name of that attribute using this method and it will be handled automatically.
36
- # The serialization is done through YAML. If +class_name+ is specified, the serialized object must be of that
37
- # class on retrieval or SerializationTypeMismatch will be raised.
7
+ # If you have an attribute that needs to be saved to the database as an
8
+ # object, and retrieved as the same object, then specify the name of that
9
+ # attribute using this method and it will be handled automatically. The
10
+ # serialization is done through YAML. If +class_name+ is specified, the
11
+ # serialized object must be of that class on assignment and retrieval.
12
+ # Otherwise SerializationTypeMismatch will be raised.
13
+ #
14
+ # Empty objects as <tt>{}</tt>, in the case of +Hash+, or <tt>[]</tt>, in the case of
15
+ # +Array+, will always be persisted as null.
16
+ #
17
+ # Keep in mind that database adapters handle certain serialization tasks
18
+ # for you. For instance: +json+ and +jsonb+ types in PostgreSQL will be
19
+ # converted between JSON object/array syntax and Ruby +Hash+ or +Array+
20
+ # objects transparently. There is no need to use #serialize in this
21
+ # case.
22
+ #
23
+ # For more complex cases, such as conversion to or from your application
24
+ # domain objects, consider using the ActiveRecord::Attributes API.
38
25
  #
39
26
  # ==== Parameters
40
27
  #
41
28
  # * +attr_name+ - The field name that should be serialized.
42
- # * +class_name+ - Optional, class name that the object type should be equal to.
29
+ # * +class_name_or_coder+ - Optional, a coder object, which responds to `.load` / `.dump`
30
+ # or a class name that the object type should be equal to.
43
31
  #
44
32
  # ==== Example
45
- # # Serialize a preferences attribute
33
+ #
34
+ # # Serialize a preferences attribute.
46
35
  # class User < ActiveRecord::Base
47
36
  # serialize :preferences
48
37
  # end
49
- def serialize(attr_name, class_name = Object)
50
- coder = if [:load, :dump].all? { |x| class_name.respond_to?(x) }
51
- class_name
38
+ #
39
+ # # Serialize preferences using JSON as coder.
40
+ # class User < ActiveRecord::Base
41
+ # serialize :preferences, JSON
42
+ # end
43
+ #
44
+ # # Serialize preferences as Hash using YAML coder.
45
+ # class User < ActiveRecord::Base
46
+ # serialize :preferences, Hash
47
+ # end
48
+ def serialize(attr_name, class_name_or_coder = Object)
49
+ # When ::JSON is used, force it to go through the Active Support JSON encoder
50
+ # to ensure special objects (e.g. Active Record models) are dumped correctly
51
+ # using the #as_json hook.
52
+ coder = if class_name_or_coder == ::JSON
53
+ Coders::JSON
54
+ elsif [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
55
+ class_name_or_coder
52
56
  else
53
- Coders::YAMLColumn.new(class_name)
57
+ Coders::YAMLColumn.new(class_name_or_coder)
54
58
  end
55
59
 
56
- # merge new serialized attribute and create new hash to ensure that each class in inheritance hierarchy
57
- # has its own hash of own serialized attributes
58
- self.serialized_attributes = serialized_attributes.merge(attr_name.to_s => coder)
59
- end
60
-
61
- def initialize_attributes(attributes, options = {}) #:nodoc:
62
- serialized = (options.delete(:serialized) { true }) ? :serialized : :unserialized
63
- super(attributes, options)
64
-
65
- serialized_attributes.each do |key, coder|
66
- if attributes.key?(key)
67
- attributes[key] = Attribute.new(coder, attributes[key], serialized)
68
- end
69
- end
70
-
71
- attributes
72
- end
73
-
74
- private
75
-
76
- def attribute_cast_code(attr_name)
77
- if serialized_attributes.include?(attr_name)
78
- "v.unserialized_value"
79
- else
80
- super
81
- end
82
- end
83
- end
84
-
85
- def type_cast_attribute_for_write(column, value)
86
- if column && coder = self.class.serialized_attributes[column.name]
87
- Attribute.new(coder, value, :unserialized)
88
- else
89
- super
90
- end
91
- end
92
-
93
- def _field_changed?(attr, old, value)
94
- if self.class.serialized_attributes.include?(attr)
95
- old != value
96
- else
97
- super
98
- end
99
- end
100
-
101
- def read_attribute_before_type_cast(attr_name)
102
- if serialized_attributes.include?(attr_name)
103
- super.unserialized_value
104
- else
105
- super
106
- end
107
- end
108
-
109
- def attributes_before_type_cast
110
- super.dup.tap do |attributes|
111
- self.class.serialized_attributes.each_key do |key|
112
- if attributes.key?(key)
113
- attributes[key] = attributes[key].unserialized_value
114
- end
60
+ decorate_attribute_type(attr_name, :serialize) do |type|
61
+ Type::Serialized.new(type, coder)
115
62
  end
116
63
  end
117
64
  end
@@ -1,64 +1,115 @@
1
- require 'active_support/core_ext/class/attribute'
2
- require 'active_support/core_ext/object/inclusion'
1
+ require 'active_support/core_ext/string/strip'
3
2
 
4
3
  module ActiveRecord
5
4
  module AttributeMethods
6
5
  module TimeZoneConversion
6
+ class TimeZoneConverter < DelegateClass(Type::Value) # :nodoc:
7
+ def deserialize(value)
8
+ convert_time_to_time_zone(super)
9
+ end
10
+
11
+ def cast(value)
12
+ return if value.nil?
13
+
14
+ if value.is_a?(Hash)
15
+ set_time_zone_without_conversion(super)
16
+ elsif value.respond_to?(:in_time_zone)
17
+ begin
18
+ super(user_input_in_time_zone(value)) || super
19
+ rescue ArgumentError
20
+ nil
21
+ end
22
+ else
23
+ map_avoiding_infinite_recursion(super) { |v| cast(v) }
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def convert_time_to_time_zone(value)
30
+ return if value.nil?
31
+
32
+ if value.acts_like?(:time)
33
+ value.in_time_zone
34
+ elsif value.is_a?(::Float)
35
+ value
36
+ else
37
+ map_avoiding_infinite_recursion(value) { |v| convert_time_to_time_zone(v) }
38
+ end
39
+ end
40
+
41
+ def set_time_zone_without_conversion(value)
42
+ ::Time.zone.local_to_utc(value).in_time_zone
43
+ end
44
+
45
+ def map_avoiding_infinite_recursion(value)
46
+ map(value) do |v|
47
+ if value.equal?(v)
48
+ nil
49
+ else
50
+ yield(v)
51
+ end
52
+ end
53
+ end
54
+ end
55
+
7
56
  extend ActiveSupport::Concern
8
57
 
9
58
  included do
10
- cattr_accessor :time_zone_aware_attributes, :instance_writer => false
59
+ mattr_accessor :time_zone_aware_attributes, instance_writer: false
11
60
  self.time_zone_aware_attributes = false
12
61
 
13
- class_attribute :skip_time_zone_conversion_for_attributes, :instance_writer => false
62
+ class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false
14
63
  self.skip_time_zone_conversion_for_attributes = []
64
+
65
+ class_attribute :time_zone_aware_types, instance_writer: false
66
+ self.time_zone_aware_types = [:datetime, :not_explicitly_configured]
15
67
  end
16
68
 
17
69
  module ClassMethods
18
- protected
19
- # The enhanced read method automatically converts the UTC time stored in the database to the time
20
- # zone stored in Time.zone.
21
- def attribute_cast_code(attr_name)
22
- column = columns_hash[attr_name]
23
-
24
- if create_time_zone_conversion_attribute?(attr_name, column)
25
- typecast = "v = #{super}"
26
- time_zone_conversion = "v.acts_like?(:time) ? v.in_time_zone : v"
70
+ private
27
71
 
28
- "((#{typecast}) && (#{time_zone_conversion}))"
29
- else
30
- super
72
+ def inherited(subclass)
73
+ # We need to apply this decorator here, rather than on module inclusion. The closure
74
+ # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
75
+ # sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
76
+ # `skip_time_zone_conversion_for_attributes` would not be picked up.
77
+ subclass.class_eval do
78
+ matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
79
+ decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type|
80
+ TimeZoneConverter.new(type)
31
81
  end
32
82
  end
83
+ super
84
+ end
33
85
 
34
- # Defined for all +datetime+ and +timestamp+ attributes when +time_zone_aware_attributes+ are enabled.
35
- # This enhanced write method will automatically convert the time passed to it to the zone stored in Time.zone.
36
- def define_method_attribute=(attr_name)
37
- if create_time_zone_conversion_attribute?(attr_name, columns_hash[attr_name])
38
- method_body, line = <<-EOV, __LINE__ + 1
39
- def #{attr_name}=(original_time)
40
- original_time = nil if original_time.blank?
41
- time = original_time
42
- unless time.acts_like?(:time)
43
- time = time.is_a?(String) ? Time.zone.parse(time) : time.to_time rescue time
44
- end
45
- time = time.in_time_zone rescue nil if time
46
- previous_time = attribute_changed?("#{attr_name}") ? changed_attributes["#{attr_name}"] : read_attribute(:#{attr_name})
47
- write_attribute(:#{attr_name}, original_time)
48
- #{attr_name}_will_change! if previous_time != time
49
- @attributes_cache["#{attr_name}"] = time
50
- end
51
- EOV
52
- generated_attribute_methods.module_eval(method_body, __FILE__, line)
53
- else
54
- super
55
- end
56
- end
86
+ def create_time_zone_conversion_attribute?(name, cast_type)
87
+ enabled_for_column = time_zone_aware_attributes &&
88
+ !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym)
89
+ result = enabled_for_column &&
90
+ time_zone_aware_types.include?(cast_type.type)
57
91
 
58
- private
59
- def create_time_zone_conversion_attribute?(name, column)
60
- time_zone_aware_attributes && !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) && column.type.in?([:datetime, :timestamp])
92
+ if enabled_for_column &&
93
+ !result &&
94
+ cast_type.type == :time &&
95
+ time_zone_aware_types.include?(:not_explicitly_configured)
96
+ ActiveSupport::Deprecation.warn(<<-MESSAGE.strip_heredoc)
97
+ Time columns will become time zone aware in Rails 5.1. This
98
+ still causes `String`s to be parsed as if they were in `Time.zone`,
99
+ and `Time`s to be converted to `Time.zone`.
100
+
101
+ To keep the old behavior, you must add the following to your initializer:
102
+
103
+ config.active_record.time_zone_aware_types = [:datetime]
104
+
105
+ To silence this deprecation warning, add the following:
106
+
107
+ config.active_record.time_zone_aware_types = [:datetime, :time]
108
+ MESSAGE
61
109
  end
110
+
111
+ result
112
+ end
62
113
  end
63
114
  end
64
115
  end
@@ -9,62 +9,51 @@ module ActiveRecord
9
9
 
10
10
  module ClassMethods
11
11
  protected
12
- def define_method_attribute=(attr_name)
13
- if attr_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP
14
- generated_attribute_methods.module_eval("def #{attr_name}=(new_value); write_attribute('#{attr_name}', new_value); end", __FILE__, __LINE__)
15
- else
16
- generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value|
17
- write_attribute(attr_name, new_value)
18
- end
12
+
13
+ def define_method_attribute=(name)
14
+ safe_name = name.unpack('h*'.freeze).first
15
+ ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
16
+
17
+ generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
18
+ def __temp__#{safe_name}=(value)
19
+ name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
20
+ write_attribute(name, value)
19
21
  end
20
- end
22
+ alias_method #{(name + '=').inspect}, :__temp__#{safe_name}=
23
+ undef_method :__temp__#{safe_name}=
24
+ STR
25
+ end
21
26
  end
22
27
 
23
- # Updates the attribute identified by <tt>attr_name</tt> with the specified +value+. Empty strings
24
- # for fixnum and float columns are turned into +nil+.
28
+ # Updates the attribute identified by <tt>attr_name</tt> with the
29
+ # specified +value+. Empty strings for Integer and Float columns are
30
+ # turned into +nil+.
25
31
  def write_attribute(attr_name, value)
26
- attr_name = attr_name.to_s
27
- attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
28
- @attributes_cache.delete(attr_name)
29
- column = column_for_attribute(attr_name)
30
-
31
- unless column || @attributes.has_key?(attr_name)
32
- ActiveSupport::Deprecation.warn(
33
- "You're trying to create an attribute `#{attr_name}'. Writing arbitrary " \
34
- "attributes on a model is deprecated. Please just use `attr_writer` etc."
35
- )
36
- end
32
+ write_attribute_with_type_cast(attr_name, value, true)
33
+ end
37
34
 
38
- @attributes[attr_name] = type_cast_attribute_for_write(column, value)
35
+ def raw_write_attribute(attr_name, value) # :nodoc:
36
+ write_attribute_with_type_cast(attr_name, value, false)
39
37
  end
40
- alias_method :raw_write_attribute, :write_attribute
41
38
 
42
39
  private
43
- # Handle *= for method_missing.
44
- def attribute=(attribute_name, value)
45
- write_attribute(attribute_name, value)
46
- end
40
+ # Handle *= for method_missing.
41
+ def attribute=(attribute_name, value)
42
+ write_attribute(attribute_name, value)
43
+ end
47
44
 
48
- def type_cast_attribute_for_write(column, value)
49
- if column && column.number?
50
- convert_number_column_value(value)
51
- else
52
- value
53
- end
54
- end
45
+ def write_attribute_with_type_cast(attr_name, value, should_type_cast)
46
+ attr_name = attr_name.to_s
47
+ attr_name = self.class.primary_key if attr_name == 'id' && self.class.primary_key
55
48
 
56
- def convert_number_column_value(value)
57
- case value
58
- when FalseClass
59
- 0
60
- when TrueClass
61
- 1
62
- when String
63
- value.presence
64
- else
65
- value
66
- end
49
+ if should_type_cast
50
+ @attributes.write_from_user(attr_name, value)
51
+ else
52
+ @attributes.write_cast_value(attr_name, value)
67
53
  end
54
+
55
+ value
56
+ end
68
57
  end
69
58
  end
70
59
  end