activerecord 6.0.5.1 → 6.1.7.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (243) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1163 -774
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +2 -2
  5. data/lib/active_record/aggregations.rb +5 -5
  6. data/lib/active_record/association_relation.rb +30 -12
  7. data/lib/active_record/associations/alias_tracker.rb +19 -15
  8. data/lib/active_record/associations/association.rb +49 -26
  9. data/lib/active_record/associations/association_scope.rb +18 -20
  10. data/lib/active_record/associations/belongs_to_association.rb +23 -10
  11. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +8 -3
  12. data/lib/active_record/associations/builder/association.rb +32 -5
  13. data/lib/active_record/associations/builder/belongs_to.rb +10 -7
  14. data/lib/active_record/associations/builder/collection_association.rb +5 -4
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +0 -1
  16. data/lib/active_record/associations/builder/has_many.rb +6 -2
  17. data/lib/active_record/associations/builder/has_one.rb +11 -14
  18. data/lib/active_record/associations/builder/singular_association.rb +1 -1
  19. data/lib/active_record/associations/collection_association.rb +32 -18
  20. data/lib/active_record/associations/collection_proxy.rb +12 -5
  21. data/lib/active_record/associations/foreign_association.rb +13 -0
  22. data/lib/active_record/associations/has_many_association.rb +24 -2
  23. data/lib/active_record/associations/has_many_through_association.rb +10 -4
  24. data/lib/active_record/associations/has_one_association.rb +15 -1
  25. data/lib/active_record/associations/join_dependency/join_association.rb +37 -21
  26. data/lib/active_record/associations/join_dependency/join_part.rb +1 -1
  27. data/lib/active_record/associations/join_dependency.rb +63 -49
  28. data/lib/active_record/associations/preloader/association.rb +14 -8
  29. data/lib/active_record/associations/preloader/through_association.rb +1 -1
  30. data/lib/active_record/associations/preloader.rb +5 -3
  31. data/lib/active_record/associations/singular_association.rb +1 -1
  32. data/lib/active_record/associations.rb +118 -11
  33. data/lib/active_record/attribute_assignment.rb +10 -8
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +13 -9
  35. data/lib/active_record/attribute_methods/dirty.rb +1 -11
  36. data/lib/active_record/attribute_methods/primary_key.rb +6 -2
  37. data/lib/active_record/attribute_methods/query.rb +3 -6
  38. data/lib/active_record/attribute_methods/read.rb +8 -11
  39. data/lib/active_record/attribute_methods/serialization.rb +11 -5
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +12 -13
  41. data/lib/active_record/attribute_methods/write.rb +12 -20
  42. data/lib/active_record/attribute_methods.rb +64 -54
  43. data/lib/active_record/attributes.rb +33 -8
  44. data/lib/active_record/autosave_association.rb +47 -30
  45. data/lib/active_record/base.rb +2 -14
  46. data/lib/active_record/callbacks.rb +152 -22
  47. data/lib/active_record/coders/yaml_column.rb +16 -6
  48. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +185 -134
  49. data/lib/active_record/connection_adapters/abstract/database_limits.rb +2 -44
  50. data/lib/active_record/connection_adapters/abstract/database_statements.rb +66 -23
  51. data/lib/active_record/connection_adapters/abstract/query_cache.rb +3 -8
  52. data/lib/active_record/connection_adapters/abstract/quoting.rb +44 -35
  53. data/lib/active_record/connection_adapters/abstract/savepoints.rb +3 -3
  54. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +153 -116
  55. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +114 -26
  56. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +3 -3
  57. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +228 -83
  58. data/lib/active_record/connection_adapters/abstract/transaction.rb +92 -33
  59. data/lib/active_record/connection_adapters/abstract_adapter.rb +52 -76
  60. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +123 -87
  61. data/lib/active_record/connection_adapters/column.rb +15 -1
  62. data/lib/active_record/connection_adapters/deduplicable.rb +29 -0
  63. data/lib/active_record/connection_adapters/legacy_pool_manager.rb +35 -0
  64. data/lib/active_record/connection_adapters/mysql/database_statements.rb +24 -24
  65. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +1 -1
  66. data/lib/active_record/connection_adapters/mysql/quoting.rb +18 -3
  67. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +32 -6
  68. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +8 -0
  69. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +5 -2
  70. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +7 -4
  71. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +10 -1
  72. data/lib/active_record/connection_adapters/mysql2_adapter.rb +31 -12
  73. data/lib/active_record/connection_adapters/pool_config.rb +73 -0
  74. data/lib/active_record/connection_adapters/pool_manager.rb +47 -0
  75. data/lib/active_record/connection_adapters/postgresql/column.rb +24 -1
  76. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +14 -53
  77. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +3 -5
  78. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +2 -2
  79. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +2 -2
  80. data/lib/active_record/connection_adapters/postgresql/oid/interval.rb +49 -0
  81. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +2 -2
  82. data/lib/active_record/connection_adapters/postgresql/oid/macaddr.rb +25 -0
  83. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +2 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +11 -1
  85. data/lib/active_record/connection_adapters/postgresql/oid.rb +2 -0
  86. data/lib/active_record/connection_adapters/postgresql/quoting.rb +30 -4
  87. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  88. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +5 -1
  89. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +61 -29
  90. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +8 -0
  91. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -64
  92. data/lib/active_record/connection_adapters/schema_cache.rb +130 -15
  93. data/lib/active_record/connection_adapters/sql_type_metadata.rb +8 -0
  94. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +32 -5
  95. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +1 -1
  96. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +5 -1
  97. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +36 -3
  98. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +48 -50
  99. data/lib/active_record/connection_adapters.rb +52 -0
  100. data/lib/active_record/connection_handling.rb +218 -71
  101. data/lib/active_record/core.rb +265 -64
  102. data/lib/active_record/database_configurations/connection_url_resolver.rb +99 -0
  103. data/lib/active_record/database_configurations/database_config.rb +52 -9
  104. data/lib/active_record/database_configurations/hash_config.rb +54 -8
  105. data/lib/active_record/database_configurations/url_config.rb +15 -40
  106. data/lib/active_record/database_configurations.rb +125 -85
  107. data/lib/active_record/delegated_type.rb +209 -0
  108. data/lib/active_record/destroy_association_async_job.rb +36 -0
  109. data/lib/active_record/enum.rb +69 -34
  110. data/lib/active_record/errors.rb +47 -12
  111. data/lib/active_record/explain.rb +9 -4
  112. data/lib/active_record/explain_subscriber.rb +1 -1
  113. data/lib/active_record/fixture_set/file.rb +10 -17
  114. data/lib/active_record/fixture_set/model_metadata.rb +1 -2
  115. data/lib/active_record/fixture_set/render_context.rb +1 -1
  116. data/lib/active_record/fixture_set/table_row.rb +2 -2
  117. data/lib/active_record/fixtures.rb +58 -9
  118. data/lib/active_record/gem_version.rb +3 -3
  119. data/lib/active_record/inheritance.rb +40 -18
  120. data/lib/active_record/insert_all.rb +38 -5
  121. data/lib/active_record/integration.rb +3 -5
  122. data/lib/active_record/internal_metadata.rb +18 -7
  123. data/lib/active_record/legacy_yaml_adapter.rb +7 -3
  124. data/lib/active_record/locking/optimistic.rb +24 -17
  125. data/lib/active_record/locking/pessimistic.rb +6 -2
  126. data/lib/active_record/log_subscriber.rb +27 -8
  127. data/lib/active_record/middleware/database_selector/resolver/session.rb +3 -0
  128. data/lib/active_record/middleware/database_selector/resolver.rb +5 -0
  129. data/lib/active_record/middleware/database_selector.rb +4 -1
  130. data/lib/active_record/migration/command_recorder.rb +47 -27
  131. data/lib/active_record/migration/compatibility.rb +72 -18
  132. data/lib/active_record/migration.rb +114 -84
  133. data/lib/active_record/model_schema.rb +89 -14
  134. data/lib/active_record/nested_attributes.rb +2 -3
  135. data/lib/active_record/no_touching.rb +1 -1
  136. data/lib/active_record/persistence.rb +50 -45
  137. data/lib/active_record/query_cache.rb +15 -5
  138. data/lib/active_record/querying.rb +11 -6
  139. data/lib/active_record/railtie.rb +61 -59
  140. data/lib/active_record/railties/console_sandbox.rb +2 -4
  141. data/lib/active_record/railties/databases.rake +279 -101
  142. data/lib/active_record/readonly_attributes.rb +4 -0
  143. data/lib/active_record/reflection.rb +60 -44
  144. data/lib/active_record/relation/batches/batch_enumerator.rb +25 -9
  145. data/lib/active_record/relation/batches.rb +38 -31
  146. data/lib/active_record/relation/calculations.rb +104 -43
  147. data/lib/active_record/relation/finder_methods.rb +44 -14
  148. data/lib/active_record/relation/from_clause.rb +1 -1
  149. data/lib/active_record/relation/merger.rb +20 -23
  150. data/lib/active_record/relation/predicate_builder/array_handler.rb +8 -9
  151. data/lib/active_record/relation/predicate_builder/association_query_value.rb +4 -5
  152. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +10 -6
  153. data/lib/active_record/relation/predicate_builder/relation_handler.rb +1 -1
  154. data/lib/active_record/relation/predicate_builder.rb +61 -38
  155. data/lib/active_record/relation/query_methods.rb +324 -196
  156. data/lib/active_record/relation/record_fetch_warning.rb +3 -3
  157. data/lib/active_record/relation/spawn_methods.rb +8 -7
  158. data/lib/active_record/relation/where_clause.rb +111 -61
  159. data/lib/active_record/relation.rb +100 -81
  160. data/lib/active_record/result.rb +41 -33
  161. data/lib/active_record/runtime_registry.rb +2 -2
  162. data/lib/active_record/sanitization.rb +6 -17
  163. data/lib/active_record/schema_dumper.rb +34 -4
  164. data/lib/active_record/schema_migration.rb +2 -8
  165. data/lib/active_record/scoping/default.rb +1 -3
  166. data/lib/active_record/scoping/named.rb +1 -17
  167. data/lib/active_record/secure_token.rb +16 -8
  168. data/lib/active_record/serialization.rb +5 -3
  169. data/lib/active_record/signed_id.rb +116 -0
  170. data/lib/active_record/statement_cache.rb +20 -4
  171. data/lib/active_record/store.rb +8 -3
  172. data/lib/active_record/suppressor.rb +2 -2
  173. data/lib/active_record/table_metadata.rb +42 -51
  174. data/lib/active_record/tasks/database_tasks.rb +140 -113
  175. data/lib/active_record/tasks/mysql_database_tasks.rb +34 -35
  176. data/lib/active_record/tasks/postgresql_database_tasks.rb +24 -26
  177. data/lib/active_record/tasks/sqlite_database_tasks.rb +13 -9
  178. data/lib/active_record/test_databases.rb +5 -4
  179. data/lib/active_record/test_fixtures.rb +79 -31
  180. data/lib/active_record/timestamp.rb +4 -6
  181. data/lib/active_record/touch_later.rb +21 -21
  182. data/lib/active_record/transactions.rb +19 -66
  183. data/lib/active_record/type/serialized.rb +6 -2
  184. data/lib/active_record/type.rb +8 -1
  185. data/lib/active_record/type_caster/connection.rb +0 -1
  186. data/lib/active_record/type_caster/map.rb +8 -5
  187. data/lib/active_record/validations/associated.rb +1 -1
  188. data/lib/active_record/validations/numericality.rb +35 -0
  189. data/lib/active_record/validations/uniqueness.rb +24 -4
  190. data/lib/active_record/validations.rb +1 -0
  191. data/lib/active_record.rb +7 -14
  192. data/lib/arel/attributes/attribute.rb +4 -0
  193. data/lib/arel/collectors/bind.rb +5 -0
  194. data/lib/arel/collectors/composite.rb +8 -0
  195. data/lib/arel/collectors/sql_string.rb +7 -0
  196. data/lib/arel/collectors/substitute_binds.rb +7 -0
  197. data/lib/arel/nodes/binary.rb +82 -8
  198. data/lib/arel/nodes/bind_param.rb +8 -0
  199. data/lib/arel/nodes/casted.rb +21 -9
  200. data/lib/arel/nodes/equality.rb +6 -9
  201. data/lib/arel/nodes/grouping.rb +3 -0
  202. data/lib/arel/nodes/homogeneous_in.rb +76 -0
  203. data/lib/arel/nodes/in.rb +8 -1
  204. data/lib/arel/nodes/infix_operation.rb +13 -1
  205. data/lib/arel/nodes/join_source.rb +1 -1
  206. data/lib/arel/nodes/node.rb +7 -6
  207. data/lib/arel/nodes/ordering.rb +27 -0
  208. data/lib/arel/nodes/sql_literal.rb +3 -0
  209. data/lib/arel/nodes/table_alias.rb +7 -3
  210. data/lib/arel/nodes/unary.rb +0 -1
  211. data/lib/arel/nodes.rb +3 -1
  212. data/lib/arel/predications.rb +12 -18
  213. data/lib/arel/select_manager.rb +1 -2
  214. data/lib/arel/table.rb +13 -5
  215. data/lib/arel/visitors/dot.rb +14 -2
  216. data/lib/arel/visitors/mysql.rb +11 -1
  217. data/lib/arel/visitors/postgresql.rb +15 -4
  218. data/lib/arel/visitors/to_sql.rb +89 -78
  219. data/lib/arel/visitors.rb +0 -7
  220. data/lib/arel.rb +5 -13
  221. data/lib/rails/generators/active_record/migration/migration_generator.rb +1 -0
  222. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +2 -0
  223. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +3 -3
  224. data/lib/rails/generators/active_record/migration.rb +6 -1
  225. data/lib/rails/generators/active_record/model/model_generator.rb +39 -2
  226. data/lib/rails/generators/active_record/model/templates/abstract_base_class.rb.tt +7 -0
  227. metadata +24 -25
  228. data/lib/active_record/advisory_lock_base.rb +0 -18
  229. data/lib/active_record/attribute_decorators.rb +0 -88
  230. data/lib/active_record/connection_adapters/connection_specification.rb +0 -296
  231. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +0 -29
  232. data/lib/active_record/define_callbacks.rb +0 -22
  233. data/lib/active_record/railties/collection_cache_association_loading.rb +0 -34
  234. data/lib/active_record/relation/predicate_builder/base_handler.rb +0 -18
  235. data/lib/active_record/relation/where_clause_factory.rb +0 -33
  236. data/lib/arel/attributes.rb +0 -22
  237. data/lib/arel/visitors/depth_first.rb +0 -203
  238. data/lib/arel/visitors/ibm_db.rb +0 -34
  239. data/lib/arel/visitors/informix.rb +0 -62
  240. data/lib/arel/visitors/mssql.rb +0 -156
  241. data/lib/arel/visitors/oracle.rb +0 -158
  242. data/lib/arel/visitors/oracle12.rb +0 -65
  243. data/lib/arel/visitors/where_sql.rb +0 -22
@@ -12,12 +12,12 @@ module ActiveRecord
12
12
  # Becomes:
13
13
  #
14
14
  # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10
15
- # @env_name="development", @spec_name="primary", @config={"database"=>"db_name"}>
15
+ # @env_name="development", @name="primary", @config={database: "db_name"}>
16
16
  #
17
17
  # ==== Options
18
18
  #
19
19
  # * <tt>:env_name</tt> - The Rails environment, i.e. "development".
20
- # * <tt>:spec_name</tt> - The specification name. In a standard two-tier
20
+ # * <tt>:name</tt> - The db config name. In a standard two-tier
21
21
  # database configuration this will default to "primary". In a multiple
22
22
  # database three-tier database configuration this corresponds to the name
23
23
  # used in the second tier, for example "primary_readonly".
@@ -25,25 +25,71 @@ module ActiveRecord
25
25
  # database adapter, name, and other important information for database
26
26
  # connections.
27
27
  class HashConfig < DatabaseConfig
28
- attr_reader :config
28
+ attr_reader :configuration_hash
29
+ def initialize(env_name, name, configuration_hash)
30
+ super(env_name, name)
31
+ @configuration_hash = configuration_hash.symbolize_keys.freeze
32
+ end
29
33
 
30
- def initialize(env_name, spec_name, config)
31
- super(env_name, spec_name)
32
- @config = config
34
+ def config
35
+ ActiveSupport::Deprecation.warn("DatabaseConfig#config will be removed in 7.0.0 in favor of DatabaseConfig#configuration_hash which returns a hash with symbol keys")
36
+ configuration_hash.stringify_keys
33
37
  end
34
38
 
35
39
  # Determines whether a database configuration is for a replica / readonly
36
40
  # connection. If the +replica+ key is present in the config, +replica?+ will
37
41
  # return +true+.
38
42
  def replica?
39
- config["replica"]
43
+ configuration_hash[:replica]
40
44
  end
41
45
 
42
46
  # The migrations paths for a database configuration. If the
43
47
  # +migrations_paths+ key is present in the config, +migrations_paths+
44
48
  # will return its value.
45
49
  def migrations_paths
46
- config["migrations_paths"]
50
+ configuration_hash[:migrations_paths]
51
+ end
52
+
53
+ def host
54
+ configuration_hash[:host]
55
+ end
56
+
57
+ def database
58
+ configuration_hash[:database]
59
+ end
60
+
61
+ def _database=(database) # :nodoc:
62
+ @configuration_hash = configuration_hash.merge(database: database).freeze
63
+ end
64
+
65
+ def pool
66
+ (configuration_hash[:pool] || 5).to_i
67
+ end
68
+
69
+ def checkout_timeout
70
+ (configuration_hash[:checkout_timeout] || 5).to_f
71
+ end
72
+
73
+ # +reaping_frequency+ is configurable mostly for historical reasons, but it could
74
+ # also be useful if someone wants a very low +idle_timeout+.
75
+ def reaping_frequency
76
+ configuration_hash.fetch(:reaping_frequency, 60)&.to_f
77
+ end
78
+
79
+ def idle_timeout
80
+ timeout = configuration_hash.fetch(:idle_timeout, 300).to_f
81
+ timeout if timeout > 0
82
+ end
83
+
84
+ def adapter
85
+ configuration_hash[:adapter]
86
+ end
87
+
88
+ # The path to the schema cache dump file for a database.
89
+ # If omitted, the filename will be read from ENV or a
90
+ # default will be derived.
91
+ def schema_cache_path
92
+ configuration_hash[:schema_cache_path]
47
93
  end
48
94
  end
49
95
  end
@@ -13,14 +13,14 @@ module ActiveRecord
13
13
  # Becomes:
14
14
  #
15
15
  # #<ActiveRecord::DatabaseConfigurations::UrlConfig:0x00007fdc3238f340
16
- # @env_name="default_env", @spec_name="primary",
17
- # @config={"adapter"=>"postgresql", "database"=>"foo", "host"=>"localhost"},
16
+ # @env_name="default_env", @name="primary",
17
+ # @config={adapter: "postgresql", database: "foo", host: "localhost"},
18
18
  # @url="postgres://localhost/foo">
19
19
  #
20
20
  # ==== Options
21
21
  #
22
22
  # * <tt>:env_name</tt> - The Rails environment, ie "development".
23
- # * <tt>:spec_name</tt> - The specification name. In a standard two-tier
23
+ # * <tt>:name</tt> - The db config name. In a standard two-tier
24
24
  # database configuration this will default to "primary". In a multiple
25
25
  # database three-tier database configuration this corresponds to the name
26
26
  # used in the second tier, for example "primary_readonly".
@@ -28,49 +28,24 @@ module ActiveRecord
28
28
  # * <tt>:config</tt> - The config hash. This is the hash that contains the
29
29
  # database adapter, name, and other important information for database
30
30
  # connections.
31
- class UrlConfig < DatabaseConfig
32
- attr_reader :url, :config
31
+ class UrlConfig < HashConfig
32
+ attr_reader :url
33
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
34
+ def initialize(env_name, name, url, configuration_hash = {})
35
+ super(env_name, name, configuration_hash)
50
36
 
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"]
37
+ @url = url
38
+ @configuration_hash = @configuration_hash.merge(build_url_hash).freeze
56
39
  end
57
40
 
58
41
  private
59
- def build_url_hash(url)
60
- if url.nil? || /^jdbc:/.match?(url)
61
- { "url" => url }
62
- else
63
- ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(url).to_hash
64
- end
65
- end
66
-
67
- def build_config(original_config, url)
68
- hash = build_url_hash(url)
69
-
70
- if original_config[env_name]
71
- original_config[env_name].merge(hash)
42
+ # Return a Hash that can be merged into the main config that represents
43
+ # the passed in url
44
+ def build_url_hash
45
+ if url.nil? || %w(jdbc: http: https:).any? { |protocol| url.start_with?(protocol) }
46
+ { url: url }
72
47
  else
73
- original_config.merge(hash)
48
+ ConnectionUrlResolver.new(url).to_hash
74
49
  end
75
50
  end
76
51
  end
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "uri"
3
4
  require "active_record/database_configurations/database_config"
4
5
  require "active_record/database_configurations/hash_config"
5
6
  require "active_record/database_configurations/url_config"
7
+ require "active_record/database_configurations/connection_url_resolver"
6
8
 
7
9
  module ActiveRecord
8
10
  # ActiveRecord::DatabaseConfigurations returns an array of DatabaseConfig
@@ -21,7 +23,7 @@ module ActiveRecord
21
23
  # Collects the configs for the environment and optionally the specification
22
24
  # name passed in. To include replica configurations pass <tt>include_replicas: true</tt>.
23
25
  #
24
- # If a spec name is provided a single DatabaseConfig object will be
26
+ # If a name is provided a single DatabaseConfig object will be
25
27
  # returned, otherwise an array of DatabaseConfig objects will be
26
28
  # returned that corresponds with the environment and type requested.
27
29
  #
@@ -29,13 +31,20 @@ module ActiveRecord
29
31
  #
30
32
  # * <tt>env_name:</tt> The environment name. Defaults to +nil+ which will collect
31
33
  # configs for all environments.
32
- # * <tt>spec_name:</tt> The specification name (i.e. primary, animals, etc.). Defaults
33
- # to +nil+.
34
+ # * <tt>name:</tt> The db config name (i.e. primary, animals, etc.). Defaults
35
+ # to +nil+. If no +env_name+ is specified the config for the default env and the
36
+ # passed +name+ will be returned.
34
37
  # * <tt>include_replicas:</tt> Determines whether to include replicas in
35
38
  # the returned list. Most of the time we're only iterating over the write
36
39
  # connection (i.e. migrations don't need to run for the write and read connection).
37
40
  # Defaults to +false+.
38
- def configs_for(env_name: nil, spec_name: nil, include_replicas: false)
41
+ def configs_for(env_name: nil, spec_name: nil, name: nil, include_replicas: false)
42
+ if spec_name
43
+ name = spec_name
44
+ ActiveSupport::Deprecation.warn("The kwarg `spec_name` is deprecated in favor of `name`. `spec_name` will be removed in Rails 7.0")
45
+ end
46
+
47
+ env_name ||= default_env if name
39
48
  configs = env_with_configs(env_name)
40
49
 
41
50
  unless include_replicas
@@ -44,9 +53,9 @@ module ActiveRecord
44
53
  end
45
54
  end
46
55
 
47
- if spec_name
56
+ if name
48
57
  configs.find do |db_config|
49
- db_config.spec_name == spec_name
58
+ db_config.name == name
50
59
  end
51
60
  else
52
61
  configs
@@ -59,31 +68,46 @@ module ActiveRecord
59
68
  # return the first config hash for the environment.
60
69
  #
61
70
  # { database: "my_db", adapter: "mysql2" }
62
- def default_hash(env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s)
71
+ def default_hash(env = default_env)
63
72
  default = find_db_config(env)
64
- default.config if default
73
+ default.configuration_hash if default
65
74
  end
66
75
  alias :[] :default_hash
76
+ deprecate "[]": "Use configs_for", default_hash: "Use configs_for"
67
77
 
68
78
  # Returns a single DatabaseConfig object based on the requested environment.
69
79
  #
70
80
  # If the application has multiple databases +find_db_config+ will return
71
81
  # the first DatabaseConfig for the environment.
72
82
  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
83
+ configurations
84
+ .sort_by.with_index { |db_config, i| db_config.for_current_env? ? [0, i] : [1, i] }
85
+ .find do |db_config|
86
+ db_config.env_name == env.to_s ||
87
+ (db_config.for_current_env? && db_config.name == env.to_s)
88
+ end
89
+ end
90
+
91
+ # A primary configuration is one that is named primary or if there is
92
+ # no primary, the first configuration for an environment will be treated
93
+ # as primary. This is used as the "default" configuration and is used
94
+ # when the application needs to treat one configuration differently. For
95
+ # example, when Rails dumps the schema, the primary configuration's schema
96
+ # file will be named `schema.rb` instead of `primary_schema.rb`.
97
+ def primary?(name) # :nodoc:
98
+ return true if name == "primary"
99
+
100
+ first_config = find_db_config(default_env)
101
+ first_config && name == first_config.name
77
102
  end
78
103
 
79
104
  # Returns the DatabaseConfigurations object as a Hash.
80
105
  def to_h
81
- configs = configurations.reverse.inject({}) do |memo, db_config|
82
- memo.merge(db_config.to_legacy_hash)
106
+ configurations.inject({}) do |memo, db_config|
107
+ memo.merge(db_config.env_name => db_config.configuration_hash.stringify_keys)
83
108
  end
84
-
85
- Hash[configs.to_a.reverse]
86
109
  end
110
+ deprecate to_h: "You can use `ActiveRecord::Base.configurations.configs_for(env_name: 'env', name: 'primary').configuration_hash` to get the configuration hashes."
87
111
 
88
112
  # Checks if the application's configurations are empty.
89
113
  #
@@ -93,20 +117,43 @@ module ActiveRecord
93
117
  end
94
118
  alias :blank? :empty?
95
119
 
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]
120
+ # Returns fully resolved connection, accepts hash, string or symbol.
121
+ # Always returns a DatabaseConfiguration::DatabaseConfig
122
+ #
123
+ # == Examples
124
+ #
125
+ # Symbol representing current environment.
126
+ #
127
+ # DatabaseConfigurations.new("production" => {}).resolve(:production)
128
+ # # => DatabaseConfigurations::HashConfig.new(env_name: "production", config: {})
129
+ #
130
+ # One layer deep hash of connection values.
131
+ #
132
+ # DatabaseConfigurations.new({}).resolve("adapter" => "sqlite3")
133
+ # # => DatabaseConfigurations::HashConfig.new(config: {"adapter" => "sqlite3"})
134
+ #
135
+ # Connection URL.
136
+ #
137
+ # DatabaseConfigurations.new({}).resolve("postgresql://localhost/foo")
138
+ # # => DatabaseConfigurations::UrlConfig.new(config: {"adapter" => "postgresql", "host" => "localhost", "database" => "foo"})
139
+ def resolve(config) # :nodoc:
140
+ return config if DatabaseConfigurations::DatabaseConfig === config
141
+
142
+ case config
143
+ when Symbol
144
+ resolve_symbol_connection(config)
145
+ when Hash, String
146
+ build_db_config_from_raw_config(default_env, "primary", config)
147
+ else
148
+ raise TypeError, "Invalid type for configuration. Expected Symbol, String, or Hash. Got #{config.inspect}"
149
+ end
107
150
  end
108
151
 
109
152
  private
153
+ def default_env
154
+ ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s
155
+ end
156
+
110
157
  def env_with_configs(env = nil)
111
158
  if env
112
159
  configurations.select { |db_config| db_config.env_name == env }
@@ -127,107 +174,100 @@ module ActiveRecord
127
174
  end
128
175
  end
129
176
 
130
- current_env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s
131
-
132
177
  unless db_configs.find(&:for_current_env?)
133
- db_configs << environment_url_config(current_env, "primary", {})
178
+ db_configs << environment_url_config(default_env, "primary", {})
134
179
  end
135
180
 
136
- merge_db_environment_variables(current_env, db_configs.compact)
181
+ merge_db_environment_variables(default_env, db_configs.compact)
137
182
  end
138
183
 
139
184
  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)
185
+ config.map do |name, sub_config|
186
+ build_db_config_from_raw_config(env_name, name.to_s, sub_config)
142
187
  end
143
188
  end
144
189
 
145
- def build_db_config_from_raw_config(env_name, spec_name, config)
190
+ def resolve_symbol_connection(name)
191
+ if db_config = find_db_config(name)
192
+ db_config
193
+ else
194
+ raise AdapterNotSpecified, <<~MSG
195
+ The `#{name}` database is not configured for the `#{default_env}` environment.
196
+
197
+ Available databases configurations are:
198
+
199
+ #{build_configuration_sentence}
200
+ MSG
201
+ end
202
+ end
203
+
204
+ def build_configuration_sentence
205
+ configs = configs_for(include_replicas: true)
206
+
207
+ configs.group_by(&:env_name).map do |env, config|
208
+ names = config.map(&:name)
209
+ if names.size > 1
210
+ "#{env}: #{names.join(", ")}"
211
+ else
212
+ env
213
+ end
214
+ end.join("\n")
215
+ end
216
+
217
+ def build_db_config_from_raw_config(env_name, name, config)
146
218
  case config
147
219
  when String
148
- build_db_config_from_string(env_name, spec_name, config)
220
+ build_db_config_from_string(env_name, name, config)
149
221
  when Hash
150
- build_db_config_from_hash(env_name, spec_name, config.stringify_keys)
222
+ build_db_config_from_hash(env_name, name, config.symbolize_keys)
151
223
  else
152
224
  raise InvalidConfigurationError, "'{ #{env_name} => #{config} }' is not a valid configuration. Expected '#{config}' to be a URL string or a Hash."
153
225
  end
154
226
  end
155
227
 
156
- def build_db_config_from_string(env_name, spec_name, config)
228
+ def build_db_config_from_string(env_name, name, config)
157
229
  url = config
158
230
  uri = URI.parse(url)
159
231
  if uri.scheme
160
- ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url)
232
+ UrlConfig.new(env_name, name, url)
161
233
  else
162
234
  raise InvalidConfigurationError, "'{ #{env_name} => #{config} }' is not a valid configuration. Expected '#{config}' to be a URL string or a Hash."
163
235
  end
164
236
  end
165
237
 
166
- def build_db_config_from_hash(env_name, spec_name, config)
167
- if config.has_key?("url")
168
- url = config["url"]
238
+ def build_db_config_from_hash(env_name, name, config)
239
+ if config.has_key?(:url)
240
+ url = config[:url]
169
241
  config_without_url = config.dup
170
- config_without_url.delete "url"
242
+ config_without_url.delete :url
171
243
 
172
- ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url, config_without_url)
244
+ UrlConfig.new(env_name, name, url, config_without_url)
173
245
  else
174
- ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
246
+ HashConfig.new(env_name, name, config)
175
247
  end
176
248
  end
177
249
 
178
250
  def merge_db_environment_variables(current_env, configs)
179
251
  configs.map do |config|
180
- next config if config.url_config? || config.env_name != current_env
252
+ next config if config.is_a?(UrlConfig) || config.env_name != current_env
181
253
 
182
- url_config = environment_url_config(current_env, config.spec_name, config.config)
254
+ url_config = environment_url_config(current_env, config.name, config.configuration_hash)
183
255
  url_config || config
184
256
  end
185
257
  end
186
258
 
187
- def environment_url_config(env, spec_name, config)
188
- url = environment_value_for(spec_name)
259
+ def environment_url_config(env, name, config)
260
+ url = environment_value_for(name)
189
261
  return unless url
190
262
 
191
- ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, spec_name, url, config)
263
+ UrlConfig.new(env, name, url, config)
192
264
  end
193
265
 
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"
266
+ def environment_value_for(name)
267
+ name_env_key = "#{name.upcase}_DATABASE_URL"
268
+ url = ENV[name_env_key]
269
+ url ||= ENV["DATABASE_URL"] if name == "primary"
198
270
  url
199
271
  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
272
  end
233
273
  end