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,4 +1,6 @@
1
- require 'active_support/core_ext/hash/indifferent_access'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/indifferent_access"
2
4
 
3
5
  module ActiveRecord
4
6
  # == Single table inheritance
@@ -19,7 +21,7 @@ module ActiveRecord
19
21
  # Be aware that because the type column is an attribute on the record every new
20
22
  # subclass will instantly be marked as dirty and the type column will be included
21
23
  # in the list of changed attributes on the record. This is different from non
22
- # STI classes:
24
+ # Single Table Inheritance(STI) classes:
23
25
  #
24
26
  # Company.new.changed? # => false
25
27
  # Firm.new.changed? # => true
@@ -30,33 +32,36 @@ module ActiveRecord
30
32
  # for differentiating between them or reloading the right type with find.
31
33
  #
32
34
  # Note, all the attributes for all the cases are kept in the same table. Read more:
33
- # http://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
35
+ # https://www.martinfowler.com/eaaCatalog/singleTableInheritance.html
34
36
  #
35
37
  module Inheritance
36
38
  extend ActiveSupport::Concern
37
39
 
38
40
  included do
39
41
  # Determines whether to store the full constant name including namespace when using STI.
40
- class_attribute :store_full_sti_class, instance_writer: false
41
- self.store_full_sti_class = true
42
+ # This is true, by default.
43
+ class_attribute :store_full_sti_class, instance_writer: false, default: true
42
44
  end
43
45
 
44
46
  module ClassMethods
45
47
  # Determines if one of the attributes passed in is the inheritance column,
46
48
  # and if the inheritance column is attr accessible, it initializes an
47
49
  # instance of the given subclass instead of the base class.
48
- def new(*args, &block)
50
+ def new(attributes = nil, &block)
49
51
  if abstract_class? || self == Base
50
52
  raise NotImplementedError, "#{self} is an abstract class and cannot be instantiated."
51
53
  end
52
54
 
53
- attrs = args.first
54
- if subclass_from_attributes?(attrs)
55
- subclass = subclass_from_attributes(attrs)
55
+ if has_attribute?(inheritance_column)
56
+ subclass = subclass_from_attributes(attributes)
57
+
58
+ if subclass.nil? && base_class == self
59
+ subclass = subclass_from_attributes(column_defaults)
60
+ end
56
61
  end
57
62
 
58
- if subclass
59
- subclass.new(*args, &block)
63
+ if subclass && subclass != self
64
+ subclass.new(attributes, &block)
60
65
  else
61
66
  super
62
67
  end
@@ -79,20 +84,10 @@ module ActiveRecord
79
84
  :true == (@finder_needs_type_condition ||= descends_from_active_record? ? :false : :true)
80
85
  end
81
86
 
82
- def symbolized_base_class
83
- ActiveSupport::Deprecation.warn('`ActiveRecord::Base.symbolized_base_class` is deprecated and will be removed without replacement.')
84
- @symbolized_base_class ||= base_class.to_s.to_sym
85
- end
86
-
87
- def symbolized_sti_name
88
- ActiveSupport::Deprecation.warn('`ActiveRecord::Base.symbolized_sti_name` is deprecated and will be removed without replacement.')
89
- @symbolized_sti_name ||= sti_name.present? ? sti_name.to_sym : symbolized_base_class
90
- end
91
-
92
87
  # Returns the class descending directly from ActiveRecord::Base, or
93
88
  # an abstract class, if any, in the inheritance hierarchy.
94
89
  #
95
- # If A extends AR::Base, A.base_class will return A. If B descends from A
90
+ # If A extends ActiveRecord::Base, A.base_class will return A. If B descends from A
96
91
  # through some arbitrarily deep hierarchy, B.base_class will return A.
97
92
  #
98
93
  # If B < A and C < B and if A is an abstract_class then both B.base_class
@@ -109,21 +104,47 @@ module ActiveRecord
109
104
  end
110
105
  end
111
106
 
112
- # Set this to true if this is an abstract class (see <tt>abstract_class?</tt>).
113
- # If you are using inheritance with ActiveRecord and don't want child classes
114
- # to utilize the implied STI table name of the parent class, this will need to be true.
115
- # For example, given the following:
107
+ # Set this to +true+ if this is an abstract class (see
108
+ # <tt>abstract_class?</tt>).
109
+ # If you are using inheritance with Active Record and don't want a class
110
+ # to be considered as part of the STI hierarchy, you must set this to
111
+ # true.
112
+ # +ApplicationRecord+, for example, is generated as an abstract class.
113
+ #
114
+ # Consider the following default behaviour:
115
+ #
116
+ # Shape = Class.new(ActiveRecord::Base)
117
+ # Polygon = Class.new(Shape)
118
+ # Square = Class.new(Polygon)
116
119
  #
117
- # class SuperClass < ActiveRecord::Base
120
+ # Shape.table_name # => "shapes"
121
+ # Polygon.table_name # => "shapes"
122
+ # Square.table_name # => "shapes"
123
+ # Shape.create! # => #<Shape id: 1, type: nil>
124
+ # Polygon.create! # => #<Polygon id: 2, type: "Polygon">
125
+ # Square.create! # => #<Square id: 3, type: "Square">
126
+ #
127
+ # However, when using <tt>abstract_class</tt>, +Shape+ is omitted from
128
+ # the hierarchy:
129
+ #
130
+ # class Shape < ActiveRecord::Base
118
131
  # self.abstract_class = true
119
132
  # end
120
- # class Child < SuperClass
121
- # self.table_name = 'the_table_i_really_want'
122
- # end
133
+ # Polygon = Class.new(Shape)
134
+ # Square = Class.new(Polygon)
123
135
  #
136
+ # Shape.table_name # => nil
137
+ # Polygon.table_name # => "polygons"
138
+ # Square.table_name # => "polygons"
139
+ # Shape.create! # => NotImplementedError: Shape is an abstract class and cannot be instantiated.
140
+ # Polygon.create! # => #<Polygon id: 1, type: nil>
141
+ # Square.create! # => #<Square id: 2, type: "Square">
124
142
  #
125
- # <tt>self.abstract_class = true</tt> is required to make <tt>Child<.find,.create, or any Arel method></tt> use <tt>the_table_i_really_want</tt> instead of a table called <tt>super_classes</tt>
126
- #
143
+ # Note that in the above example, to disallow the creation of a plain
144
+ # +Polygon+, you should use <tt>validates :type, presence: true</tt>,
145
+ # instead of setting it as an abstract class. This way, +Polygon+ will
146
+ # stay in the hierarchy, and Active Record will continue to correctly
147
+ # derive the table name.
127
148
  attr_accessor :abstract_class
128
149
 
129
150
  # Returns whether this class is an abstract class or not.
@@ -135,89 +156,104 @@ module ActiveRecord
135
156
  store_full_sti_class ? name : name.demodulize
136
157
  end
137
158
 
159
+ def polymorphic_name
160
+ base_class.name
161
+ end
162
+
163
+ def inherited(subclass)
164
+ subclass.instance_variable_set(:@_type_candidates_cache, Concurrent::Map.new)
165
+ super
166
+ end
167
+
138
168
  protected
139
169
 
140
- # Returns the class type of the record using the current module as a prefix. So descendants of
141
- # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
142
- def compute_type(type_name)
143
- if type_name.match(/^::/)
144
- # If the type is prefixed with a scope operator then we assume that
145
- # the type_name is an absolute reference.
146
- ActiveSupport::Dependencies.constantize(type_name)
147
- else
148
- # Build a list of candidates to search for
149
- candidates = []
150
- name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
151
- candidates << type_name
152
-
153
- candidates.each do |candidate|
154
- constant = ActiveSupport::Dependencies.safe_constantize(candidate)
155
- return constant if candidate == constant.to_s
170
+ # Returns the class type of the record using the current module as a prefix. So descendants of
171
+ # MyApp::Business::Account would appear as MyApp::Business::AccountSubclass.
172
+ def compute_type(type_name)
173
+ if type_name.start_with?("::".freeze)
174
+ # If the type is prefixed with a scope operator then we assume that
175
+ # the type_name is an absolute reference.
176
+ ActiveSupport::Dependencies.constantize(type_name)
177
+ else
178
+ type_candidate = @_type_candidates_cache[type_name]
179
+ if type_candidate && type_constant = ActiveSupport::Dependencies.safe_constantize(type_candidate)
180
+ return type_constant
181
+ end
182
+
183
+ # Build a list of candidates to search for
184
+ candidates = []
185
+ name.scan(/::|$/) { candidates.unshift "#{$`}::#{type_name}" }
186
+ candidates << type_name
187
+
188
+ candidates.each do |candidate|
189
+ constant = ActiveSupport::Dependencies.safe_constantize(candidate)
190
+ if candidate == constant.to_s
191
+ @_type_candidates_cache[type_name] = candidate
192
+ return constant
193
+ end
194
+ end
195
+
196
+ raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
156
197
  end
157
-
158
- raise NameError.new("uninitialized constant #{candidates.first}", candidates.first)
159
198
  end
160
- end
161
199
 
162
200
  private
163
201
 
164
- # Called by +instantiate+ to decide which class to use for a new
165
- # record instance. For single-table inheritance, we check the record
166
- # for a +type+ column and return the corresponding class.
167
- def discriminate_class_for_record(record)
168
- if using_single_table_inheritance?(record)
169
- find_sti_class(record[inheritance_column])
170
- else
171
- super
202
+ # Called by +instantiate+ to decide which class to use for a new
203
+ # record instance. For single-table inheritance, we check the record
204
+ # for a +type+ column and return the corresponding class.
205
+ def discriminate_class_for_record(record)
206
+ if using_single_table_inheritance?(record)
207
+ find_sti_class(record[inheritance_column])
208
+ else
209
+ super
210
+ end
172
211
  end
173
- end
174
-
175
- def using_single_table_inheritance?(record)
176
- record[inheritance_column].present? && columns_hash.include?(inheritance_column)
177
- end
178
212
 
179
- def find_sti_class(type_name)
180
- if store_full_sti_class
181
- ActiveSupport::Dependencies.constantize(type_name)
182
- else
183
- compute_type(type_name)
213
+ def using_single_table_inheritance?(record)
214
+ record[inheritance_column].present? && has_attribute?(inheritance_column)
184
215
  end
185
- rescue NameError
186
- raise SubclassNotFound,
187
- "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " +
188
- "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " +
189
- "Please rename this column if you didn't intend it to be used for storing the inheritance class " +
190
- "or overwrite #{name}.inheritance_column to use another column for that information."
191
- end
192
-
193
- def type_condition(table = arel_table)
194
- sti_column = table[inheritance_column]
195
- sti_names = ([self] + descendants).map { |model| model.sti_name }
196
216
 
197
- sti_column.in(sti_names)
198
- end
217
+ def find_sti_class(type_name)
218
+ type_name = base_class.type_for_attribute(inheritance_column).cast(type_name)
219
+ subclass = begin
220
+ if store_full_sti_class
221
+ ActiveSupport::Dependencies.constantize(type_name)
222
+ else
223
+ compute_type(type_name)
224
+ end
225
+ rescue NameError
226
+ raise SubclassNotFound,
227
+ "The single-table inheritance mechanism failed to locate the subclass: '#{type_name}'. " \
228
+ "This error is raised because the column '#{inheritance_column}' is reserved for storing the class in case of inheritance. " \
229
+ "Please rename this column if you didn't intend it to be used for storing the inheritance class " \
230
+ "or overwrite #{name}.inheritance_column to use another column for that information."
231
+ end
232
+ unless subclass == self || descendants.include?(subclass)
233
+ raise SubclassNotFound, "Invalid single-table inheritance type: #{subclass.name} is not a subclass of #{name}"
234
+ end
235
+ subclass
236
+ end
199
237
 
200
- # Detect the subclass from the inheritance column of attrs. If the inheritance column value
201
- # is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
202
- # If this is a StrongParameters hash, and access to inheritance_column is not permitted,
203
- # this will ignore the inheritance column and return nil
204
- def subclass_from_attributes?(attrs)
205
- columns_hash.include?(inheritance_column) && attrs.is_a?(Hash)
206
- end
238
+ def type_condition(table = arel_table)
239
+ sti_column = arel_attribute(inheritance_column, table)
240
+ sti_names = ([self] + descendants).map(&:sti_name)
207
241
 
208
- def subclass_from_attributes(attrs)
209
- subclass_name = attrs.with_indifferent_access[inheritance_column]
242
+ sti_column.in(sti_names)
243
+ end
210
244
 
211
- if subclass_name.present? && subclass_name != self.name
212
- subclass = subclass_name.safe_constantize
245
+ # Detect the subclass from the inheritance column of attrs. If the inheritance column value
246
+ # is not self or a valid subclass, raises ActiveRecord::SubclassNotFound
247
+ def subclass_from_attributes(attrs)
248
+ attrs = attrs.to_h if attrs.respond_to?(:permitted?)
249
+ if attrs.is_a?(Hash)
250
+ subclass_name = attrs[inheritance_column] || attrs[inheritance_column.to_sym]
213
251
 
214
- unless descendants.include?(subclass)
215
- raise ActiveRecord::SubclassNotFound.new("Invalid single-table inheritance type: #{subclass_name} is not a subclass of #{name}")
252
+ if subclass_name.present?
253
+ find_sti_class(subclass_name)
254
+ end
216
255
  end
217
-
218
- subclass
219
256
  end
220
- end
221
257
  end
222
258
 
223
259
  def initialize_dup(other)
@@ -227,21 +263,21 @@ module ActiveRecord
227
263
 
228
264
  private
229
265
 
230
- def initialize_internals_callback
231
- super
232
- ensure_proper_type
233
- end
266
+ def initialize_internals_callback
267
+ super
268
+ ensure_proper_type
269
+ end
234
270
 
235
- # Sets the attribute used for single table inheritance to this class name if this is not the
236
- # ActiveRecord::Base descendant.
237
- # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
238
- # do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
239
- # No such attribute would be set for objects of the Message class in that example.
240
- def ensure_proper_type
241
- klass = self.class
242
- if klass.finder_needs_type_condition?
243
- write_attribute(klass.inheritance_column, klass.sti_name)
271
+ # Sets the attribute used for single table inheritance to this class name if this is not the
272
+ # ActiveRecord::Base descendant.
273
+ # Considering the hierarchy Reply < Message < ActiveRecord::Base, this makes it possible to
274
+ # do Reply.new without having to set <tt>Reply[Reply.inheritance_column] = "Reply"</tt> yourself.
275
+ # No such attribute would be set for objects of the Message class in that example.
276
+ def ensure_proper_type
277
+ klass = self.class
278
+ if klass.finder_needs_type_condition?
279
+ _write_attribute(klass.inheritance_column, klass.sti_name)
280
+ end
244
281
  end
245
- end
246
282
  end
247
283
  end
@@ -1,4 +1,6 @@
1
- require 'active_support/core_ext/string/filters'
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/string/filters"
2
4
 
3
5
  module ActiveRecord
4
6
  module Integration
@@ -7,17 +9,24 @@ module ActiveRecord
7
9
  included do
8
10
  ##
9
11
  # :singleton-method:
10
- # Indicates the format used to generate the timestamp in the cache key.
11
- # Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
12
+ # Indicates the format used to generate the timestamp in the cache key, if
13
+ # versioning is off. Accepts any of the symbols in <tt>Time::DATE_FORMATS</tt>.
14
+ #
15
+ # This is +:usec+, by default.
16
+ class_attribute :cache_timestamp_format, instance_writer: false, default: :usec
17
+
18
+ ##
19
+ # :singleton-method:
20
+ # Indicates whether to use a stable #cache_key method that is accompanied
21
+ # by a changing version in the #cache_version method.
12
22
  #
13
- # This is +:nsec+, by default.
14
- class_attribute :cache_timestamp_format, :instance_writer => false
15
- self.cache_timestamp_format = :nsec
23
+ # This is +false+, by default until Rails 6.0.
24
+ class_attribute :cache_versioning, instance_writer: false, default: false
16
25
  end
17
26
 
18
- # Returns a String, which Action Pack uses for constructing an URL to this
19
- # object. The default implementation returns this record's id as a String,
20
- # or nil if this record's unsaved.
27
+ # Returns a +String+, which Action Pack uses for constructing a URL to this
28
+ # object. The default implementation returns this record's id as a +String+,
29
+ # or +nil+ if this record's unsaved.
21
30
  #
22
31
  # For example, suppose that you have a User model, and that you have a
23
32
  # <tt>resources :users</tt> route. Normally, +user_path+ will
@@ -42,29 +51,62 @@ module ActiveRecord
42
51
  id && id.to_s # Be sure to stringify the id for routes
43
52
  end
44
53
 
45
- # Returns a cache key that can be used to identify this record.
54
+ # Returns a stable cache key that can be used to identify this record.
46
55
  #
47
56
  # Product.new.cache_key # => "products/new"
48
- # Product.find(5).cache_key # => "products/5" (updated_at not available)
49
- # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
57
+ # Product.find(5).cache_key # => "products/5"
50
58
  #
51
- # You can also pass a list of named timestamps, and the newest in the list will be
52
- # used to generate the key:
59
+ # If ActiveRecord::Base.cache_versioning is turned off, as it was in Rails 5.1 and earlier,
60
+ # the cache key will also include a version.
53
61
  #
54
- # Person.find(5).cache_key(:updated_at, :last_reviewed_at)
62
+ # Product.cache_versioning = false
63
+ # Person.find(5).cache_key # => "people/5-20071224150000" (updated_at available)
55
64
  def cache_key(*timestamp_names)
56
- case
57
- when new_record?
65
+ if new_record?
58
66
  "#{model_name.cache_key}/new"
59
- when timestamp_names.any?
60
- timestamp = max_updated_column_timestamp(timestamp_names)
61
- timestamp = timestamp.utc.to_s(cache_timestamp_format)
62
- "#{model_name.cache_key}/#{id}-#{timestamp}"
63
- when timestamp = max_updated_column_timestamp
64
- timestamp = timestamp.utc.to_s(cache_timestamp_format)
65
- "#{model_name.cache_key}/#{id}-#{timestamp}"
66
67
  else
67
- "#{model_name.cache_key}/#{id}"
68
+ if cache_version && timestamp_names.none?
69
+ "#{model_name.cache_key}/#{id}"
70
+ else
71
+ timestamp = if timestamp_names.any?
72
+ ActiveSupport::Deprecation.warn(<<-MSG.squish)
73
+ Specifying a timestamp name for #cache_key has been deprecated in favor of
74
+ the explicit #cache_version method that can be overwritten.
75
+ MSG
76
+
77
+ max_updated_column_timestamp(timestamp_names)
78
+ else
79
+ max_updated_column_timestamp
80
+ end
81
+
82
+ if timestamp
83
+ timestamp = timestamp.utc.to_s(cache_timestamp_format)
84
+ "#{model_name.cache_key}/#{id}-#{timestamp}"
85
+ else
86
+ "#{model_name.cache_key}/#{id}"
87
+ end
88
+ end
89
+ end
90
+ end
91
+
92
+ # Returns a cache version that can be used together with the cache key to form
93
+ # a recyclable caching scheme. By default, the #updated_at column is used for the
94
+ # cache_version, but this method can be overwritten to return something else.
95
+ #
96
+ # Note, this method will return nil if ActiveRecord::Base.cache_versioning is set to
97
+ # +false+ (which it is by default until Rails 6.0).
98
+ def cache_version
99
+ if cache_versioning && timestamp = try(:updated_at)
100
+ timestamp.utc.to_s(:usec)
101
+ end
102
+ end
103
+
104
+ # Returns a cache key along with the version.
105
+ def cache_key_with_version
106
+ if version = cache_version
107
+ "#{cache_key}-#{version}"
108
+ else
109
+ cache_key
68
110
  end
69
111
  end
70
112
 
@@ -84,9 +126,9 @@ module ActiveRecord
84
126
  # Values longer than 20 characters will be truncated. The value
85
127
  # is truncated word by word.
86
128
  #
87
- # user = User.find_by(name: 'David HeinemeierHansson')
129
+ # user = User.find_by(name: 'David Heinemeier Hansson')
88
130
  # user.id # => 125
89
- # user_path(user) # => "/users/125-david"
131
+ # user_path(user) # => "/users/125-david-heinemeier"
90
132
  #
91
133
  # Because the generated param begins with the record's +id+, it is
92
134
  # suitable for passing to +find+. In a controller, for example:
@@ -100,7 +142,7 @@ module ActiveRecord
100
142
  define_method :to_param do
101
143
  if (default = super()) &&
102
144
  (result = send(method_name).to_s).present? &&
103
- (param = result.squish.truncate(20, separator: /\s/, omission: nil).parameterize).present?
145
+ (param = result.squish.parameterize.truncate(20, separator: /-/, omission: "")).present?
104
146
  "#{default}-#{param}"
105
147
  else
106
148
  default
@@ -0,0 +1,45 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/scoping/default"
4
+ require "active_record/scoping/named"
5
+
6
+ module ActiveRecord
7
+ # This class is used to create a table that keeps track of values and keys such
8
+ # as which environment migrations were run in.
9
+ class InternalMetadata < ActiveRecord::Base # :nodoc:
10
+ class << self
11
+ def primary_key
12
+ "key"
13
+ end
14
+
15
+ def table_name
16
+ "#{table_name_prefix}#{ActiveRecord::Base.internal_metadata_table_name}#{table_name_suffix}"
17
+ end
18
+
19
+ def []=(key, value)
20
+ find_or_initialize_by(key: key).update_attributes!(value: value)
21
+ end
22
+
23
+ def [](key)
24
+ where(key: key).pluck(:value).first
25
+ end
26
+
27
+ def table_exists?
28
+ connection.table_exists?(table_name)
29
+ end
30
+
31
+ # Creates an internal metadata table with columns +key+ and +value+
32
+ def create_table
33
+ unless table_exists?
34
+ key_options = connection.internal_string_options_for_primary_key
35
+
36
+ connection.create_table(table_name, id: false) do |t|
37
+ t.string :key, key_options
38
+ t.string :value
39
+ t.timestamps
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,48 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module LegacyYamlAdapter
5
+ def self.convert(klass, coder)
6
+ return coder unless coder.is_a?(Psych::Coder)
7
+
8
+ case coder["active_record_yaml_version"]
9
+ when 1, 2 then coder
10
+ else
11
+ if coder["attributes"].is_a?(ActiveModel::AttributeSet)
12
+ Rails420.convert(klass, coder)
13
+ else
14
+ Rails41.convert(klass, coder)
15
+ end
16
+ end
17
+ end
18
+
19
+ module Rails420
20
+ def self.convert(klass, coder)
21
+ attribute_set = coder["attributes"]
22
+
23
+ klass.attribute_names.each do |attr_name|
24
+ attribute = attribute_set[attr_name]
25
+ if attribute.type.is_a?(Delegator)
26
+ type_from_klass = klass.type_for_attribute(attr_name)
27
+ attribute_set[attr_name] = attribute.with_type(type_from_klass)
28
+ end
29
+ end
30
+
31
+ coder
32
+ end
33
+ end
34
+
35
+ module Rails41
36
+ def self.convert(klass, coder)
37
+ attributes = klass.attributes_builder
38
+ .build_from_database(coder["attributes"])
39
+ new_record = coder["attributes"][klass.primary_key].blank?
40
+
41
+ {
42
+ "attributes" => attributes,
43
+ "new_record" => new_record,
44
+ }
45
+ end
46
+ end
47
+ end
48
+ end
@@ -7,6 +7,7 @@ en:
7
7
  # Default error messages
8
8
  errors:
9
9
  messages:
10
+ required: "must exist"
10
11
  taken: "has already been taken"
11
12
 
12
13
  # Active Record models configuration
@@ -15,8 +16,8 @@ en:
15
16
  messages:
16
17
  record_invalid: "Validation failed: %{errors}"
17
18
  restrict_dependent_destroy:
18
- one: "Cannot delete record because a dependent %{record} exists"
19
- many: "Cannot delete record because dependent %{record} exist"
19
+ has_one: "Cannot delete record because a dependent %{record} exists"
20
+ has_many: "Cannot delete record because dependent %{record} exist"
20
21
  # Append your own errors here or at the model/attributes scope.
21
22
 
22
23
  # You can define own errors for models or model attributes.