activerecord 4.2.0 → 5.2.8.1

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 (274) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +640 -928
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +10 -11
  5. data/examples/performance.rb +32 -31
  6. data/examples/simple.rb +5 -4
  7. data/lib/active_record/aggregations.rb +264 -247
  8. data/lib/active_record/association_relation.rb +24 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +87 -41
  11. data/lib/active_record/associations/association_scope.rb +106 -132
  12. data/lib/active_record/associations/belongs_to_association.rb +55 -36
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -8
  14. data/lib/active_record/associations/builder/association.rb +29 -38
  15. data/lib/active_record/associations/builder/belongs_to.rb +77 -30
  16. data/lib/active_record/associations/builder/collection_association.rb +14 -23
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +50 -39
  18. data/lib/active_record/associations/builder/has_many.rb +6 -4
  19. data/lib/active_record/associations/builder/has_one.rb +13 -6
  20. data/lib/active_record/associations/builder/singular_association.rb +15 -11
  21. data/lib/active_record/associations/collection_association.rb +145 -266
  22. data/lib/active_record/associations/collection_proxy.rb +242 -138
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +35 -75
  25. data/lib/active_record/associations/has_many_through_association.rb +51 -69
  26. data/lib/active_record/associations/has_one_association.rb +39 -24
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +40 -81
  29. data/lib/active_record/associations/join_dependency/join_base.rb +10 -9
  30. data/lib/active_record/associations/join_dependency/join_part.rb +12 -12
  31. data/lib/active_record/associations/join_dependency.rb +134 -154
  32. data/lib/active_record/associations/preloader/association.rb +85 -116
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +83 -93
  35. data/lib/active_record/associations/singular_association.rb +27 -40
  36. data/lib/active_record/associations/through_association.rb +48 -23
  37. data/lib/active_record/associations.rb +1732 -1596
  38. data/lib/active_record/attribute_assignment.rb +58 -182
  39. data/lib/active_record/attribute_decorators.rb +39 -15
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +12 -5
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -125
  42. data/lib/active_record/attribute_methods/primary_key.rb +86 -71
  43. data/lib/active_record/attribute_methods/query.rb +4 -2
  44. data/lib/active_record/attribute_methods/read.rb +45 -63
  45. data/lib/active_record/attribute_methods/serialization.rb +40 -20
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +62 -36
  47. data/lib/active_record/attribute_methods/write.rb +31 -46
  48. data/lib/active_record/attribute_methods.rb +170 -117
  49. data/lib/active_record/attributes.rb +201 -74
  50. data/lib/active_record/autosave_association.rb +118 -45
  51. data/lib/active_record/base.rb +60 -48
  52. data/lib/active_record/callbacks.rb +97 -57
  53. data/lib/active_record/coders/json.rb +3 -1
  54. data/lib/active_record/coders/yaml_column.rb +37 -13
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +254 -87
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +72 -22
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +119 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +6 -4
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +328 -217
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +81 -36
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +617 -212
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +139 -75
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +332 -191
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +567 -563
  69. data/lib/active_record/connection_adapters/column.rb +50 -41
  70. data/lib/active_record/connection_adapters/connection_specification.rb +147 -135
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +42 -195
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +46 -115
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +50 -57
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +10 -6
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +2 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +5 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +5 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +13 -1
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +9 -13
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +7 -3
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +31 -19
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +2 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +3 -11
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +7 -9
  99. data/lib/active_record/connection_adapters/postgresql/oid/{integer.rb → oid.rb} +6 -2
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +33 -11
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +52 -34
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +4 -1
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +65 -51
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +3 -1
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +3 -1
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +23 -25
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +107 -47
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +27 -14
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +144 -90
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +466 -280
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +12 -8
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +439 -330
  117. data/lib/active_record/connection_adapters/schema_cache.rb +48 -24
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +269 -324
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +40 -27
  128. data/lib/active_record/core.rb +205 -202
  129. data/lib/active_record/counter_cache.rb +80 -37
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +87 -105
  132. data/lib/active_record/enum.rb +136 -90
  133. data/lib/active_record/errors.rb +180 -52
  134. data/lib/active_record/explain.rb +23 -11
  135. data/lib/active_record/explain_registry.rb +4 -2
  136. data/lib/active_record/explain_subscriber.rb +11 -6
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +193 -135
  139. data/lib/active_record/gem_version.rb +5 -3
  140. data/lib/active_record/inheritance.rb +148 -112
  141. data/lib/active_record/integration.rb +70 -28
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +92 -98
  146. data/lib/active_record/locking/pessimistic.rb +15 -3
  147. data/lib/active_record/log_subscriber.rb +95 -33
  148. data/lib/active_record/migration/command_recorder.rb +133 -90
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +8 -6
  151. data/lib/active_record/migration.rb +594 -267
  152. data/lib/active_record/model_schema.rb +292 -111
  153. data/lib/active_record/nested_attributes.rb +266 -214
  154. data/lib/active_record/no_touching.rb +8 -2
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +350 -119
  157. data/lib/active_record/query_cache.rb +13 -24
  158. data/lib/active_record/querying.rb +19 -17
  159. data/lib/active_record/railtie.rb +117 -35
  160. data/lib/active_record/railties/console_sandbox.rb +2 -0
  161. data/lib/active_record/railties/controller_runtime.rb +9 -3
  162. data/lib/active_record/railties/databases.rake +160 -174
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +447 -288
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +204 -55
  167. data/lib/active_record/relation/calculations.rb +259 -244
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +290 -253
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +91 -68
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -23
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +7 -1
  179. data/lib/active_record/relation/predicate_builder.rb +118 -92
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +446 -389
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +18 -16
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +287 -339
  187. data/lib/active_record/result.rb +54 -36
  188. data/lib/active_record/runtime_registry.rb +6 -4
  189. data/lib/active_record/sanitization.rb +155 -124
  190. data/lib/active_record/schema.rb +30 -24
  191. data/lib/active_record/schema_dumper.rb +91 -87
  192. data/lib/active_record/schema_migration.rb +19 -19
  193. data/lib/active_record/scoping/default.rb +102 -84
  194. data/lib/active_record/scoping/named.rb +81 -32
  195. data/lib/active_record/scoping.rb +45 -26
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +5 -5
  198. data/lib/active_record/statement_cache.rb +45 -35
  199. data/lib/active_record/store.rb +42 -36
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +136 -95
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +59 -89
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +84 -31
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +44 -16
  206. data/lib/active_record/timestamp.rb +70 -38
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +208 -123
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +4 -41
  212. data/lib/active_record/type/date_time.rb +4 -38
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +13 -5
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +30 -15
  218. data/lib/active_record/type/text.rb +2 -2
  219. data/lib/active_record/type/time.rb +11 -16
  220. data/lib/active_record/type/type_map.rb +15 -17
  221. data/lib/active_record/type/unsigned_integer.rb +9 -7
  222. data/lib/active_record/type.rb +79 -23
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +13 -4
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +14 -13
  230. data/lib/active_record/validations/uniqueness.rb +41 -32
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +36 -21
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +43 -35
  237. data/lib/rails/generators/active_record/migration/templates/{create_table_migration.rb → create_table_migration.rb.tt} +8 -6
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -7
  239. data/lib/rails/generators/active_record/migration.rb +18 -1
  240. data/lib/rails/generators/active_record/model/model_generator.rb +18 -22
  241. data/lib/rails/generators/active_record/model/templates/{model.rb → model.rb.tt} +3 -0
  242. data/lib/rails/generators/active_record.rb +7 -5
  243. metadata +77 -53
  244. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  245. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  246. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  247. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  248. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  249. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  250. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  251. data/lib/active_record/attribute.rb +0 -149
  252. data/lib/active_record/attribute_set/builder.rb +0 -86
  253. data/lib/active_record/attribute_set.rb +0 -77
  254. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  255. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  256. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  257. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  258. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  259. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  260. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  261. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  262. data/lib/active_record/type/big_integer.rb +0 -13
  263. data/lib/active_record/type/binary.rb +0 -50
  264. data/lib/active_record/type/boolean.rb +0 -30
  265. data/lib/active_record/type/decimal.rb +0 -40
  266. data/lib/active_record/type/decorator.rb +0 -14
  267. data/lib/active_record/type/float.rb +0 -19
  268. data/lib/active_record/type/integer.rb +0 -55
  269. data/lib/active_record/type/mutable.rb +0 -16
  270. data/lib/active_record/type/numeric.rb +0 -36
  271. data/lib/active_record/type/string.rb +0 -36
  272. data/lib/active_record/type/time_value.rb +0 -38
  273. data/lib/active_record/type/value.rb +0 -101
  274. /data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +0 -0
@@ -1,30 +1,31 @@
1
- require 'set'
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
2
4
 
3
5
  module ActiveRecord
4
6
  module AttributeMethods
5
7
  module PrimaryKey
6
8
  extend ActiveSupport::Concern
7
9
 
8
- # Returns this record's primary key value wrapped in an Array if one is
10
+ # Returns this record's primary key value wrapped in an array if one is
9
11
  # available.
10
12
  def to_key
11
- sync_with_transaction_state
12
- key = self.id
13
+ key = id
13
14
  [key] if key
14
15
  end
15
16
 
16
17
  # Returns the primary key value.
17
18
  def id
18
- if pk = self.class.primary_key
19
- sync_with_transaction_state
20
- _read_attribute(pk)
21
- end
19
+ sync_with_transaction_state
20
+ primary_key = self.class.primary_key
21
+ _read_attribute(primary_key) if primary_key
22
22
  end
23
23
 
24
24
  # Sets the primary key value.
25
25
  def id=(value)
26
26
  sync_with_transaction_state
27
- write_attribute(self.class.primary_key, value) if self.class.primary_key
27
+ primary_key = self.class.primary_key
28
+ _write_attribute(primary_key, value) if primary_key
28
29
  end
29
30
 
30
31
  # Queries the primary key value.
@@ -45,84 +46,98 @@ module ActiveRecord
45
46
  attribute_was(self.class.primary_key)
46
47
  end
47
48
 
48
- protected
49
-
50
- def attribute_method?(attr_name)
51
- attr_name == 'id' || super
49
+ def id_in_database
50
+ sync_with_transaction_state
51
+ attribute_in_database(self.class.primary_key)
52
52
  end
53
53
 
54
- module ClassMethods
55
- def define_method_attribute(attr_name)
56
- super
54
+ private
57
55
 
58
- if attr_name == primary_key && attr_name != 'id'
59
- generated_attribute_methods.send(:alias_method, :id, primary_key)
60
- end
56
+ def attribute_method?(attr_name)
57
+ attr_name == "id" || super
61
58
  end
62
59
 
63
- ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was).to_set
60
+ module ClassMethods
61
+ ID_ATTRIBUTE_METHODS = %w(id id= id? id_before_type_cast id_was id_in_database).to_set
64
62
 
65
- def dangerous_attribute_method?(method_name)
66
- super && !ID_ATTRIBUTE_METHODS.include?(method_name)
67
- end
63
+ def instance_method_already_implemented?(method_name)
64
+ super || primary_key && ID_ATTRIBUTE_METHODS.include?(method_name)
65
+ end
68
66
 
69
- # Defines the primary key field -- can be overridden in subclasses.
70
- # Overwriting will negate any effect of the +primary_key_prefix_type+
71
- # setting, though.
72
- def primary_key
73
- @primary_key = reset_primary_key unless defined? @primary_key
74
- @primary_key
75
- end
67
+ def dangerous_attribute_method?(method_name)
68
+ super && !ID_ATTRIBUTE_METHODS.include?(method_name)
69
+ end
76
70
 
77
- # Returns a quoted version of the primary key name, used to construct
78
- # SQL statements.
79
- def quoted_primary_key
80
- @quoted_primary_key ||= connection.quote_column_name(primary_key)
81
- end
71
+ # Defines the primary key field -- can be overridden in subclasses.
72
+ # Overwriting will negate any effect of the +primary_key_prefix_type+
73
+ # setting, though.
74
+ def primary_key
75
+ @primary_key = reset_primary_key unless defined? @primary_key
76
+ @primary_key
77
+ end
82
78
 
83
- def reset_primary_key #:nodoc:
84
- if self == base_class
85
- self.primary_key = get_primary_key(base_class.name)
86
- else
87
- self.primary_key = base_class.primary_key
79
+ # Returns a quoted version of the primary key name, used to construct
80
+ # SQL statements.
81
+ def quoted_primary_key
82
+ @quoted_primary_key ||= connection.quote_column_name(primary_key)
88
83
  end
89
- end
90
84
 
91
- def get_primary_key(base_name) #:nodoc:
92
- if base_name && primary_key_prefix_type == :table_name
93
- base_name.foreign_key(false)
94
- elsif base_name && primary_key_prefix_type == :table_name_with_underscore
95
- base_name.foreign_key
96
- else
97
- if ActiveRecord::Base != self && table_exists?
98
- connection.schema_cache.primary_keys(table_name)
85
+ def reset_primary_key #:nodoc:
86
+ if self == base_class
87
+ self.primary_key = get_primary_key(base_class.name)
99
88
  else
100
- 'id'
89
+ self.primary_key = base_class.primary_key
101
90
  end
102
91
  end
103
- end
104
92
 
105
- # Sets the name of the primary key column.
106
- #
107
- # class Project < ActiveRecord::Base
108
- # self.primary_key = 'sysid'
109
- # end
110
- #
111
- # You can also define the +primary_key+ method yourself:
112
- #
113
- # class Project < ActiveRecord::Base
114
- # def self.primary_key
115
- # 'foo_' + super
116
- # end
117
- # end
118
- #
119
- # Project.primary_key # => "foo_id"
120
- def primary_key=(value)
121
- @primary_key = value && value.to_s
122
- @quoted_primary_key = nil
123
- @attributes_builder = nil
93
+ def get_primary_key(base_name) #:nodoc:
94
+ if base_name && primary_key_prefix_type == :table_name
95
+ base_name.foreign_key(false)
96
+ elsif base_name && primary_key_prefix_type == :table_name_with_underscore
97
+ base_name.foreign_key
98
+ else
99
+ if ActiveRecord::Base != self && table_exists?
100
+ pk = connection.schema_cache.primary_keys(table_name)
101
+ suppress_composite_primary_key(pk)
102
+ else
103
+ "id"
104
+ end
105
+ end
106
+ end
107
+
108
+ # Sets the name of the primary key column.
109
+ #
110
+ # class Project < ActiveRecord::Base
111
+ # self.primary_key = 'sysid'
112
+ # end
113
+ #
114
+ # You can also define the #primary_key method yourself:
115
+ #
116
+ # class Project < ActiveRecord::Base
117
+ # def self.primary_key
118
+ # 'foo_' + super
119
+ # end
120
+ # end
121
+ #
122
+ # Project.primary_key # => "foo_id"
123
+ def primary_key=(value)
124
+ @primary_key = value && value.to_s
125
+ @quoted_primary_key = nil
126
+ @attributes_builder = nil
127
+ end
128
+
129
+ private
130
+
131
+ def suppress_composite_primary_key(pk)
132
+ return pk unless pk.is_a?(Array)
133
+
134
+ warn <<-WARNING.strip_heredoc
135
+ WARNING: Active Record does not support composite primary key.
136
+
137
+ #{table_name} has composite primary key. Composite primary key is ignored.
138
+ WARNING
139
+ end
124
140
  end
125
- end
126
141
  end
127
142
  end
128
143
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module AttributeMethods
3
5
  module Query
@@ -19,10 +21,10 @@ module ActiveRecord
19
21
  if Numeric === value || value !~ /[^0-9]/
20
22
  !value.to_i.zero?
21
23
  else
22
- return false if ActiveRecord::ConnectionAdapters::Column::FALSE_VALUES.include?(value)
24
+ return false if ActiveModel::Type::Boolean::FALSE_VALUES.include?(value)
23
25
  !value.blank?
24
26
  end
25
- elsif column.number?
27
+ elsif value.respond_to?(:zero?)
26
28
  !value.zero?
27
29
  else
28
30
  !value.blank?
@@ -1,68 +1,41 @@
1
- require 'active_support/core_ext/module/method_transplanting'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
4
  module AttributeMethods
5
5
  module Read
6
- ReaderMethodCache = Class.new(AttributeMethodCache) {
7
- private
8
- # We want to generate the methods via module_eval rather than
9
- # define_method, because define_method is slower on dispatch.
10
- # Evaluating many similar methods may use more memory as the instruction
11
- # sequences are duplicated and cached (in MRI). define_method may
12
- # be slower on dispatch, but if you're careful about the closure
13
- # created, then define_method will consume much less memory.
14
- #
15
- # But sometimes the database might return columns with
16
- # characters that are not allowed in normal method names (like
17
- # 'my_column(omg)'. So to work around this we first define with
18
- # the __temp__ identifier, and then use alias method to rename
19
- # it to what we want.
20
- #
21
- # We are also defining a constant to hold the frozen string of
22
- # the attribute name. Using a constant means that we do not have
23
- # to allocate an object on each call to the attribute method.
24
- # Making it frozen means that it doesn't get duped when used to
25
- # key the @attributes in read_attribute.
26
- def method_body(method_name, const_name)
27
- <<-EOMETHOD
28
- def #{method_name}
29
- name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{const_name}
30
- _read_attribute(name) { |n| missing_attribute(n, caller) }
31
- end
32
- EOMETHOD
33
- end
34
- }.new
35
-
36
6
  extend ActiveSupport::Concern
37
7
 
38
- module ClassMethods
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
44
- end
45
-
46
- protected
47
-
48
- def cached_attributes_deprecation_warning(method_name)
49
- ActiveSupport::Deprecation.warn "Calling `#{method_name}` is no longer necessary. All attributes are cached."
50
- end
8
+ module ClassMethods # :nodoc:
9
+ private
51
10
 
52
- if Module.methods_transplantable?
53
- def define_method_attribute(name)
54
- method = ReaderMethodCache[name]
55
- generated_attribute_methods.module_eval { define_method name, method }
56
- end
57
- else
11
+ # We want to generate the methods via module_eval rather than
12
+ # define_method, because define_method is slower on dispatch.
13
+ # Evaluating many similar methods may use more memory as the instruction
14
+ # sequences are duplicated and cached (in MRI). define_method may
15
+ # be slower on dispatch, but if you're careful about the closure
16
+ # created, then define_method will consume much less memory.
17
+ #
18
+ # But sometimes the database might return columns with
19
+ # characters that are not allowed in normal method names (like
20
+ # 'my_column(omg)'. So to work around this we first define with
21
+ # the __temp__ identifier, and then use alias method to rename
22
+ # it to what we want.
23
+ #
24
+ # We are also defining a constant to hold the frozen string of
25
+ # the attribute name. Using a constant means that we do not have
26
+ # to allocate an object on each call to the attribute method.
27
+ # Making it frozen means that it doesn't get duped when used to
28
+ # key the @attributes in read_attribute.
58
29
  def define_method_attribute(name)
59
- safe_name = name.unpack('h*').first
30
+ safe_name = name.unpack("h*".freeze).first
60
31
  temp_method = "__temp__#{safe_name}"
61
32
 
62
33
  ActiveRecord::AttributeMethods::AttrNames.set_name_cache safe_name, name
34
+ sync_with_transaction_state = "sync_with_transaction_state" if name == primary_key
63
35
 
64
36
  generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ + 1
65
37
  def #{temp_method}
38
+ #{sync_with_transaction_state}
66
39
  name = ::ActiveRecord::AttributeMethods::AttrNames::ATTR_#{safe_name}
67
40
  _read_attribute(name) { |n| missing_attribute(n, caller) }
68
41
  end
@@ -73,31 +46,40 @@ module ActiveRecord
73
46
  undef_method temp_method
74
47
  end
75
48
  end
76
- end
77
49
  end
78
50
 
79
- ID = 'id'.freeze
80
-
81
51
  # Returns the value of the attribute identified by <tt>attr_name</tt> after
82
52
  # it has been typecast (for example, "2004-12-12" in a date column is cast
83
53
  # to a date object, like Date.new(2004, 12, 12)).
84
54
  def read_attribute(attr_name, &block)
85
- name = attr_name.to_s
86
- name = self.class.primary_key if name == ID
55
+ name = if self.class.attribute_alias?(attr_name)
56
+ self.class.attribute_alias(attr_name).to_s
57
+ else
58
+ attr_name.to_s
59
+ end
60
+
61
+ primary_key = self.class.primary_key
62
+ name = primary_key if name == "id".freeze && primary_key
63
+ sync_with_transaction_state if name == primary_key
87
64
  _read_attribute(name, &block)
88
65
  end
89
66
 
90
67
  # This method exists to avoid the expensive primary_key check internally, without
91
68
  # 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? }
69
+ if defined?(JRUBY_VERSION)
70
+ # This form is significantly faster on JRuby, and this is one of our biggest hotspots.
71
+ # https://github.com/jruby/jruby/pull/2562
72
+ def _read_attribute(attr_name, &block) # :nodoc:
73
+ @attributes.fetch_value(attr_name.to_s, &block)
74
+ end
75
+ else
76
+ def _read_attribute(attr_name) # :nodoc:
77
+ @attributes.fetch_value(attr_name.to_s) { |n| yield n if block_given? }
78
+ end
94
79
  end
95
80
 
96
- private
97
-
98
- def attribute(attribute_name)
99
- _read_attribute(attribute_name)
100
- end
81
+ alias :attribute :_read_attribute
82
+ private :attribute
101
83
  end
102
84
  end
103
85
  end
@@ -1,22 +1,44 @@
1
- require 'active_support/core_ext/string/filters'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
4
  module AttributeMethods
5
5
  module Serialization
6
6
  extend ActiveSupport::Concern
7
7
 
8
+ class ColumnNotSerializableError < StandardError
9
+ def initialize(name, type)
10
+ super <<-EOS.strip_heredoc
11
+ Column `#{name}` of type #{type.class} does not support `serialize` feature.
12
+ Usually it means that you are trying to use `serialize`
13
+ on a column that already implements serialization natively.
14
+ EOS
15
+ end
16
+ end
17
+
8
18
  module ClassMethods
9
19
  # If you have an attribute that needs to be saved to the database as an
10
20
  # object, and retrieved as the same object, then specify the name of that
11
21
  # attribute using this method and it will be handled automatically. The
12
22
  # serialization is done through YAML. If +class_name+ is specified, the
13
23
  # serialized object must be of that class on assignment and retrieval.
14
- # Otherwise <tt>SerializationTypeMismatch</tt> will be raised.
24
+ # Otherwise SerializationTypeMismatch will be raised.
25
+ #
26
+ # Empty objects as <tt>{}</tt>, in the case of +Hash+, or <tt>[]</tt>, in the case of
27
+ # +Array+, will always be persisted as null.
28
+ #
29
+ # Keep in mind that database adapters handle certain serialization tasks
30
+ # for you. For instance: +json+ and +jsonb+ types in PostgreSQL will be
31
+ # converted between JSON object/array syntax and Ruby +Hash+ or +Array+
32
+ # objects transparently. There is no need to use #serialize in this
33
+ # case.
34
+ #
35
+ # For more complex cases, such as conversion to or from your application
36
+ # domain objects, consider using the ActiveRecord::Attributes API.
15
37
  #
16
38
  # ==== Parameters
17
39
  #
18
40
  # * +attr_name+ - The field name that should be serialized.
19
- # * +class_name_or_coder+ - Optional, a coder object, which responds to `.load` / `.dump`
41
+ # * +class_name_or_coder+ - Optional, a coder object, which responds to +.load+ and +.dump+
20
42
  # or a class name that the object type should be equal to.
21
43
  #
22
44
  # ==== Example
@@ -40,30 +62,28 @@ module ActiveRecord
40
62
  # to ensure special objects (e.g. Active Record models) are dumped correctly
41
63
  # using the #as_json hook.
42
64
  coder = if class_name_or_coder == ::JSON
43
- Coders::JSON
44
- elsif [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
45
- class_name_or_coder
46
- else
47
- Coders::YAMLColumn.new(class_name_or_coder)
48
- end
65
+ Coders::JSON
66
+ elsif [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
67
+ class_name_or_coder
68
+ else
69
+ Coders::YAMLColumn.new(attr_name, class_name_or_coder)
70
+ end
49
71
 
50
72
  decorate_attribute_type(attr_name, :serialize) do |type|
73
+ if type_incompatible_with_serialize?(type, class_name_or_coder)
74
+ raise ColumnNotSerializableError.new(attr_name, type)
75
+ end
76
+
51
77
  Type::Serialized.new(type, coder)
52
78
  end
53
79
  end
54
80
 
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
81
+ private
60
82
 
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
- ]
66
- end
83
+ def type_incompatible_with_serialize?(type, class_name)
84
+ type.is_a?(ActiveRecord::Type::Json) && class_name == ::JSON ||
85
+ type.respond_to?(:type_cast_array, true) && class_name == ::Array
86
+ end
67
87
  end
68
88
  end
69
89
  end
@@ -1,64 +1,90 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module ActiveRecord
2
4
  module AttributeMethods
3
5
  module TimeZoneConversion
4
6
  class TimeZoneConverter < DelegateClass(Type::Value) # :nodoc:
5
- include Type::Decorator
6
-
7
- def type_cast_from_database(value)
7
+ def deserialize(value)
8
8
  convert_time_to_time_zone(super)
9
9
  end
10
10
 
11
- def type_cast_from_user(value)
12
- if value.is_a?(Array)
13
- value.map { |v| type_cast_from_user(v) }
11
+ def cast(value)
12
+ return if value.nil?
13
+
14
+ if value.is_a?(Hash)
15
+ set_time_zone_without_conversion(super)
14
16
  elsif value.respond_to?(:in_time_zone)
15
- value.in_time_zone || super
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) }
16
24
  end
17
25
  end
18
26
 
19
- def convert_time_to_time_zone(value)
20
- if value.is_a?(Array)
21
- value.map { |v| convert_time_to_time_zone(v) }
22
- elsif value.acts_like?(:time)
23
- value.in_time_zone
24
- else
25
- value
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).try(:in_time_zone) if value
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
26
53
  end
27
- end
28
54
  end
29
55
 
30
56
  extend ActiveSupport::Concern
31
57
 
32
58
  included do
33
- mattr_accessor :time_zone_aware_attributes, instance_writer: false
34
- self.time_zone_aware_attributes = false
59
+ mattr_accessor :time_zone_aware_attributes, instance_writer: false, default: false
35
60
 
36
- class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false
37
- self.skip_time_zone_conversion_for_attributes = []
61
+ class_attribute :skip_time_zone_conversion_for_attributes, instance_writer: false, default: []
62
+ class_attribute :time_zone_aware_types, instance_writer: false, default: [ :datetime, :time ]
38
63
  end
39
64
 
40
- module ClassMethods
65
+ module ClassMethods # :nodoc:
41
66
  private
42
67
 
43
- def inherited(subclass)
44
- # We need to apply this decorator here, rather than on module inclusion. The closure
45
- # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
46
- # sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
47
- # `skip_time_zone_conversion_for_attributes` would not be picked up.
48
- subclass.class_eval do
49
- matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
50
- decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type|
51
- TimeZoneConverter.new(type)
68
+ def inherited(subclass)
69
+ super
70
+ # We need to apply this decorator here, rather than on module inclusion. The closure
71
+ # created by the matcher would otherwise evaluate for `ActiveRecord::Base`, not the
72
+ # sub class being decorated. As such, changes to `time_zone_aware_attributes`, or
73
+ # `skip_time_zone_conversion_for_attributes` would not be picked up.
74
+ subclass.class_eval do
75
+ matcher = ->(name, type) { create_time_zone_conversion_attribute?(name, type) }
76
+ decorate_matching_attribute_types(matcher, :_time_zone_conversion) do |type|
77
+ TimeZoneConverter.new(type)
78
+ end
52
79
  end
53
80
  end
54
- super
55
- end
56
81
 
57
- def create_time_zone_conversion_attribute?(name, cast_type)
58
- time_zone_aware_attributes &&
59
- !self.skip_time_zone_conversion_for_attributes.include?(name.to_sym) &&
60
- (:datetime == cast_type.type)
61
- end
82
+ def create_time_zone_conversion_attribute?(name, cast_type)
83
+ enabled_for_column = time_zone_aware_attributes &&
84
+ !skip_time_zone_conversion_for_attributes.include?(name.to_sym)
85
+
86
+ enabled_for_column && time_zone_aware_types.include?(cast_type.type)
87
+ end
62
88
  end
63
89
  end
64
90
  end