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
@@ -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,24 +164,36 @@ module ActiveRecord
130
164
  end
131
165
 
132
166
  def migrate
133
- verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true
134
- version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil
135
- scope = ENV['SCOPE']
167
+ check_target_version
168
+
169
+ verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] != "false" : true
170
+ scope = ENV["SCOPE"]
136
171
  verbose_was, Migration.verbose = Migration.verbose, verbose
137
- Migrator.migrate(Migrator.migrations_paths, version) do |migration|
172
+ Base.connection.migration_context.migrate(target_version) do |migration|
138
173
  scope.blank? || scope == migration.scope
139
174
  end
175
+ ActiveRecord::Base.clear_cache!
140
176
  ensure
141
177
  Migration.verbose = verbose_was
142
178
  end
143
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
+
144
190
  def charset_current(environment = env)
145
191
  charset ActiveRecord::Base.configurations[environment]
146
192
  end
147
193
 
148
194
  def charset(*arguments)
149
195
  configuration = arguments.first
150
- class_for_adapter(configuration['adapter']).new(*arguments).charset
196
+ class_for_adapter(configuration["adapter"]).new(*arguments).charset
151
197
  end
152
198
 
153
199
  def collation_current(environment = env)
@@ -156,11 +202,11 @@ module ActiveRecord
156
202
 
157
203
  def collation(*arguments)
158
204
  configuration = arguments.first
159
- class_for_adapter(configuration['adapter']).new(*arguments).collation
205
+ class_for_adapter(configuration["adapter"]).new(*arguments).collation
160
206
  end
161
207
 
162
208
  def purge(configuration)
163
- class_for_adapter(configuration['adapter']).new(configuration).purge
209
+ class_for_adapter(configuration["adapter"]).new(configuration).purge
164
210
  end
165
211
 
166
212
  def purge_all
@@ -179,68 +225,53 @@ module ActiveRecord
179
225
  def structure_dump(*arguments)
180
226
  configuration = arguments.first
181
227
  filename = arguments.delete_at 1
182
- class_for_adapter(configuration['adapter']).new(*arguments).structure_dump(filename)
228
+ class_for_adapter(configuration["adapter"]).new(*arguments).structure_dump(filename, structure_dump_flags)
183
229
  end
184
230
 
185
231
  def structure_load(*arguments)
186
232
  configuration = arguments.first
187
233
  filename = arguments.delete_at 1
188
- class_for_adapter(configuration['adapter']).new(*arguments).structure_load(filename)
189
- end
190
-
191
- def load_schema(format = ActiveRecord::Base.schema_format, file = nil)
192
- ActiveSupport::Deprecation.warn(<<-MSG.squish)
193
- This method will act on a specific connection in the future.
194
- To act on the current connection, use `load_schema_current` instead.
195
- MSG
196
-
197
- load_schema_current(format, file)
234
+ class_for_adapter(configuration["adapter"]).new(*arguments).structure_load(filename, structure_load_flags)
198
235
  end
199
236
 
200
- def schema_file(format = ActiveSupport::Base.schema_format)
201
- case format
202
- when :ruby
203
- File.join(db_dir, "schema.rb")
204
- when :sql
205
- File.join(db_dir, "structure.sql")
206
- end
207
- end
208
-
209
- # This method is the successor of +load_schema+. We should rename it
210
- # after +load_schema+ went through a deprecation cycle. (Rails > 4.2)
211
- 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:
212
238
  file ||= schema_file(format)
213
239
 
240
+ check_schema_file(file)
241
+ ActiveRecord::Base.establish_connection(configuration)
242
+
214
243
  case format
215
244
  when :ruby
216
- check_schema_file(file)
217
- ActiveRecord::Base.establish_connection(configuration)
218
245
  load(file)
219
246
  when :sql
220
- check_schema_file(file)
221
247
  structure_load(configuration, file)
222
248
  else
223
249
  raise ArgumentError, "unknown format #{format.inspect}"
224
250
  end
251
+ ActiveRecord::InternalMetadata.create_table
252
+ ActiveRecord::InternalMetadata[:environment] = environment
225
253
  end
226
254
 
227
- def load_schema_current_if_exists(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
228
- if File.exist?(file || schema_file(format))
229
- 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")
230
261
  end
231
262
  end
232
263
 
233
264
  def load_schema_current(format = ActiveRecord::Base.schema_format, file = nil, environment = env)
234
- each_current_configuration(environment) { |configuration|
235
- load_schema_for configuration, format, file
265
+ each_current_configuration(environment) { |configuration, configuration_environment|
266
+ load_schema configuration, format, file, configuration_environment
236
267
  }
237
268
  ActiveRecord::Base.establish_connection(environment.to_sym)
238
269
  end
239
270
 
240
271
  def check_schema_file(filename)
241
272
  unless File.exist?(filename)
242
- message = %{#{filename} doesn't exist yet. Run `rake db:migrate` to create it, then try again.}
243
- 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)
244
275
  Kernel.abort message
245
276
  end
246
277
  end
@@ -249,48 +280,58 @@ module ActiveRecord
249
280
  if seed_loader
250
281
  seed_loader.load_seed
251
282
  else
252
- raise "You tried to load seed data, but no seed loader is specified. Please specify seed " +
253
- "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" \
254
285
  "Seed loader should respond to load_seed method"
255
286
  end
256
287
  end
257
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
+
258
299
  private
259
300
 
260
- def class_for_adapter(adapter)
261
- key = @tasks.keys.detect { |pattern| adapter[pattern] }
262
- unless key
263
- 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
264
307
  end
265
- @tasks[key]
266
- end
267
308
 
268
- def each_current_configuration(environment)
269
- environments = [environment]
270
- # add test environment only if no RAILS_ENV was specified.
271
- environments << 'test' if environment == 'development' && ENV['RAILS_ENV'].nil?
309
+ def each_current_configuration(environment)
310
+ environments = [environment]
311
+ environments << "test" if environment == "development"
312
+
313
+ ActiveRecord::Base.configurations.slice(*environments).each do |configuration_environment, configuration|
314
+ next unless configuration["database"]
272
315
 
273
- configurations = ActiveRecord::Base.configurations.values_at(*environments)
274
- configurations.compact.each do |configuration|
275
- yield configuration unless configuration['database'].blank?
316
+ yield configuration, configuration_environment
317
+ end
276
318
  end
277
- end
278
319
 
279
- def each_local_configuration
280
- ActiveRecord::Base.configurations.each_value do |configuration|
281
- next unless configuration['database']
320
+ def each_local_configuration
321
+ ActiveRecord::Base.configurations.each_value do |configuration|
322
+ next unless configuration["database"]
282
323
 
283
- if local_database?(configuration)
284
- yield configuration
285
- else
286
- $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
287
329
  end
288
330
  end
289
- end
290
331
 
291
- def local_database?(configuration)
292
- configuration['host'].blank? || LOCAL_HOSTS.include?(configuration['host'])
293
- end
332
+ def local_database?(configuration)
333
+ configuration["host"].blank? || LOCAL_HOSTS.include?(configuration["host"])
334
+ end
294
335
  end
295
336
  end
296
337
  end