activerecord 4.2.9 → 5.2.8

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 +614 -1572
  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 +263 -249
  8. data/lib/active_record/association_relation.rb +11 -6
  9. data/lib/active_record/associations/alias_tracker.rb +29 -35
  10. data/lib/active_record/associations/association.rb +77 -43
  11. data/lib/active_record/associations/association_scope.rb +106 -133
  12. data/lib/active_record/associations/belongs_to_association.rb +52 -41
  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 +9 -22
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +42 -35
  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 +139 -280
  22. data/lib/active_record/associations/collection_proxy.rb +231 -133
  23. data/lib/active_record/associations/foreign_association.rb +3 -1
  24. data/lib/active_record/associations/has_many_association.rb +34 -89
  25. data/lib/active_record/associations/has_many_through_association.rb +49 -76
  26. data/lib/active_record/associations/has_one_association.rb +38 -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 -89
  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 +133 -159
  32. data/lib/active_record/associations/preloader/association.rb +85 -120
  33. data/lib/active_record/associations/preloader/through_association.rb +85 -74
  34. data/lib/active_record/associations/preloader.rb +81 -91
  35. data/lib/active_record/associations/singular_association.rb +27 -34
  36. data/lib/active_record/associations/through_association.rb +38 -18
  37. data/lib/active_record/associations.rb +1732 -1597
  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 +10 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +94 -135
  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 +58 -36
  47. data/lib/active_record/attribute_methods/write.rb +30 -45
  48. data/lib/active_record/attribute_methods.rb +166 -109
  49. data/lib/active_record/attributes.rb +201 -82
  50. data/lib/active_record/autosave_association.rb +94 -36
  51. data/lib/active_record/base.rb +57 -44
  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 +24 -12
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +712 -290
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +10 -5
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +237 -90
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +71 -21
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +118 -52
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +5 -3
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +67 -46
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +318 -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 +570 -228
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +138 -70
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +325 -202
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +542 -593
  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 +41 -188
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +35 -11
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +45 -114
  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 -58
  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 +4 -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 -22
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +3 -1
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +5 -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 -5
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +55 -53
  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 +462 -284
  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 +432 -323
  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 -308
  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 +178 -198
  129. data/lib/active_record/counter_cache.rb +79 -36
  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 +135 -88
  133. data/lib/active_record/errors.rb +179 -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 +10 -5
  137. data/lib/active_record/fixture_set/file.rb +35 -9
  138. data/lib/active_record/fixtures.rb +188 -132
  139. data/lib/active_record/gem_version.rb +4 -2
  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 +21 -3
  144. data/lib/active_record/locale/en.yml +3 -2
  145. data/lib/active_record/locking/optimistic.rb +88 -96
  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 +581 -282
  152. data/lib/active_record/model_schema.rb +290 -111
  153. data/lib/active_record/nested_attributes.rb +264 -222
  154. data/lib/active_record/no_touching.rb +7 -1
  155. data/lib/active_record/null_relation.rb +24 -37
  156. data/lib/active_record/persistence.rb +347 -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 +94 -32
  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 +149 -156
  163. data/lib/active_record/readonly_attributes.rb +5 -4
  164. data/lib/active_record/reflection.rb +414 -267
  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 +256 -248
  168. data/lib/active_record/relation/delegation.rb +67 -60
  169. data/lib/active_record/relation/finder_methods.rb +288 -239
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +86 -86
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +24 -24
  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 +116 -119
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +448 -393
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +11 -13
  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 -340
  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 -16
  193. data/lib/active_record/scoping/default.rb +102 -85
  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 +134 -96
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +56 -100
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +83 -41
  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 +199 -124
  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 -45
  212. data/lib/active_record/type/date_time.rb +4 -49
  213. data/lib/active_record/type/decimal_without_scale.rb +6 -2
  214. data/lib/active_record/type/hash_lookup_type_map.rb +5 -3
  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 +24 -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 +40 -41
  231. data/lib/active_record/validations.rb +38 -35
  232. data/lib/active_record/version.rb +3 -1
  233. data/lib/active_record.rb +34 -22
  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 -3
  238. data/lib/rails/generators/active_record/migration/templates/{migration.rb → migration.rb.tt} +8 -1
  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/model/templates/{module.rb → module.rb.tt} +0 -0
  243. data/lib/rails/generators/active_record.rb +7 -5
  244. metadata +72 -50
  245. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  246. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  247. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  248. data/lib/active_record/associations/preloader/has_many_through.rb +0 -19
  249. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  250. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  251. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  252. data/lib/active_record/attribute.rb +0 -163
  253. data/lib/active_record/attribute_set/builder.rb +0 -106
  254. data/lib/active_record/attribute_set.rb +0 -81
  255. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -491
  256. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +0 -93
  257. data/lib/active_record/connection_adapters/postgresql/oid/float.rb +0 -21
  258. data/lib/active_record/connection_adapters/postgresql/oid/infinity.rb +0 -13
  259. data/lib/active_record/connection_adapters/postgresql/oid/json.rb +0 -35
  260. data/lib/active_record/connection_adapters/postgresql/oid/time.rb +0 -11
  261. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  262. data/lib/active_record/serializers/xml_serializer.rb +0 -193
  263. data/lib/active_record/type/big_integer.rb +0 -13
  264. data/lib/active_record/type/binary.rb +0 -50
  265. data/lib/active_record/type/boolean.rb +0 -31
  266. data/lib/active_record/type/decimal.rb +0 -64
  267. data/lib/active_record/type/decorator.rb +0 -14
  268. data/lib/active_record/type/float.rb +0 -19
  269. data/lib/active_record/type/integer.rb +0 -59
  270. data/lib/active_record/type/mutable.rb +0 -16
  271. data/lib/active_record/type/numeric.rb +0 -36
  272. data/lib/active_record/type/string.rb +0 -40
  273. data/lib/active_record/type/time_value.rb +0 -38
  274. data/lib/active_record/type/value.rb +0 -110
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ # ActiveRecord::Suppressor prevents the receiver from being saved during
5
+ # a given block.
6
+ #
7
+ # For example, here's a pattern of creating notifications when new comments
8
+ # are posted. (The notification may in turn trigger an email, a push
9
+ # notification, or just appear in the UI somewhere):
10
+ #
11
+ # class Comment < ActiveRecord::Base
12
+ # belongs_to :commentable, polymorphic: true
13
+ # after_create -> { Notification.create! comment: self,
14
+ # recipients: commentable.recipients }
15
+ # end
16
+ #
17
+ # That's what you want the bulk of the time. New comment creates a new
18
+ # Notification. But there may well be off cases, like copying a commentable
19
+ # and its comments, where you don't want that. So you'd have a concern
20
+ # something like this:
21
+ #
22
+ # module Copyable
23
+ # def copy_to(destination)
24
+ # Notification.suppress do
25
+ # # Copy logic that creates new comments that we do not want
26
+ # # triggering notifications.
27
+ # end
28
+ # end
29
+ # end
30
+ module Suppressor
31
+ extend ActiveSupport::Concern
32
+
33
+ module ClassMethods
34
+ def suppress(&block)
35
+ previous_state = SuppressorRegistry.suppressed[name]
36
+ SuppressorRegistry.suppressed[name] = true
37
+ yield
38
+ ensure
39
+ SuppressorRegistry.suppressed[name] = previous_state
40
+ end
41
+ end
42
+
43
+ def save(*) # :nodoc:
44
+ SuppressorRegistry.suppressed[self.class.name] ? true : super
45
+ end
46
+
47
+ def save!(*) # :nodoc:
48
+ SuppressorRegistry.suppressed[self.class.name] ? true : super
49
+ end
50
+ end
51
+
52
+ class SuppressorRegistry # :nodoc:
53
+ extend ActiveSupport::PerThreadRegistry
54
+
55
+ attr_reader :suppressed
56
+
57
+ def initialize
58
+ @suppressed = {}
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class TableMetadata # :nodoc:
5
+ delegate :foreign_type, :foreign_key, :join_primary_key, :join_foreign_key, to: :association, prefix: true
6
+
7
+ def initialize(klass, arel_table, association = nil)
8
+ @klass = klass
9
+ @arel_table = arel_table
10
+ @association = association
11
+ end
12
+
13
+ def resolve_column_aliases(hash)
14
+ new_hash = hash.dup
15
+ hash.each do |key, _|
16
+ if (key.is_a?(Symbol)) && klass.attribute_alias?(key)
17
+ new_hash[klass.attribute_alias(key)] = new_hash.delete(key)
18
+ end
19
+ end
20
+ new_hash
21
+ end
22
+
23
+ def arel_attribute(column_name)
24
+ if klass
25
+ klass.arel_attribute(column_name, arel_table)
26
+ else
27
+ arel_table[column_name]
28
+ end
29
+ end
30
+
31
+ def type(column_name)
32
+ if klass
33
+ klass.type_for_attribute(column_name)
34
+ else
35
+ Type.default_value
36
+ end
37
+ end
38
+
39
+ def has_column?(column_name)
40
+ klass && klass.columns_hash.key?(column_name.to_s)
41
+ end
42
+
43
+ def associated_with?(association_name)
44
+ klass && klass._reflect_on_association(association_name)
45
+ end
46
+
47
+ def associated_table(table_name)
48
+ association = klass._reflect_on_association(table_name) || klass._reflect_on_association(table_name.to_s.singularize)
49
+
50
+ if !association && table_name == arel_table.name
51
+ return self
52
+ elsif association && !association.polymorphic?
53
+ association_klass = association.klass
54
+ arel_table = association_klass.arel_table.alias(table_name)
55
+ else
56
+ type_caster = TypeCaster::Connection.new(klass, table_name)
57
+ association_klass = nil
58
+ arel_table = Arel::Table.new(table_name, type_caster: type_caster)
59
+ end
60
+
61
+ TableMetadata.new(association_klass, arel_table, association)
62
+ end
63
+
64
+ def polymorphic_association?
65
+ association && association.polymorphic?
66
+ end
67
+
68
+ def aggregated_with?(aggregation_name)
69
+ klass && reflect_on_aggregation(aggregation_name)
70
+ end
71
+
72
+ def reflect_on_aggregation(aggregation_name)
73
+ klass.reflect_on_aggregation(aggregation_name)
74
+ end
75
+
76
+ # TODO Change this to private once we've dropped Ruby 2.2 support.
77
+ # Workaround for Ruby 2.2 "private attribute?" warning.
78
+ protected
79
+
80
+ attr_reader :klass, :arel_table, :association
81
+ end
82
+ end
@@ -1,11 +1,11 @@
1
- require 'active_support/core_ext/string/filters'
1
+ # frozen_string_literal: true
2
2
 
3
3
  module ActiveRecord
4
4
  module Tasks # :nodoc:
5
5
  class DatabaseAlreadyExists < StandardError; end # :nodoc:
6
6
  class DatabaseNotSupported < StandardError; end # :nodoc:
7
7
 
8
- # <tt>ActiveRecord::Tasks::DatabaseTasks</tt> is a utility class, which encapsulates
8
+ # ActiveRecord::Tasks::DatabaseTasks is a utility class, which encapsulates
9
9
  # logic behind common tasks used to manage database and migrations.
10
10
  #
11
11
  # The tasks defined here are used with Rake tasks provided by Active Record.
@@ -18,15 +18,15 @@ module ActiveRecord
18
18
  #
19
19
  # The possible config values are:
20
20
  #
21
- # * +env+: current environment (like Rails.env).
22
- # * +database_configuration+: configuration of your databases (as in +config/database.yml+).
23
- # * +db_dir+: your +db+ directory.
24
- # * +fixtures_path+: a path to fixtures directory.
25
- # * +migrations_paths+: a list of paths to directories with migrations.
26
- # * +seed_loader+: an object which will load seeds, it needs to respond to the +load_seed+ method.
27
- # * +root+: a path to the root of the application.
21
+ # * +env+: current environment (like Rails.env).
22
+ # * +database_configuration+: configuration of your databases (as in +config/database.yml+).
23
+ # * +db_dir+: your +db+ directory.
24
+ # * +fixtures_path+: a path to fixtures directory.
25
+ # * +migrations_paths+: a list of paths to directories with migrations.
26
+ # * +seed_loader+: an object which will load seeds, it needs to respond to the +load_seed+ method.
27
+ # * +root+: a path to the root of the application.
28
28
  #
29
- # Example usage of +DatabaseTasks+ outside Rails could look as such:
29
+ # Example usage of DatabaseTasks outside Rails could look as such:
30
30
  #
31
31
  # include ActiveRecord::Tasks
32
32
  # DatabaseTasks.database_configuration = YAML.load_file('my_database_config.yml')
@@ -35,36 +35,62 @@ module ActiveRecord
35
35
  #
36
36
  # DatabaseTasks.create_current('production')
37
37
  module DatabaseTasks
38
+ ##
39
+ # :singleton-method:
40
+ # Extra flags passed to database CLI tool (mysqldump/pg_dump) when calling db:structure:dump
41
+ mattr_accessor :structure_dump_flags, instance_accessor: false
42
+
43
+ ##
44
+ # :singleton-method:
45
+ # Extra flags passed to database CLI tool when calling db:structure:load
46
+ mattr_accessor :structure_load_flags, instance_accessor: false
47
+
38
48
  extend self
39
49
 
40
50
  attr_writer :current_config, :db_dir, :migrations_paths, :fixtures_path, :root, :env, :seed_loader
41
51
  attr_accessor :database_configuration
42
52
 
43
- LOCAL_HOSTS = ['127.0.0.1', 'localhost']
53
+ LOCAL_HOSTS = ["127.0.0.1", "localhost"]
54
+
55
+ def check_protected_environments!
56
+ unless ENV["DISABLE_DATABASE_ENVIRONMENT_CHECK"]
57
+ current = ActiveRecord::Base.connection.migration_context.current_environment
58
+ stored = ActiveRecord::Base.connection.migration_context.last_stored_environment
59
+
60
+ if ActiveRecord::Base.connection.migration_context.protected_environment?
61
+ raise ActiveRecord::ProtectedEnvironmentError.new(stored)
62
+ end
63
+
64
+ if stored && stored != current
65
+ raise ActiveRecord::EnvironmentMismatchError.new(current: current, stored: stored)
66
+ end
67
+ end
68
+ rescue ActiveRecord::NoDatabaseError
69
+ end
44
70
 
45
71
  def register_task(pattern, task)
46
72
  @tasks ||= {}
47
73
  @tasks[pattern] = task
48
74
  end
49
75
 
50
- register_task(/mysql/, ActiveRecord::Tasks::MySQLDatabaseTasks)
51
- register_task(/postgresql/, ActiveRecord::Tasks::PostgreSQLDatabaseTasks)
52
- register_task(/sqlite/, ActiveRecord::Tasks::SQLiteDatabaseTasks)
76
+ register_task(/mysql/, "ActiveRecord::Tasks::MySQLDatabaseTasks")
77
+ register_task(/postgresql/, "ActiveRecord::Tasks::PostgreSQLDatabaseTasks")
78
+ register_task(/sqlite/, "ActiveRecord::Tasks::SQLiteDatabaseTasks")
53
79
 
54
80
  def db_dir
55
81
  @db_dir ||= Rails.application.config.paths["db"].first
56
82
  end
57
83
 
58
84
  def migrations_paths
59
- @migrations_paths ||= Rails.application.paths['db/migrate'].to_a
85
+ @migrations_paths ||= Rails.application.paths["db/migrate"].to_a
60
86
  end
61
87
 
62
88
  def fixtures_path
63
- @fixtures_path ||= if ENV['FIXTURES_PATH']
64
- File.join(root, ENV['FIXTURES_PATH'])
65
- else
66
- File.join(root, 'test', 'fixtures')
67
- end
89
+ @fixtures_path ||= if ENV["FIXTURES_PATH"]
90
+ File.join(root, ENV["FIXTURES_PATH"])
91
+ else
92
+ File.join(root, "test", "fixtures")
93
+ end
68
94
  end
69
95
 
70
96
  def root
@@ -80,7 +106,7 @@ module ActiveRecord
80
106
  end
81
107
 
82
108
  def current_config(options = {})
83
- options.reverse_merge! :env => env
109
+ options.reverse_merge! env: env
84
110
  if options.has_key?(:config)
85
111
  @current_config = options[:config]
86
112
  else
@@ -90,16 +116,22 @@ module ActiveRecord
90
116
 
91
117
  def create(*arguments)
92
118
  configuration = arguments.first
93
- class_for_adapter(configuration['adapter']).new(*arguments).create
119
+ class_for_adapter(configuration["adapter"]).new(*arguments).create
120
+ $stdout.puts "Created database '#{configuration['database']}'"
94
121
  rescue DatabaseAlreadyExists
95
- $stderr.puts "#{configuration['database']} already exists"
122
+ $stderr.puts "Database '#{configuration['database']}' already exists"
96
123
  rescue Exception => error
97
- $stderr.puts error, *(error.backtrace)
98
- $stderr.puts "Couldn't create database for #{configuration.inspect}"
124
+ $stderr.puts error
125
+ $stderr.puts "Couldn't create '#{configuration['database']}' database. Please check your configuration."
126
+ raise
99
127
  end
100
128
 
101
129
  def create_all
130
+ old_pool = ActiveRecord::Base.connection_handler.retrieve_connection_pool(ActiveRecord::Base.connection_specification_name)
102
131
  each_local_configuration { |configuration| create configuration }
132
+ if old_pool
133
+ ActiveRecord::Base.connection_handler.establish_connection(old_pool.spec.to_hash)
134
+ end
103
135
  end
104
136
 
105
137
  def create_current(environment = env)
@@ -111,12 +143,14 @@ module ActiveRecord
111
143
 
112
144
  def drop(*arguments)
113
145
  configuration = arguments.first
114
- class_for_adapter(configuration['adapter']).new(*arguments).drop
146
+ class_for_adapter(configuration["adapter"]).new(*arguments).drop
147
+ $stdout.puts "Dropped database '#{configuration['database']}'"
115
148
  rescue ActiveRecord::NoDatabaseError
116
149
  $stderr.puts "Database '#{configuration['database']}' does not exist"
117
150
  rescue Exception => error
118
- $stderr.puts error, *(error.backtrace)
119
- $stderr.puts "Couldn't drop #{configuration['database']}"
151
+ $stderr.puts error
152
+ $stderr.puts "Couldn't drop database '#{configuration['database']}'"
153
+ raise
120
154
  end
121
155
 
122
156
  def drop_all
@@ -130,13 +164,12 @@ module ActiveRecord
130
164
  end
131
165
 
132
166
  def migrate
133
- raise "Empty VERSION provided" if ENV["VERSION"] && ENV["VERSION"].empty?
167
+ check_target_version
134
168
 
135
- verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
136
- version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
137
- scope = ENV['SCOPE']
169
+ verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] != "false" : true
170
+ scope = ENV["SCOPE"]
138
171
  verbose_was, Migration.verbose = Migration.verbose, verbose
139
- Migrator.migrate(migrations_paths, version) do |migration|
172
+ Base.connection.migration_context.migrate(target_version) do |migration|
140
173
  scope.blank? || scope == migration.scope
141
174
  end
142
175
  ActiveRecord::Base.clear_cache!
@@ -144,13 +177,23 @@ module ActiveRecord
144
177
  Migration.verbose = verbose_was
145
178
  end
146
179
 
180
+ def check_target_version
181
+ if target_version && !(Migration::MigrationFilenameRegexp.match?(ENV["VERSION"]) || /\A\d+\z/.match?(ENV["VERSION"]))
182
+ raise "Invalid format of target version: `VERSION=#{ENV['VERSION']}`"
183
+ end
184
+ end
185
+
186
+ def target_version
187
+ ENV["VERSION"].to_i if ENV["VERSION"] && !ENV["VERSION"].empty?
188
+ end
189
+
147
190
  def charset_current(environment = env)
148
191
  charset ActiveRecord::Base.configurations[environment]
149
192
  end
150
193
 
151
194
  def charset(*arguments)
152
195
  configuration = arguments.first
153
- class_for_adapter(configuration['adapter']).new(*arguments).charset
196
+ class_for_adapter(configuration["adapter"]).new(*arguments).charset
154
197
  end
155
198
 
156
199
  def collation_current(environment = env)
@@ -159,11 +202,11 @@ module ActiveRecord
159
202
 
160
203
  def collation(*arguments)
161
204
  configuration = arguments.first
162
- class_for_adapter(configuration['adapter']).new(*arguments).collation
205
+ class_for_adapter(configuration["adapter"]).new(*arguments).collation
163
206
  end
164
207
 
165
208
  def purge(configuration)
166
- class_for_adapter(configuration['adapter']).new(configuration).purge
209
+ class_for_adapter(configuration["adapter"]).new(configuration).purge
167
210
  end
168
211
 
169
212
  def purge_all
@@ -182,68 +225,53 @@ module ActiveRecord
182
225
  def structure_dump(*arguments)
183
226
  configuration = arguments.first
184
227
  filename = arguments.delete_at 1
185
- class_for_adapter(configuration['adapter']).new(*arguments).structure_dump(filename)
228
+ class_for_adapter(configuration["adapter"]).new(*arguments).structure_dump(filename, structure_dump_flags)
186
229
  end
187
230
 
188
231
  def structure_load(*arguments)
189
232
  configuration = arguments.first
190
233
  filename = arguments.delete_at 1
191
- class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename)
192
- end
193
-
194
- def load_schema(format = ActiveRecord::Base.schema_format, file = nil)
195
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
196
- This method will act on a specific connection in the future.
197
- To act on the current connection, use `load_schema_current` instead.
198
- MSG
199
-
200
- load_schema_current(format, file)
201
- end
202
-
203
- def schema_file(format = ActiveRecord::Base.schema_format)
204
- case format
205
- when :ruby
206
- File.join(db_dir, "schema.rb")
207
- when :sql
208
- File.join(db_dir, "structure.sql")
209
- end
234
+ class_for_adapter(configuration["adapter"]).new(*arguments).structure_load(filename, structure_load_flags)
210
235
  end
211
236
 
212
- # This method is the successor of +load_schema+. We should rename it
213
- # after +load_schema+ went through a deprecation cycle. (Rails > 4.2)
214
- def load_schema_for(configuration, format = ActiveRecord::Base.schema_format, file = nil) # :nodoc:
237
+ def load_schema(configuration, format = ActiveRecord::Base.schema_format, file = nil, environment = env) # :nodoc:
215
238
  file ||= schema_file(format)
216
239
 
240
+ check_schema_file(file)
241
+ ActiveRecord::Base.establish_connection(configuration)
242
+
217
243
  case format
218
244
  when :ruby
219
- check_schema_file(file)
220
- ActiveRecord::Base.establish_connection(configuration)
221
245
  load(file)
222
246
  when :sql
223
- check_schema_file(file)
224
247
  structure_load(configuration, file)
225
248
  else
226
249
  raise ArgumentError, "unknown format #{format.inspect}"
227
250
  end
251
+ ActiveRecord::InternalMetadata.create_table
252
+ ActiveRecord::InternalMetadata[:environment] = environment
228
253
  end
229
254
 
230
- def load_schema_current_if_exists(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
231
- if File.exist?(file || schema_file(format))
232
- load_schema_current(format, file, environment)
255
+ def schema_file(format = ActiveRecord::Base.schema_format)
256
+ case format
257
+ when :ruby
258
+ File.join(db_dir, "schema.rb")
259
+ when :sql
260
+ File.join(db_dir, "structure.sql")
233
261
  end
234
262
  end
235
263
 
236
264
  def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
237
- each_current_configuration(environment) { |configuration|
238
- load_schema_for configuration, format, file
265
+ each_current_configuration(environment) { |configuration, configuration_environment|
266
+ load_schema configuration, format, file, configuration_environment
239
267
  }
240
268
  ActiveRecord::Base.establish_connection(environment.to_sym)
241
269
  end
242
270
 
243
271
  def check_schema_file(filename)
244
272
  unless File.exist?(filename)
245
- message = %{#{filename} doesn't exist yet. Run `rake db:migrate` to create it, then try again.}
246
- message << %{ If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.} if defined?(::Rails)
273
+ message = %{#{filename} doesn't exist yet. Run `rails db:migrate` to create it, then try again.}.dup
274
+ message << %{ If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded.} if defined?(::Rails.root)
247
275
  Kernel.abort message
248
276
  end
249
277
  end
@@ -252,48 +280,58 @@ module ActiveRecord
252
280
  if seed_loader
253
281
  seed_loader.load_seed
254
282
  else
255
- raise "You tried to load seed data, but no seed loader is specified. Please specify seed " +
256
- "loader with ActiveRecord::Tasks::DatabaseTasks.seed_loader = your_seed_loader\n" +
283
+ raise "You tried to load seed data, but no seed loader is specified. Please specify seed " \
284
+ "loader with ActiveRecord::Tasks::DatabaseTasks.seed_loader = your_seed_loader\n" \
257
285
  "Seed loader should respond to load_seed method"
258
286
  end
259
287
  end
260
288
 
289
+ # Dumps the schema cache in YAML format for the connection into the file
290
+ #
291
+ # ==== Examples:
292
+ # ActiveRecord::Tasks::DatabaseTasks.dump_schema_cache(ActiveRecord::Base.connection, "tmp/schema_dump.yaml")
293
+ def dump_schema_cache(conn, filename)
294
+ conn.schema_cache.clear!
295
+ conn.data_sources.each { |table| conn.schema_cache.add(table) }
296
+ open(filename, "wb") { |f| f.write(YAML.dump(conn.schema_cache)) }
297
+ end
298
+
261
299
  private
262
300
 
263
- def class_for_adapter(adapter)
264
- key = @tasks.keys.detect { |pattern| adapter[pattern] }
265
- unless key
266
- raise DatabaseNotSupported, "Rake tasks not supported by '#{adapter}' adapter"
301
+ def class_for_adapter(adapter)
302
+ _key, task = @tasks.each_pair.detect { |pattern, _task| adapter[pattern] }
303
+ unless task
304
+ raise DatabaseNotSupported, "Rake tasks not supported by '#{adapter}' adapter"
305
+ end
306
+ task.is_a?(String) ? task.constantize : task
267
307
  end
268
- @tasks[key]
269
- end
270
308
 
271
- def each_current_configuration(environment)
272
- environments = [environment]
273
- # add test environment only if no RAILS_ENV was specified.
274
- environments << 'test' if environment == 'development' && ENV['RAILS_ENV'].nil?
309
+ def each_current_configuration(environment)
310
+ environments = [environment]
311
+ environments << "test" if environment == "development"
275
312
 
276
- configurations = ActiveRecord::Base.configurations.values_at(*environments)
277
- configurations.compact.each do |configuration|
278
- yield configuration unless configuration['database'].blank?
313
+ ActiveRecord::Base.configurations.slice(*environments).each do |configuration_environment, configuration|
314
+ next unless configuration["database"]
315
+
316
+ yield configuration, configuration_environment
317
+ end
279
318
  end
280
- end
281
319
 
282
- def each_local_configuration
283
- ActiveRecord::Base.configurations.each_value do |configuration|
284
- next unless configuration['database']
320
+ def each_local_configuration
321
+ ActiveRecord::Base.configurations.each_value do |configuration|
322
+ next unless configuration["database"]
285
323
 
286
- if local_database?(configuration)
287
- yield configuration
288
- else
289
- $stderr.puts "This task only modifies local databases. #{configuration['database']} is on a remote host."
324
+ if local_database?(configuration)
325
+ yield configuration
326
+ else
327
+ $stderr.puts "This task only modifies local databases. #{configuration['database']} is on a remote host."
328
+ end
290
329
  end
291
330
  end
292
- end
293
331
 
294
- def local_database?(configuration)
295
- configuration['host'].blank? || LOCAL_HOSTS.include?(configuration['host'])
296
- end
332
+ def local_database?(configuration)
333
+ configuration["host"].blank? || LOCAL_HOSTS.include?(configuration["host"])
334
+ end
297
335
  end
298
336
  end
299
337
  end