activerecord 5.2.4.5 → 6.0.0.beta1

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 (241) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -739
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +2 -1
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/associations.rb +16 -12
  9. data/lib/active_record/associations/association.rb +35 -19
  10. data/lib/active_record/associations/association_scope.rb +4 -6
  11. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +14 -50
  14. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  16. data/lib/active_record/associations/collection_association.rb +11 -25
  17. data/lib/active_record/associations/collection_proxy.rb +32 -6
  18. data/lib/active_record/associations/foreign_association.rb +7 -0
  19. data/lib/active_record/associations/has_many_association.rb +1 -1
  20. data/lib/active_record/associations/has_many_through_association.rb +25 -18
  21. data/lib/active_record/associations/has_one_association.rb +28 -30
  22. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  23. data/lib/active_record/associations/join_dependency.rb +15 -20
  24. data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
  25. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  26. data/lib/active_record/associations/preloader.rb +32 -29
  27. data/lib/active_record/associations/preloader/association.rb +1 -2
  28. data/lib/active_record/associations/singular_association.rb +2 -16
  29. data/lib/active_record/attribute_assignment.rb +7 -10
  30. data/lib/active_record/attribute_methods.rb +34 -56
  31. data/lib/active_record/attribute_methods/dirty.rb +64 -26
  32. data/lib/active_record/attribute_methods/primary_key.rb +8 -7
  33. data/lib/active_record/attribute_methods/read.rb +16 -48
  34. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  36. data/lib/active_record/attribute_methods/write.rb +15 -16
  37. data/lib/active_record/autosave_association.rb +7 -21
  38. data/lib/active_record/base.rb +2 -2
  39. data/lib/active_record/callbacks.rb +3 -17
  40. data/lib/active_record/collection_cache_key.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
  42. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
  53. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  54. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
  55. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
  56. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  57. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  58. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
  60. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  64. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  65. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  67. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  69. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  70. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
  71. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
  73. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
  75. data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
  79. data/lib/active_record/connection_handling.rb +132 -26
  80. data/lib/active_record/core.rb +76 -43
  81. data/lib/active_record/counter_cache.rb +4 -29
  82. data/lib/active_record/database_configurations.rb +184 -0
  83. data/lib/active_record/database_configurations/database_config.rb +37 -0
  84. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  85. data/lib/active_record/database_configurations/url_config.rb +74 -0
  86. data/lib/active_record/enum.rb +22 -7
  87. data/lib/active_record/errors.rb +24 -21
  88. data/lib/active_record/explain.rb +1 -1
  89. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  90. data/lib/active_record/fixture_set/render_context.rb +17 -0
  91. data/lib/active_record/fixture_set/table_row.rb +153 -0
  92. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  93. data/lib/active_record/fixtures.rb +140 -472
  94. data/lib/active_record/gem_version.rb +4 -4
  95. data/lib/active_record/inheritance.rb +12 -2
  96. data/lib/active_record/integration.rb +56 -16
  97. data/lib/active_record/internal_metadata.rb +5 -1
  98. data/lib/active_record/locking/optimistic.rb +2 -2
  99. data/lib/active_record/locking/pessimistic.rb +3 -3
  100. data/lib/active_record/log_subscriber.rb +7 -26
  101. data/lib/active_record/migration.rb +38 -37
  102. data/lib/active_record/migration/command_recorder.rb +35 -5
  103. data/lib/active_record/migration/compatibility.rb +34 -16
  104. data/lib/active_record/model_schema.rb +30 -9
  105. data/lib/active_record/nested_attributes.rb +2 -2
  106. data/lib/active_record/no_touching.rb +7 -0
  107. data/lib/active_record/persistence.rb +18 -7
  108. data/lib/active_record/query_cache.rb +11 -4
  109. data/lib/active_record/querying.rb +19 -11
  110. data/lib/active_record/railtie.rb +71 -42
  111. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  112. data/lib/active_record/railties/controller_runtime.rb +30 -35
  113. data/lib/active_record/railties/databases.rake +94 -43
  114. data/lib/active_record/reflection.rb +60 -44
  115. data/lib/active_record/relation.rb +150 -69
  116. data/lib/active_record/relation/batches.rb +13 -10
  117. data/lib/active_record/relation/calculations.rb +38 -28
  118. data/lib/active_record/relation/delegation.rb +4 -13
  119. data/lib/active_record/relation/finder_methods.rb +12 -25
  120. data/lib/active_record/relation/merger.rb +2 -6
  121. data/lib/active_record/relation/predicate_builder.rb +4 -6
  122. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  123. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  124. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  125. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  126. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  127. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  128. data/lib/active_record/relation/query_attribute.rb +15 -12
  129. data/lib/active_record/relation/query_methods.rb +29 -52
  130. data/lib/active_record/relation/where_clause.rb +4 -0
  131. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  132. data/lib/active_record/result.rb +30 -11
  133. data/lib/active_record/sanitization.rb +2 -39
  134. data/lib/active_record/schema.rb +1 -10
  135. data/lib/active_record/schema_dumper.rb +12 -6
  136. data/lib/active_record/schema_migration.rb +4 -0
  137. data/lib/active_record/scoping.rb +9 -8
  138. data/lib/active_record/scoping/default.rb +10 -3
  139. data/lib/active_record/scoping/named.rb +10 -14
  140. data/lib/active_record/statement_cache.rb +32 -5
  141. data/lib/active_record/store.rb +39 -8
  142. data/lib/active_record/table_metadata.rb +1 -4
  143. data/lib/active_record/tasks/database_tasks.rb +89 -23
  144. data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
  145. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  146. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  147. data/lib/active_record/test_databases.rb +38 -0
  148. data/lib/active_record/test_fixtures.rb +224 -0
  149. data/lib/active_record/timestamp.rb +4 -6
  150. data/lib/active_record/transactions.rb +3 -22
  151. data/lib/active_record/translation.rb +1 -1
  152. data/lib/active_record/type.rb +3 -4
  153. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  154. data/lib/active_record/type_caster/connection.rb +1 -6
  155. data/lib/active_record/type_caster/map.rb +1 -4
  156. data/lib/active_record/validations/uniqueness.rb +13 -25
  157. data/lib/arel.rb +44 -0
  158. data/lib/arel/alias_predication.rb +9 -0
  159. data/lib/arel/attributes.rb +22 -0
  160. data/lib/arel/attributes/attribute.rb +37 -0
  161. data/lib/arel/collectors/bind.rb +24 -0
  162. data/lib/arel/collectors/composite.rb +31 -0
  163. data/lib/arel/collectors/plain_string.rb +20 -0
  164. data/lib/arel/collectors/sql_string.rb +20 -0
  165. data/lib/arel/collectors/substitute_binds.rb +28 -0
  166. data/lib/arel/crud.rb +42 -0
  167. data/lib/arel/delete_manager.rb +18 -0
  168. data/lib/arel/errors.rb +9 -0
  169. data/lib/arel/expressions.rb +29 -0
  170. data/lib/arel/factory_methods.rb +49 -0
  171. data/lib/arel/insert_manager.rb +49 -0
  172. data/lib/arel/math.rb +45 -0
  173. data/lib/arel/nodes.rb +67 -0
  174. data/lib/arel/nodes/and.rb +32 -0
  175. data/lib/arel/nodes/ascending.rb +23 -0
  176. data/lib/arel/nodes/binary.rb +52 -0
  177. data/lib/arel/nodes/bind_param.rb +36 -0
  178. data/lib/arel/nodes/case.rb +55 -0
  179. data/lib/arel/nodes/casted.rb +50 -0
  180. data/lib/arel/nodes/count.rb +12 -0
  181. data/lib/arel/nodes/delete_statement.rb +45 -0
  182. data/lib/arel/nodes/descending.rb +23 -0
  183. data/lib/arel/nodes/equality.rb +18 -0
  184. data/lib/arel/nodes/extract.rb +24 -0
  185. data/lib/arel/nodes/false.rb +16 -0
  186. data/lib/arel/nodes/full_outer_join.rb +8 -0
  187. data/lib/arel/nodes/function.rb +44 -0
  188. data/lib/arel/nodes/grouping.rb +8 -0
  189. data/lib/arel/nodes/in.rb +8 -0
  190. data/lib/arel/nodes/infix_operation.rb +80 -0
  191. data/lib/arel/nodes/inner_join.rb +8 -0
  192. data/lib/arel/nodes/insert_statement.rb +37 -0
  193. data/lib/arel/nodes/join_source.rb +20 -0
  194. data/lib/arel/nodes/matches.rb +18 -0
  195. data/lib/arel/nodes/named_function.rb +23 -0
  196. data/lib/arel/nodes/node.rb +50 -0
  197. data/lib/arel/nodes/node_expression.rb +13 -0
  198. data/lib/arel/nodes/outer_join.rb +8 -0
  199. data/lib/arel/nodes/over.rb +15 -0
  200. data/lib/arel/nodes/regexp.rb +16 -0
  201. data/lib/arel/nodes/right_outer_join.rb +8 -0
  202. data/lib/arel/nodes/select_core.rb +63 -0
  203. data/lib/arel/nodes/select_statement.rb +41 -0
  204. data/lib/arel/nodes/sql_literal.rb +16 -0
  205. data/lib/arel/nodes/string_join.rb +11 -0
  206. data/lib/arel/nodes/table_alias.rb +27 -0
  207. data/lib/arel/nodes/terminal.rb +16 -0
  208. data/lib/arel/nodes/true.rb +16 -0
  209. data/lib/arel/nodes/unary.rb +44 -0
  210. data/lib/arel/nodes/unary_operation.rb +20 -0
  211. data/lib/arel/nodes/unqualified_column.rb +22 -0
  212. data/lib/arel/nodes/update_statement.rb +41 -0
  213. data/lib/arel/nodes/values.rb +16 -0
  214. data/lib/arel/nodes/values_list.rb +24 -0
  215. data/lib/arel/nodes/window.rb +126 -0
  216. data/lib/arel/nodes/with.rb +11 -0
  217. data/lib/arel/order_predications.rb +13 -0
  218. data/lib/arel/predications.rb +257 -0
  219. data/lib/arel/select_manager.rb +271 -0
  220. data/lib/arel/table.rb +110 -0
  221. data/lib/arel/tree_manager.rb +72 -0
  222. data/lib/arel/update_manager.rb +34 -0
  223. data/lib/arel/visitors.rb +20 -0
  224. data/lib/arel/visitors/depth_first.rb +199 -0
  225. data/lib/arel/visitors/dot.rb +292 -0
  226. data/lib/arel/visitors/ibm_db.rb +21 -0
  227. data/lib/arel/visitors/informix.rb +56 -0
  228. data/lib/arel/visitors/mssql.rb +143 -0
  229. data/lib/arel/visitors/mysql.rb +83 -0
  230. data/lib/arel/visitors/oracle.rb +159 -0
  231. data/lib/arel/visitors/oracle12.rb +67 -0
  232. data/lib/arel/visitors/postgresql.rb +116 -0
  233. data/lib/arel/visitors/sqlite.rb +39 -0
  234. data/lib/arel/visitors/to_sql.rb +913 -0
  235. data/lib/arel/visitors/visitor.rb +42 -0
  236. data/lib/arel/visitors/where_sql.rb +23 -0
  237. data/lib/arel/window_predications.rb +9 -0
  238. data/lib/rails/generators/active_record/migration.rb +14 -1
  239. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  240. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  241. metadata +104 -26
@@ -2,6 +2,7 @@
2
2
 
3
3
  require "active_support/core_ext/hash/indifferent_access"
4
4
  require "active_support/core_ext/string/filters"
5
+ require "active_support/parameter_filter"
5
6
  require "concurrent/map"
6
7
 
7
8
  module ActiveRecord
@@ -26,7 +27,7 @@ module ActiveRecord
26
27
 
27
28
  ##
28
29
  # Contains the database configuration - as is typically stored in config/database.yml -
29
- # as a Hash.
30
+ # as an ActiveRecord::DatabaseConfigurations object.
30
31
  #
31
32
  # For example, the following database.yml...
32
33
  #
@@ -40,22 +41,18 @@ module ActiveRecord
40
41
  #
41
42
  # ...would result in ActiveRecord::Base.configurations to look like this:
42
43
  #
43
- # {
44
- # 'development' => {
45
- # 'adapter' => 'sqlite3',
46
- # 'database' => 'db/development.sqlite3'
47
- # },
48
- # 'production' => {
49
- # 'adapter' => 'sqlite3',
50
- # 'database' => 'db/production.sqlite3'
51
- # }
52
- # }
44
+ # #<ActiveRecord::DatabaseConfigurations:0x00007fd1acbdf800 @configurations=[
45
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbded10 @env_name="development",
46
+ # @spec_name="primary", @config={"adapter"=>"sqlite3", "database"=>"db/development.sqlite3"}>,
47
+ # #<ActiveRecord::DatabaseConfigurations::HashConfig:0x00007fd1acbdea90 @env_name="production",
48
+ # @spec_name="primary", @config={"adapter"=>"mysql2", "database"=>"db/production.sqlite3"}>
49
+ # ]>
53
50
  def self.configurations=(config)
54
- @@configurations = ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig.new(config).resolve
51
+ @@configurations = ActiveRecord::DatabaseConfigurations.new(config)
55
52
  end
56
53
  self.configurations = {}
57
54
 
58
- # Returns fully resolved configurations hash
55
+ # Returns fully resolved ActiveRecord::DatabaseConfigurations object
59
56
  def self.configurations
60
57
  @@configurations
61
58
  end
@@ -99,7 +96,7 @@ module ActiveRecord
99
96
  ##
100
97
  # :singleton-method:
101
98
  # Specify whether schema dump should happen at the end of the
102
- # db:migrate rake task. This is true by default, which is useful for the
99
+ # db:migrate rails command. This is true by default, which is useful for the
103
100
  # development environment. This should ideally be false in the production
104
101
  # environment where dumping schema is rarely needed.
105
102
  mattr_accessor :dump_schema_after_migration, instance_writer: false, default: true
@@ -125,25 +122,25 @@ module ActiveRecord
125
122
 
126
123
  mattr_accessor :belongs_to_required_by_default, instance_accessor: false
127
124
 
125
+ mattr_accessor :connection_handlers, instance_accessor: false, default: {}
126
+
128
127
  class_attribute :default_connection_handler, instance_writer: false
129
128
 
129
+ self.filter_attributes = []
130
+
130
131
  def self.connection_handler
131
- ActiveRecord::RuntimeRegistry.connection_handler || default_connection_handler
132
+ Thread.current.thread_variable_get("ar_connection_handler") || default_connection_handler
132
133
  end
133
134
 
134
135
  def self.connection_handler=(handler)
135
- ActiveRecord::RuntimeRegistry.connection_handler = handler
136
+ Thread.current.thread_variable_set("ar_connection_handler", handler)
136
137
  end
137
138
 
138
139
  self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
140
+ self.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler }
139
141
  end
140
142
 
141
- module ClassMethods # :nodoc:
142
- def allocate
143
- define_attribute_methods
144
- super
145
- end
146
-
143
+ module ClassMethods
147
144
  def initialize_find_by_cache # :nodoc:
148
145
  @find_by_statement_cache = { true => Concurrent::Map.new, false => Concurrent::Map.new }
149
146
  end
@@ -172,20 +169,16 @@ module ActiveRecord
172
169
  where(key => params.bind).limit(1)
173
170
  }
174
171
 
175
- record = statement.execute([id], connection).first
172
+ record = statement.execute([id], connection)&.first
176
173
  unless record
177
174
  raise RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}",
178
175
  name, primary_key, id)
179
176
  end
180
177
  record
181
- rescue ::RangeError
182
- raise RecordNotFound.new("Couldn't find #{name} with an out of range value for '#{primary_key}'",
183
- name, primary_key)
184
178
  end
185
179
 
186
180
  def find_by(*args) # :nodoc:
187
- return super if scope_attributes? || reflect_on_all_aggregations.any? ||
188
- columns_hash.key?(inheritance_column) && base_class != self
181
+ return super if scope_attributes? || reflect_on_all_aggregations.any?
189
182
 
190
183
  hash = args.first
191
184
 
@@ -205,11 +198,9 @@ module ActiveRecord
205
198
  where(wheres).limit(1)
206
199
  }
207
200
  begin
208
- statement.execute(hash.values, connection).first
201
+ statement.execute(hash.values, connection)&.first
209
202
  rescue TypeError
210
203
  raise ActiveRecord::StatementInvalid
211
- rescue ::RangeError
212
- nil
213
204
  end
214
205
  end
215
206
 
@@ -221,7 +212,7 @@ module ActiveRecord
221
212
  generated_association_methods
222
213
  end
223
214
 
224
- def generated_association_methods
215
+ def generated_association_methods # :nodoc:
225
216
  @generated_association_methods ||= begin
226
217
  mod = const_set(:GeneratedAssociationMethods, Module.new)
227
218
  private_constant :GeneratedAssociationMethods
@@ -231,8 +222,20 @@ module ActiveRecord
231
222
  end
232
223
  end
233
224
 
225
+ # Returns columns which shouldn't be exposed while calling +#inspect+.
226
+ def filter_attributes
227
+ if defined?(@filter_attributes)
228
+ @filter_attributes
229
+ else
230
+ superclass.filter_attributes
231
+ end
232
+ end
233
+
234
+ # Specifies columns which shouldn't be exposed while calling +#inspect+.
235
+ attr_writer :filter_attributes
236
+
234
237
  # Returns a string like 'Post(id:integer, title:string, body:text)'
235
- def inspect
238
+ def inspect # :nodoc:
236
239
  if self == Base
237
240
  super
238
241
  elsif abstract_class?
@@ -248,7 +251,7 @@ module ActiveRecord
248
251
  end
249
252
 
250
253
  # Overwrite the default class equality method to provide support for decorated models.
251
- def ===(object)
254
+ def ===(object) # :nodoc:
252
255
  object.is_a?(self)
253
256
  end
254
257
 
@@ -274,6 +277,10 @@ module ActiveRecord
274
277
  TypeCaster::Map.new(self)
275
278
  end
276
279
 
280
+ def _internal? # :nodoc:
281
+ false
282
+ end
283
+
277
284
  private
278
285
 
279
286
  def cached_find_by_statement(key, &block)
@@ -332,13 +339,21 @@ module ActiveRecord
332
339
  # post = Post.allocate
333
340
  # post.init_with(coder)
334
341
  # post.title # => 'hello world'
335
- def init_with(coder)
342
+ def init_with(coder, &block)
336
343
  coder = LegacyYamlAdapter.convert(self.class, coder)
337
- @attributes = self.class.yaml_encoder.decode(coder)
344
+ attributes = self.class.yaml_encoder.decode(coder)
345
+ init_with_attributes(attributes, coder["new_record"], &block)
346
+ end
338
347
 
348
+ ##
349
+ # Initialize an empty model object from +attributes+.
350
+ # +attributes+ should be an attributes object, and unlike the
351
+ # `initialize` method, no assignment calls are made per attribute.
352
+ def init_with_attributes(attributes, new_record = false) # :nodoc:
339
353
  init_internals
340
354
 
341
- @new_record = coder["new_record"]
355
+ @new_record = new_record
356
+ @attributes = attributes
342
357
 
343
358
  self.class.define_attribute_methods
344
359
 
@@ -480,7 +495,14 @@ module ActiveRecord
480
495
  inspection = if defined?(@attributes) && @attributes
481
496
  self.class.attribute_names.collect do |name|
482
497
  if has_attribute?(name)
483
- "#{name}: #{attribute_for_inspect(name)}"
498
+ attr = _read_attribute(name)
499
+ value = if attr.nil?
500
+ attr.inspect
501
+ else
502
+ attr = format_for_inspect(attr)
503
+ inspection_filter.filter_param(name, attr)
504
+ end
505
+ "#{name}: #{value}"
484
506
  end
485
507
  end.compact.join(", ")
486
508
  else
@@ -496,15 +518,16 @@ module ActiveRecord
496
518
  return super if custom_inspect_method_defined?
497
519
  pp.object_address_group(self) do
498
520
  if defined?(@attributes) && @attributes
499
- column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
500
- pp.seplist(column_names, proc { pp.text "," }) do |column_name|
501
- column_value = read_attribute(column_name)
521
+ attr_names = self.class.attribute_names.select { |name| has_attribute?(name) }
522
+ pp.seplist(attr_names, proc { pp.text "," }) do |attr_name|
502
523
  pp.breakable " "
503
524
  pp.group(1) do
504
- pp.text column_name
525
+ pp.text attr_name
505
526
  pp.text ":"
506
527
  pp.breakable
507
- pp.pp column_value
528
+ value = _read_attribute(attr_name)
529
+ value = inspection_filter.filter_param(attr_name, value) unless value.nil?
530
+ pp.pp value
508
531
  end
509
532
  end
510
533
  else
@@ -555,5 +578,15 @@ module ActiveRecord
555
578
  def custom_inspect_method_defined?
556
579
  self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
557
580
  end
581
+
582
+ def inspection_filter
583
+ @inspection_filter ||= begin
584
+ mask = DelegateClass(::String).new(ActiveSupport::ParameterFilter::FILTERED)
585
+ def mask.pretty_print(pp)
586
+ pp.text __getobj__
587
+ end
588
+ ActiveSupport::ParameterFilter.new(self.class.filter_attributes, mask: mask)
589
+ end
590
+ end
558
591
  end
559
592
  end
@@ -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,184 @@
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
+ attr_reader :configurations
13
+ delegate :any?, to: :configurations
14
+
15
+ def initialize(configurations = {})
16
+ @configurations = build_configs(configurations)
17
+ end
18
+
19
+ # Collects the configs for the environment and optionally the specification
20
+ # name passed in. To include replica configurations pass `include_replicas: true`.
21
+ #
22
+ # If a spec name is provided a single DatabaseConfig object will be
23
+ # returned, otherwise an array of DatabaseConfig objects will be
24
+ # returned that corresponds with the environment and type requested.
25
+ #
26
+ # Options:
27
+ #
28
+ # <tt>env_name:</tt> The environment name. Defaults to nil which will collect
29
+ # configs for all environments.
30
+ # <tt>spec_name:</tt> The specification name (ie primary, animals, etc.). Defaults
31
+ # to +nil+.
32
+ # <tt>include_replicas:</tt> Determines whether to include replicas in
33
+ # the returned list. Most of the time we're only iterating over the write
34
+ # connection (i.e. migrations don't need to run for the write and read connection).
35
+ # Defaults to +false+.
36
+ def configs_for(env_name: nil, spec_name: nil, include_replicas: false)
37
+ configs = env_with_configs(env_name)
38
+
39
+ unless include_replicas
40
+ configs = configs.select do |db_config|
41
+ !db_config.replica?
42
+ end
43
+ end
44
+
45
+ if spec_name
46
+ configs.find do |db_config|
47
+ db_config.spec_name == spec_name
48
+ end
49
+ else
50
+ configs
51
+ end
52
+ end
53
+
54
+ # Returns the config hash that corresponds with the environment
55
+ #
56
+ # If the application has multiple databases `default_hash` will
57
+ # return the first config hash for the environment.
58
+ #
59
+ # { database: "my_db", adapter: "mysql2" }
60
+ def default_hash(env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s)
61
+ default = find_db_config(env)
62
+ default.config if default
63
+ end
64
+ alias :[] :default_hash
65
+
66
+ # Returns a single DatabaseConfig object based on the requested environment.
67
+ #
68
+ # If the application has multiple databases `find_db_config` will return
69
+ # the first DatabaseConfig for the environment.
70
+ def find_db_config(env)
71
+ configurations.find do |db_config|
72
+ db_config.env_name == env.to_s ||
73
+ (db_config.for_current_env? && db_config.spec_name == env.to_s)
74
+ end
75
+ end
76
+
77
+ # Returns the DatabaseConfigurations object as a Hash.
78
+ def to_h
79
+ configs = configurations.reverse.inject({}) do |memo, db_config|
80
+ memo.merge(db_config.to_legacy_hash)
81
+ end
82
+
83
+ Hash[configs.to_a.reverse]
84
+ end
85
+
86
+ # Checks if the application's configurations are empty.
87
+ #
88
+ # Aliased to blank?
89
+ def empty?
90
+ configurations.empty?
91
+ end
92
+ alias :blank? :empty?
93
+
94
+ private
95
+ def env_with_configs(env = nil)
96
+ if env
97
+ configurations.select { |db_config| db_config.env_name == env }
98
+ else
99
+ configurations
100
+ end
101
+ end
102
+
103
+ def build_configs(configs)
104
+ return configs.configurations if configs.is_a?(DatabaseConfigurations)
105
+
106
+ build_db_config = configs.each_pair.flat_map do |env_name, config|
107
+ walk_configs(env_name.to_s, "primary", config)
108
+ end.compact
109
+
110
+ if url = ENV["DATABASE_URL"]
111
+ build_url_config(url, build_db_config)
112
+ else
113
+ build_db_config
114
+ end
115
+ end
116
+
117
+ def walk_configs(env_name, spec_name, config)
118
+ case config
119
+ when String
120
+ build_db_config_from_string(env_name, spec_name, config)
121
+ when Hash
122
+ build_db_config_from_hash(env_name, spec_name, config.stringify_keys)
123
+ end
124
+ end
125
+
126
+ def build_db_config_from_string(env_name, spec_name, config)
127
+ url = config
128
+ uri = URI.parse(url)
129
+ if uri.try(:scheme)
130
+ ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url)
131
+ end
132
+ rescue URI::InvalidURIError
133
+ ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
134
+ end
135
+
136
+ def build_db_config_from_hash(env_name, spec_name, config)
137
+ if url = config["url"]
138
+ config_without_url = config.dup
139
+ config_without_url.delete "url"
140
+ ActiveRecord::DatabaseConfigurations::UrlConfig.new(env_name, spec_name, url, config_without_url)
141
+ elsif config["database"] || (config.size == 1 && config.values.all? { |v| v.is_a? String })
142
+ ActiveRecord::DatabaseConfigurations::HashConfig.new(env_name, spec_name, config)
143
+ else
144
+ config.each_pair.map do |sub_spec_name, sub_config|
145
+ walk_configs(env_name, sub_spec_name, sub_config)
146
+ end
147
+ end
148
+ end
149
+
150
+ def build_url_config(url, configs)
151
+ env = ActiveRecord::ConnectionHandling::DEFAULT_ENV.call.to_s
152
+
153
+ if original_config = configs.find(&:for_current_env?)
154
+ if original_config.url_config?
155
+ configs
156
+ else
157
+ configs.map do |config|
158
+ ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, config.spec_name, url, config.config)
159
+ end
160
+ end
161
+ else
162
+ configs + [ActiveRecord::DatabaseConfigurations::UrlConfig.new(env, "primary", url)]
163
+ end
164
+ end
165
+
166
+ def method_missing(method, *args, &blk)
167
+ if Hash.method_defined?(method)
168
+ ActiveSupport::Deprecation.warn \
169
+ "Returning a hash from ActiveRecord::Base.configurations is deprecated. Therefore calling `#{method}` on the hash is also deprecated. Please switch to using the `configs_for` method instead to collect and iterate over database configurations."
170
+ end
171
+
172
+ case method
173
+ when :each, :first
174
+ configurations.send(method, *args, &blk)
175
+ when :fetch
176
+ configs_for(env_name: args.first)
177
+ when :values
178
+ configurations.map(&:config)
179
+ else
180
+ super
181
+ end
182
+ end
183
+ end
184
+ end