activerecord 5.2.4.2 → 6.0.2.2

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 (269) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +715 -566
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +4 -2
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +9 -2
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/association_relation.rb +15 -6
  9. data/lib/active_record/associations.rb +20 -15
  10. data/lib/active_record/associations/association.rb +61 -20
  11. data/lib/active_record/associations/association_scope.rb +4 -6
  12. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  14. data/lib/active_record/associations/builder/association.rb +14 -18
  15. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  16. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  18. data/lib/active_record/associations/builder/has_many.rb +2 -0
  19. data/lib/active_record/associations/builder/has_one.rb +35 -1
  20. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  21. data/lib/active_record/associations/collection_association.rb +12 -23
  22. data/lib/active_record/associations/collection_proxy.rb +12 -15
  23. data/lib/active_record/associations/foreign_association.rb +7 -0
  24. data/lib/active_record/associations/has_many_association.rb +2 -10
  25. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  26. data/lib/active_record/associations/has_one_association.rb +28 -30
  27. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  28. data/lib/active_record/associations/join_dependency.rb +28 -28
  29. data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
  30. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  31. data/lib/active_record/associations/preloader.rb +39 -31
  32. data/lib/active_record/associations/preloader/association.rb +38 -36
  33. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  34. data/lib/active_record/associations/singular_association.rb +2 -16
  35. data/lib/active_record/attribute_assignment.rb +7 -10
  36. data/lib/active_record/attribute_methods.rb +28 -100
  37. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  38. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  39. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  40. data/lib/active_record/attribute_methods/query.rb +2 -3
  41. data/lib/active_record/attribute_methods/read.rb +15 -53
  42. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  43. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  44. data/lib/active_record/attribute_methods/write.rb +17 -24
  45. data/lib/active_record/attributes.rb +13 -0
  46. data/lib/active_record/autosave_association.rb +2 -2
  47. data/lib/active_record/base.rb +2 -3
  48. data/lib/active_record/callbacks.rb +5 -19
  49. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +104 -16
  50. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  51. data/lib/active_record/connection_adapters/abstract/database_statements.rb +99 -123
  52. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -8
  53. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +132 -53
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +187 -43
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +138 -195
  61. data/lib/active_record/connection_adapters/column.rb +17 -13
  62. data/lib/active_record/connection_adapters/connection_specification.rb +53 -43
  63. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +75 -13
  65. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  66. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  67. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  68. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  69. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  70. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  71. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
  72. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  73. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +22 -1
  74. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  75. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  76. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  77. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  81. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  82. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  83. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  84. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
  85. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  86. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
  87. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  88. data/lib/active_record/connection_adapters/postgresql_adapter.rb +164 -74
  89. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  90. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  91. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +120 -0
  92. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
  93. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  94. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +129 -141
  95. data/lib/active_record/connection_handling.rb +155 -26
  96. data/lib/active_record/core.rb +103 -59
  97. data/lib/active_record/counter_cache.rb +4 -29
  98. data/lib/active_record/database_configurations.rb +233 -0
  99. data/lib/active_record/database_configurations/database_config.rb +37 -0
  100. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  101. data/lib/active_record/database_configurations/url_config.rb +79 -0
  102. data/lib/active_record/dynamic_matchers.rb +1 -1
  103. data/lib/active_record/enum.rb +37 -7
  104. data/lib/active_record/errors.rb +15 -7
  105. data/lib/active_record/explain.rb +1 -1
  106. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  107. data/lib/active_record/fixture_set/render_context.rb +17 -0
  108. data/lib/active_record/fixture_set/table_row.rb +153 -0
  109. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  110. data/lib/active_record/fixtures.rb +145 -472
  111. data/lib/active_record/gem_version.rb +3 -3
  112. data/lib/active_record/inheritance.rb +13 -3
  113. data/lib/active_record/insert_all.rb +179 -0
  114. data/lib/active_record/integration.rb +68 -16
  115. data/lib/active_record/internal_metadata.rb +10 -2
  116. data/lib/active_record/locking/optimistic.rb +5 -6
  117. data/lib/active_record/locking/pessimistic.rb +3 -3
  118. data/lib/active_record/log_subscriber.rb +7 -26
  119. data/lib/active_record/middleware/database_selector.rb +75 -0
  120. data/lib/active_record/middleware/database_selector/resolver.rb +88 -0
  121. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  122. data/lib/active_record/migration.rb +100 -81
  123. data/lib/active_record/migration/command_recorder.rb +50 -6
  124. data/lib/active_record/migration/compatibility.rb +76 -49
  125. data/lib/active_record/model_schema.rb +33 -9
  126. data/lib/active_record/nested_attributes.rb +2 -2
  127. data/lib/active_record/no_touching.rb +7 -0
  128. data/lib/active_record/persistence.rb +228 -24
  129. data/lib/active_record/query_cache.rb +11 -4
  130. data/lib/active_record/querying.rb +32 -20
  131. data/lib/active_record/railtie.rb +80 -43
  132. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  133. data/lib/active_record/railties/controller_runtime.rb +30 -35
  134. data/lib/active_record/railties/databases.rake +199 -46
  135. data/lib/active_record/reflection.rb +32 -30
  136. data/lib/active_record/relation.rb +311 -80
  137. data/lib/active_record/relation/batches.rb +13 -10
  138. data/lib/active_record/relation/calculations.rb +53 -47
  139. data/lib/active_record/relation/delegation.rb +26 -43
  140. data/lib/active_record/relation/finder_methods.rb +23 -27
  141. data/lib/active_record/relation/merger.rb +11 -20
  142. data/lib/active_record/relation/predicate_builder.rb +4 -6
  143. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  144. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  145. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  146. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  147. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  148. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  149. data/lib/active_record/relation/query_attribute.rb +13 -8
  150. data/lib/active_record/relation/query_methods.rb +213 -64
  151. data/lib/active_record/relation/spawn_methods.rb +1 -1
  152. data/lib/active_record/relation/where_clause.rb +14 -10
  153. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  154. data/lib/active_record/result.rb +30 -11
  155. data/lib/active_record/sanitization.rb +32 -40
  156. data/lib/active_record/schema.rb +2 -11
  157. data/lib/active_record/schema_dumper.rb +22 -7
  158. data/lib/active_record/schema_migration.rb +5 -1
  159. data/lib/active_record/scoping.rb +8 -8
  160. data/lib/active_record/scoping/default.rb +4 -5
  161. data/lib/active_record/scoping/named.rb +20 -15
  162. data/lib/active_record/statement_cache.rb +30 -3
  163. data/lib/active_record/store.rb +87 -8
  164. data/lib/active_record/table_metadata.rb +10 -17
  165. data/lib/active_record/tasks/database_tasks.rb +194 -25
  166. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  167. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  168. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  169. data/lib/active_record/test_databases.rb +23 -0
  170. data/lib/active_record/test_fixtures.rb +225 -0
  171. data/lib/active_record/timestamp.rb +39 -25
  172. data/lib/active_record/touch_later.rb +4 -2
  173. data/lib/active_record/transactions.rb +56 -65
  174. data/lib/active_record/translation.rb +1 -1
  175. data/lib/active_record/type.rb +3 -4
  176. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  177. data/lib/active_record/type_caster/connection.rb +15 -14
  178. data/lib/active_record/type_caster/map.rb +1 -4
  179. data/lib/active_record/validations.rb +1 -0
  180. data/lib/active_record/validations/uniqueness.rb +15 -27
  181. data/lib/arel.rb +58 -0
  182. data/lib/arel/alias_predication.rb +9 -0
  183. data/lib/arel/attributes.rb +22 -0
  184. data/lib/arel/attributes/attribute.rb +37 -0
  185. data/lib/arel/collectors/bind.rb +24 -0
  186. data/lib/arel/collectors/composite.rb +31 -0
  187. data/lib/arel/collectors/plain_string.rb +20 -0
  188. data/lib/arel/collectors/sql_string.rb +20 -0
  189. data/lib/arel/collectors/substitute_binds.rb +28 -0
  190. data/lib/arel/crud.rb +42 -0
  191. data/lib/arel/delete_manager.rb +18 -0
  192. data/lib/arel/errors.rb +9 -0
  193. data/lib/arel/expressions.rb +29 -0
  194. data/lib/arel/factory_methods.rb +49 -0
  195. data/lib/arel/insert_manager.rb +49 -0
  196. data/lib/arel/math.rb +45 -0
  197. data/lib/arel/nodes.rb +68 -0
  198. data/lib/arel/nodes/and.rb +32 -0
  199. data/lib/arel/nodes/ascending.rb +23 -0
  200. data/lib/arel/nodes/binary.rb +52 -0
  201. data/lib/arel/nodes/bind_param.rb +36 -0
  202. data/lib/arel/nodes/case.rb +55 -0
  203. data/lib/arel/nodes/casted.rb +50 -0
  204. data/lib/arel/nodes/comment.rb +29 -0
  205. data/lib/arel/nodes/count.rb +12 -0
  206. data/lib/arel/nodes/delete_statement.rb +45 -0
  207. data/lib/arel/nodes/descending.rb +23 -0
  208. data/lib/arel/nodes/equality.rb +18 -0
  209. data/lib/arel/nodes/extract.rb +24 -0
  210. data/lib/arel/nodes/false.rb +16 -0
  211. data/lib/arel/nodes/full_outer_join.rb +8 -0
  212. data/lib/arel/nodes/function.rb +44 -0
  213. data/lib/arel/nodes/grouping.rb +8 -0
  214. data/lib/arel/nodes/in.rb +8 -0
  215. data/lib/arel/nodes/infix_operation.rb +80 -0
  216. data/lib/arel/nodes/inner_join.rb +8 -0
  217. data/lib/arel/nodes/insert_statement.rb +37 -0
  218. data/lib/arel/nodes/join_source.rb +20 -0
  219. data/lib/arel/nodes/matches.rb +18 -0
  220. data/lib/arel/nodes/named_function.rb +23 -0
  221. data/lib/arel/nodes/node.rb +50 -0
  222. data/lib/arel/nodes/node_expression.rb +13 -0
  223. data/lib/arel/nodes/outer_join.rb +8 -0
  224. data/lib/arel/nodes/over.rb +15 -0
  225. data/lib/arel/nodes/regexp.rb +16 -0
  226. data/lib/arel/nodes/right_outer_join.rb +8 -0
  227. data/lib/arel/nodes/select_core.rb +67 -0
  228. data/lib/arel/nodes/select_statement.rb +41 -0
  229. data/lib/arel/nodes/sql_literal.rb +16 -0
  230. data/lib/arel/nodes/string_join.rb +11 -0
  231. data/lib/arel/nodes/table_alias.rb +27 -0
  232. data/lib/arel/nodes/terminal.rb +16 -0
  233. data/lib/arel/nodes/true.rb +16 -0
  234. data/lib/arel/nodes/unary.rb +45 -0
  235. data/lib/arel/nodes/unary_operation.rb +20 -0
  236. data/lib/arel/nodes/unqualified_column.rb +22 -0
  237. data/lib/arel/nodes/update_statement.rb +41 -0
  238. data/lib/arel/nodes/values_list.rb +9 -0
  239. data/lib/arel/nodes/window.rb +126 -0
  240. data/lib/arel/nodes/with.rb +11 -0
  241. data/lib/arel/order_predications.rb +13 -0
  242. data/lib/arel/predications.rb +257 -0
  243. data/lib/arel/select_manager.rb +271 -0
  244. data/lib/arel/table.rb +110 -0
  245. data/lib/arel/tree_manager.rb +72 -0
  246. data/lib/arel/update_manager.rb +34 -0
  247. data/lib/arel/visitors.rb +20 -0
  248. data/lib/arel/visitors/depth_first.rb +204 -0
  249. data/lib/arel/visitors/dot.rb +297 -0
  250. data/lib/arel/visitors/ibm_db.rb +34 -0
  251. data/lib/arel/visitors/informix.rb +62 -0
  252. data/lib/arel/visitors/mssql.rb +157 -0
  253. data/lib/arel/visitors/mysql.rb +83 -0
  254. data/lib/arel/visitors/oracle.rb +159 -0
  255. data/lib/arel/visitors/oracle12.rb +66 -0
  256. data/lib/arel/visitors/postgresql.rb +110 -0
  257. data/lib/arel/visitors/sqlite.rb +39 -0
  258. data/lib/arel/visitors/to_sql.rb +889 -0
  259. data/lib/arel/visitors/visitor.rb +46 -0
  260. data/lib/arel/visitors/where_sql.rb +23 -0
  261. data/lib/arel/window_predications.rb +9 -0
  262. data/lib/rails/generators/active_record/migration.rb +14 -1
  263. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  264. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  265. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  266. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  267. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  268. metadata +109 -24
  269. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -102,27 +102,7 @@ module ActiveRecord
102
102
  # # `updated_at` = '2016-10-13T09:59:23-05:00'
103
103
  # # WHERE id IN (10, 15)
104
104
  def update_counters(id, counters)
105
- touch = counters.delete(:touch)
106
-
107
- updates = counters.map do |counter_name, value|
108
- operator = value < 0 ? "-" : "+"
109
- quoted_column = connection.quote_column_name(counter_name)
110
- "#{quoted_column} = COALESCE(#{quoted_column}, 0) #{operator} #{value.abs}"
111
- end
112
-
113
- if touch
114
- names = touch if touch != true
115
- touch_updates = touch_attributes_with_time(*names)
116
- updates << sanitize_sql_for_assignment(touch_updates) unless touch_updates.empty?
117
- end
118
-
119
- if id.is_a?(Relation) && self == id.klass
120
- relation = id
121
- else
122
- relation = unscoped.where!(primary_key => id)
123
- end
124
-
125
- relation.update_all updates.join(", ")
105
+ unscoped.where!(primary_key => id).update_counters(counters)
126
106
  end
127
107
 
128
108
  # Increment a numeric field by one, via a direct SQL update.
@@ -179,14 +159,11 @@ module ActiveRecord
179
159
  end
180
160
 
181
161
  private
182
-
183
- def _create_record(*)
162
+ def _create_record(attribute_names = self.attribute_names)
184
163
  id = super
185
164
 
186
165
  each_counter_cached_associations do |association|
187
- if send(association.reflection.name)
188
- association.increment_counters
189
- end
166
+ association.increment_counters
190
167
  end
191
168
 
192
169
  id
@@ -199,9 +176,7 @@ module ActiveRecord
199
176
  each_counter_cached_associations do |association|
200
177
  foreign_key = association.reflection.foreign_key.to_sym
201
178
  unless destroyed_by_association && destroyed_by_association.foreign_key.to_sym == foreign_key
202
- if send(association.reflection.name)
203
- association.decrement_counters
204
- end
179
+ association.decrement_counters
205
180
  end
206
181
  end
207
182
  end
@@ -0,0 +1,233 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/database_configurations/database_config"
4
+ require "active_record/database_configurations/hash_config"
5
+ require "active_record/database_configurations/url_config"
6
+
7
+ module ActiveRecord
8
+ # ActiveRecord::DatabaseConfigurations returns an array of DatabaseConfig
9
+ # objects (either a HashConfig or UrlConfig) that are constructed from the
10
+ # application's database configuration hash or URL string.
11
+ class DatabaseConfigurations
12
+ class InvalidConfigurationError < StandardError; end
13
+
14
+ attr_reader :configurations
15
+ delegate :any?, to: :configurations
16
+
17
+ def initialize(configurations = {})
18
+ @configurations = build_configs(configurations)
19
+ end
20
+
21
+ # Collects the configs for the environment and optionally the specification
22
+ # name passed in. To include replica configurations pass <tt>include_replicas: true</tt>.
23
+ #
24
+ # If a spec name is provided a single DatabaseConfig object will be
25
+ # returned, otherwise an array of DatabaseConfig objects will be
26
+ # returned that corresponds with the environment and type requested.
27
+ #
28
+ # ==== Options
29
+ #
30
+ # * <tt>env_name:</tt> The environment name. Defaults to +nil+ which will collect
31
+ # configs for all environments.
32
+ # * <tt>spec_name:</tt> The specification name (i.e. primary, animals, etc.). Defaults
33
+ # to +nil+.
34
+ # * <tt>include_replicas:</tt> Determines whether to include replicas in
35
+ # the returned list. Most of the time we're only iterating over the write
36
+ # connection (i.e. migrations don't need to run for the write and read connection).
37
+ # Defaults to +false+.
38
+ def configs_for(env_name: nil, spec_name: nil, include_replicas: false)
39
+ configs = env_with_configs(env_name)
40
+
41
+ unless include_replicas
42
+ configs = configs.select do |db_config|
43
+ !db_config.replica?
44
+ end
45
+ end
46
+
47
+ if spec_name
48
+ configs.find do |db_config|
49
+ db_config.spec_name == spec_name
50
+ end
51
+ else
52
+ configs
53
+ end
54
+ end
55
+
56
+ # Returns the config hash that corresponds with the environment
57
+ #
58
+ # If the application has multiple databases +default_hash+ will
59
+ # return the first config hash for the environment.
60
+ #
61
+ # { database: "my_db", adapter: "mysql2" }
62
+ def default_hash(env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s)
63
+ default = find_db_config(env)
64
+ default.config if default
65
+ end
66
+ alias :[] :default_hash
67
+
68
+ # Returns a single DatabaseConfig object based on the requested environment.
69
+ #
70
+ # If the application has multiple databases +find_db_config+ will return
71
+ # the first DatabaseConfig for the environment.
72
+ def find_db_config(env)
73
+ configurations.find do |db_config|
74
+ db_config.env_name == env.to_s ||
75
+ (db_config.for_current_env? && db_config.spec_name == env.to_s)
76
+ end
77
+ end
78
+
79
+ # Returns the DatabaseConfigurations object as a Hash.
80
+ def to_h
81
+ configs = configurations.reverse.inject({}) do |memo, db_config|
82
+ memo.merge(db_config.to_legacy_hash)
83
+ end
84
+
85
+ Hash[configs.to_a.reverse]
86
+ end
87
+
88
+ # Checks if the application's configurations are empty.
89
+ #
90
+ # Aliased to blank?
91
+ def empty?
92
+ configurations.empty?
93
+ end
94
+ alias :blank? :empty?
95
+
96
+ def each
97
+ throw_getter_deprecation(:each)
98
+ configurations.each { |config|
99
+ yield [config.env_name, config.config]
100
+ }
101
+ end
102
+
103
+ def first
104
+ throw_getter_deprecation(:first)
105
+ config = configurations.first
106
+ [config.env_name, config.config]
107
+ end
108
+
109
+ private
110
+ def env_with_configs(env = nil)
111
+ if env
112
+ configurations.select { |db_config| db_config.env_name == env }
113
+ else
114
+ configurations
115
+ end
116
+ end
117
+
118
+ def build_configs(configs)
119
+ return configs.configurations if configs.is_a?(DatabaseConfigurations)
120
+ return configs if configs.is_a?(Array)
121
+
122
+ db_configs = configs.flat_map do |env_name, config|
123
+ if config.is_a?(Hash) && config.all? { |_, v| v.is_a?(Hash) }
124
+ walk_configs(env_name.to_s, config)
125
+ else
126
+ build_db_config_from_raw_config(env_name.to_s, "primary", config)
127
+ end
128
+ end
129
+
130
+ current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s
131
+
132
+ unless db_configs.find(&:for_current_env?)
133
+ db_configs << environment_url_config(current_env, "primary", {})
134
+ end
135
+
136
+ merge_db_environment_variables(current_env, db_configs.compact)
137
+ end
138
+
139
+ def walk_configs(env_name, config)
140
+ config.map do |spec_name, sub_config|
141
+ build_db_config_from_raw_config(env_name, spec_name.to_s, sub_config)
142
+ end
143
+ end
144
+
145
+ def build_db_config_from_raw_config(env_name, spec_name, config)
146
+ case config
147
+ when String
148
+ build_db_config_from_string(env_name, spec_name, config)
149
+ when Hash
150
+ build_db_config_from_hash(env_name, spec_name, config.stringify_keys)
151
+ else
152
+ raise InvalidConfigurationError, "'{ #{env_name} => #{config} }' is not a valid configuration. Expected '#{config}' to be a URL string or a Hash."
153
+ end
154
+ end
155
+
156
+ def build_db_config_from_string(env_name, spec_name, config)
157
+ url = config
158
+ uri = URI.parse(url)
159
+ if uri.scheme
160
+ ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url)
161
+ else
162
+ raise InvalidConfigurationError, "'{ #{env_name} => #{config} }' is not a valid configuration. Expected '#{config}' to be a URL string or a Hash."
163
+ end
164
+ end
165
+
166
+ def build_db_config_from_hash(env_name, spec_name, config)
167
+ if config.has_key?("url")
168
+ url = config["url"]
169
+ config_without_url = config.dup
170
+ config_without_url.delete "url"
171
+
172
+ ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url, config_without_url)
173
+ else
174
+ ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
175
+ end
176
+ end
177
+
178
+ def merge_db_environment_variables(current_env, configs)
179
+ configs.map do |config|
180
+ next config if config.url_config? || config.env_name != current_env
181
+
182
+ url_config = environment_url_config(current_env, config.spec_name, config.config)
183
+ url_config || config
184
+ end
185
+ end
186
+
187
+ def environment_url_config(env, spec_name, config)
188
+ url = environment_value_for(spec_name)
189
+ return unless url
190
+
191
+ ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, spec_name, url, config)
192
+ end
193
+
194
+ def environment_value_for(spec_name)
195
+ spec_env_key = "#{spec_name.upcase}_DATABASE_URL"
196
+ url = ENV[spec_env_key]
197
+ url ||= ENV["DATABASE_URL"] if spec_name == "primary"
198
+ url
199
+ end
200
+
201
+ def method_missing(method, *args, &blk)
202
+ case method
203
+ when :fetch
204
+ throw_getter_deprecation(method)
205
+ configs_for(env_name: args.first)
206
+ when :values
207
+ throw_getter_deprecation(method)
208
+ configurations.map(&:config)
209
+ when :[]=
210
+ throw_setter_deprecation(method)
211
+
212
+ env_name = args[0]
213
+ config = args[1]
214
+
215
+ remaining_configs = configurations.reject { |db_config| db_config.env_name == env_name }
216
+ new_config = build_configs(env_name => config)
217
+ new_configs = remaining_configs + new_config
218
+
219
+ ActiveRecord::Base.configurations = new_configs
220
+ else
221
+ raise NotImplementedError, "`ActiveRecord::Base.configurations` in Rails 6 now returns an object instead of a hash. The `#{method}` method is not supported. Please use `configs_for` or consult the documentation for supported methods."
222
+ end
223
+ end
224
+
225
+ def throw_setter_deprecation(method)
226
+ ActiveSupport::Deprecation.warn("Setting `ActiveRecord::Base.configurations` with `#{method}` is deprecated. Use `ActiveRecord::Base.configurations=` directly to set the configurations instead.")
227
+ end
228
+
229
+ def throw_getter_deprecation(method)
230
+ ActiveSupport::Deprecation.warn("`ActiveRecord::Base.configurations` no longer returns a hash. Methods that act on the hash like `#{method}` are deprecated and will be removed in Rails 6.1. Use the `configs_for` method to collect and iterate over the database configurations.")
231
+ end
232
+ end
233
+ end
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class DatabaseConfigurations
5
+ # ActiveRecord::Base.configurations will return either a HashConfig or
6
+ # UrlConfig respectively. It will never return a DatabaseConfig object,
7
+ # as this is the parent class for the types of database configuration objects.
8
+ class DatabaseConfig # :nodoc:
9
+ attr_reader :env_name, :spec_name
10
+
11
+ def initialize(env_name, spec_name)
12
+ @env_name = env_name
13
+ @spec_name = spec_name
14
+ end
15
+
16
+ def replica?
17
+ raise NotImplementedError
18
+ end
19
+
20
+ def migrations_paths
21
+ raise NotImplementedError
22
+ end
23
+
24
+ def url_config?
25
+ false
26
+ end
27
+
28
+ def to_legacy_hash
29
+ { env_name => config }
30
+ end
31
+
32
+ def for_current_env?
33
+ env_name == ActiveRecord::ConnectionHandling::DEFAULT_ENV.call
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class DatabaseConfigurations
5
+ # A HashConfig object is created for each database configuration entry that
6
+ # is created from a hash.
7
+ #
8
+ # A hash config:
9
+ #
10
+ # { "development" => { "database" => "db_name" } }
11
+ #
12
+ # Becomes:
13
+ #
14
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
15
+ # @env_name="development", @spec_name="primary", @config={"database"=>"db_name"}>
16
+ #
17
+ # ==== Options
18
+ #
19
+ # * <tt>:env_name</tt> - The Rails environment, i.e. "development".
20
+ # * <tt>:spec_name</tt> - The specification name. In a standard two-tier
21
+ # database configuration this will default to "primary". In a multiple
22
+ # database three-tier database configuration this corresponds to the name
23
+ # used in the second tier, for example "primary_readonly".
24
+ # * <tt>:config</tt> - The config hash. This is the hash that contains the
25
+ # database adapter, name, and other important information for database
26
+ # connections.
27
+ class HashConfig < DatabaseConfig
28
+ attr_reader :config
29
+
30
+ def initialize(env_name, spec_name, config)
31
+ super(env_name, spec_name)
32
+ @config = config
33
+ end
34
+
35
+ # Determines whether a database configuration is for a replica / readonly
36
+ # connection. If the +replica+ key is present in the config, +replica?+ will
37
+ # return +true+.
38
+ def replica?
39
+ config["replica"]
40
+ end
41
+
42
+ # The migrations paths for a database configuration. If the
43
+ # +migrations_paths+ key is present in the config, +migrations_paths+
44
+ # will return its value.
45
+ def migrations_paths
46
+ config["migrations_paths"]
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ class DatabaseConfigurations
5
+ # A UrlConfig object is created for each database configuration
6
+ # entry that is created from a URL. This can either be a URL string
7
+ # or a hash with a URL in place of the config hash.
8
+ #
9
+ # A URL config:
10
+ #
11
+ # postgres://localhost/foo
12
+ #
13
+ # Becomes:
14
+ #
15
+ # #<ActiveRecord::DatabaseConfigurations::UrlConfig:0x00007fdc3238f340
16
+ # @env_name="default_env", @spec_name="primary",
17
+ # @config={"adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost"},
18
+ # @url="postgres://localhost/foo">
19
+ #
20
+ # ==== Options
21
+ #
22
+ # * <tt>:env_name</tt> - The Rails environment, ie "development".
23
+ # * <tt>:spec_name</tt> - The specification name. In a standard two-tier
24
+ # database configuration this will default to "primary". In a multiple
25
+ # database three-tier database configuration this corresponds to the name
26
+ # used in the second tier, for example "primary_readonly".
27
+ # * <tt>:url</tt> - The database URL.
28
+ # * <tt>:config</tt> - The config hash. This is the hash that contains the
29
+ # database adapter, name, and other important information for database
30
+ # connections.
31
+ class UrlConfig < DatabaseConfig
32
+ attr_reader :url, :config
33
+
34
+ def initialize(env_name, spec_name, url, config = {})
35
+ super(env_name, spec_name)
36
+ @config = build_config(config, url)
37
+ @url = url
38
+ end
39
+
40
+ def url_config? # :nodoc:
41
+ true
42
+ end
43
+
44
+ # Determines whether a database configuration is for a replica / readonly
45
+ # connection. If the +replica+ key is present in the config, +replica?+ will
46
+ # return +true+.
47
+ def replica?
48
+ config["replica"]
49
+ end
50
+
51
+ # The migrations paths for a database configuration. If the
52
+ # +migrations_paths+ key is present in the config, +migrations_paths+
53
+ # will return its value.
54
+ def migrations_paths
55
+ config["migrations_paths"]
56
+ end
57
+
58
+ private
59
+
60
+ def build_url_hash(url)
61
+ if url.nil? || /^jdbc:/.match?(url)
62
+ { "url" => url }
63
+ else
64
+ ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(url).to_hash
65
+ end
66
+ end
67
+
68
+ def build_config(original_config, url)
69
+ hash = build_url_hash(url)
70
+
71
+ if original_config[env_name]
72
+ original_config[env_name].merge(hash)
73
+ else
74
+ original_config.merge(hash)
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end