activerecord 5.2.8.1 → 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 (242) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -816
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +4 -2
  7. data/lib/active_record/associations/association.rb +35 -19
  8. data/lib/active_record/associations/association_scope.rb +4 -6
  9. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  10. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  11. data/lib/active_record/associations/builder/belongs_to.rb +14 -50
  12. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  13. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  14. data/lib/active_record/associations/collection_association.rb +11 -25
  15. data/lib/active_record/associations/collection_proxy.rb +32 -6
  16. data/lib/active_record/associations/foreign_association.rb +7 -0
  17. data/lib/active_record/associations/has_many_association.rb +1 -1
  18. data/lib/active_record/associations/has_many_through_association.rb +25 -18
  19. data/lib/active_record/associations/has_one_association.rb +28 -30
  20. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  21. data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
  22. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  23. data/lib/active_record/associations/join_dependency.rb +15 -20
  24. data/lib/active_record/associations/preloader/association.rb +1 -2
  25. data/lib/active_record/associations/preloader.rb +32 -29
  26. data/lib/active_record/associations/singular_association.rb +2 -16
  27. data/lib/active_record/associations.rb +16 -12
  28. data/lib/active_record/attribute_assignment.rb +7 -10
  29. data/lib/active_record/attribute_methods/dirty.rb +64 -26
  30. data/lib/active_record/attribute_methods/primary_key.rb +8 -7
  31. data/lib/active_record/attribute_methods/read.rb +16 -48
  32. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  33. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  34. data/lib/active_record/attribute_methods/write.rb +15 -16
  35. data/lib/active_record/attribute_methods.rb +34 -56
  36. data/lib/active_record/autosave_association.rb +7 -21
  37. data/lib/active_record/base.rb +2 -2
  38. data/lib/active_record/callbacks.rb +3 -17
  39. data/lib/active_record/coders/yaml_column.rb +1 -13
  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 +75 -52
  81. data/lib/active_record/counter_cache.rb +4 -29
  82. data/lib/active_record/database_configurations/database_config.rb +37 -0
  83. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  84. data/lib/active_record/database_configurations/url_config.rb +74 -0
  85. data/lib/active_record/database_configurations.rb +184 -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/command_recorder.rb +35 -5
  102. data/lib/active_record/migration/compatibility.rb +34 -16
  103. data/lib/active_record/migration.rb +38 -37
  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 -60
  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/batches.rb +13 -10
  116. data/lib/active_record/relation/calculations.rb +38 -28
  117. data/lib/active_record/relation/delegation.rb +4 -13
  118. data/lib/active_record/relation/finder_methods.rb +12 -25
  119. data/lib/active_record/relation/merger.rb +2 -6
  120. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  121. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  122. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  123. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  124. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  125. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  126. data/lib/active_record/relation/predicate_builder.rb +4 -6
  127. data/lib/active_record/relation/query_attribute.rb +15 -12
  128. data/lib/active_record/relation/query_methods.rb +29 -52
  129. data/lib/active_record/relation/where_clause.rb +4 -0
  130. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  131. data/lib/active_record/relation.rb +150 -69
  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/default.rb +10 -3
  138. data/lib/active_record/scoping/named.rb +10 -14
  139. data/lib/active_record/scoping.rb +9 -8
  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/adapter_specific_registry.rb +1 -8
  153. data/lib/active_record/type.rb +3 -4
  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/active_record.rb +2 -1
  158. data/lib/arel/alias_predication.rb +9 -0
  159. data/lib/arel/attributes/attribute.rb +37 -0
  160. data/lib/arel/attributes.rb +22 -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/and.rb +32 -0
  174. data/lib/arel/nodes/ascending.rb +23 -0
  175. data/lib/arel/nodes/binary.rb +52 -0
  176. data/lib/arel/nodes/bind_param.rb +36 -0
  177. data/lib/arel/nodes/case.rb +55 -0
  178. data/lib/arel/nodes/casted.rb +50 -0
  179. data/lib/arel/nodes/count.rb +12 -0
  180. data/lib/arel/nodes/delete_statement.rb +45 -0
  181. data/lib/arel/nodes/descending.rb +23 -0
  182. data/lib/arel/nodes/equality.rb +18 -0
  183. data/lib/arel/nodes/extract.rb +24 -0
  184. data/lib/arel/nodes/false.rb +16 -0
  185. data/lib/arel/nodes/full_outer_join.rb +8 -0
  186. data/lib/arel/nodes/function.rb +44 -0
  187. data/lib/arel/nodes/grouping.rb +8 -0
  188. data/lib/arel/nodes/in.rb +8 -0
  189. data/lib/arel/nodes/infix_operation.rb +80 -0
  190. data/lib/arel/nodes/inner_join.rb +8 -0
  191. data/lib/arel/nodes/insert_statement.rb +37 -0
  192. data/lib/arel/nodes/join_source.rb +20 -0
  193. data/lib/arel/nodes/matches.rb +18 -0
  194. data/lib/arel/nodes/named_function.rb +23 -0
  195. data/lib/arel/nodes/node.rb +50 -0
  196. data/lib/arel/nodes/node_expression.rb +13 -0
  197. data/lib/arel/nodes/outer_join.rb +8 -0
  198. data/lib/arel/nodes/over.rb +15 -0
  199. data/lib/arel/nodes/regexp.rb +16 -0
  200. data/lib/arel/nodes/right_outer_join.rb +8 -0
  201. data/lib/arel/nodes/select_core.rb +63 -0
  202. data/lib/arel/nodes/select_statement.rb +41 -0
  203. data/lib/arel/nodes/sql_literal.rb +16 -0
  204. data/lib/arel/nodes/string_join.rb +11 -0
  205. data/lib/arel/nodes/table_alias.rb +27 -0
  206. data/lib/arel/nodes/terminal.rb +16 -0
  207. data/lib/arel/nodes/true.rb +16 -0
  208. data/lib/arel/nodes/unary.rb +44 -0
  209. data/lib/arel/nodes/unary_operation.rb +20 -0
  210. data/lib/arel/nodes/unqualified_column.rb +22 -0
  211. data/lib/arel/nodes/update_statement.rb +41 -0
  212. data/lib/arel/nodes/values.rb +16 -0
  213. data/lib/arel/nodes/values_list.rb +24 -0
  214. data/lib/arel/nodes/window.rb +126 -0
  215. data/lib/arel/nodes/with.rb +11 -0
  216. data/lib/arel/nodes.rb +67 -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/depth_first.rb +199 -0
  224. data/lib/arel/visitors/dot.rb +292 -0
  225. data/lib/arel/visitors/ibm_db.rb +21 -0
  226. data/lib/arel/visitors/informix.rb +56 -0
  227. data/lib/arel/visitors/mssql.rb +143 -0
  228. data/lib/arel/visitors/mysql.rb +83 -0
  229. data/lib/arel/visitors/oracle.rb +159 -0
  230. data/lib/arel/visitors/oracle12.rb +67 -0
  231. data/lib/arel/visitors/postgresql.rb +116 -0
  232. data/lib/arel/visitors/sqlite.rb +39 -0
  233. data/lib/arel/visitors/to_sql.rb +913 -0
  234. data/lib/arel/visitors/visitor.rb +42 -0
  235. data/lib/arel/visitors/where_sql.rb +23 -0
  236. data/lib/arel/visitors.rb +20 -0
  237. data/lib/arel/window_predications.rb +9 -0
  238. data/lib/arel.rb +44 -0
  239. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  240. data/lib/rails/generators/active_record/migration.rb +14 -1
  241. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  242. metadata +107 -29
@@ -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,35 +122,25 @@ module ActiveRecord
125
122
 
126
123
  mattr_accessor :belongs_to_required_by_default, instance_accessor: false
127
124
 
128
- ##
129
- # :singleton-method:
130
- # Application configurable boolean that instructs the YAML Coder to use
131
- # an unsafe load if set to true.
132
- mattr_accessor :use_yaml_unsafe_load, instance_writer: false, default: false
133
-
134
- # Application configurable array that provides additional permitted classes
135
- # to Psych safe_load in the YAML Coder
136
- mattr_accessor :yaml_column_permitted_classes, instance_writer: false, default: []
125
+ mattr_accessor :connection_handlers, instance_accessor: false, default: {}
137
126
 
138
127
  class_attribute :default_connection_handler, instance_writer: false
139
128
 
129
+ self.filter_attributes = []
130
+
140
131
  def self.connection_handler
141
- ActiveRecord::RuntimeRegistry.connection_handler || default_connection_handler
132
+ Thread.current.thread_variable_get("ar_connection_handler") || default_connection_handler
142
133
  end
143
134
 
144
135
  def self.connection_handler=(handler)
145
- ActiveRecord::RuntimeRegistry.connection_handler = handler
136
+ Thread.current.thread_variable_set("ar_connection_handler", handler)
146
137
  end
147
138
 
148
139
  self.default_connection_handler = ConnectionAdapters::ConnectionHandler.new
140
+ self.connection_handlers = { writing: ActiveRecord::Base.default_connection_handler }
149
141
  end
150
142
 
151
- module ClassMethods # :nodoc:
152
- def allocate
153
- define_attribute_methods
154
- super
155
- end
156
-
143
+ module ClassMethods
157
144
  def initialize_find_by_cache # :nodoc:
158
145
  @find_by_statement_cache = { true => Concurrent::Map.new, false => Concurrent::Map.new }
159
146
  end
@@ -182,20 +169,16 @@ module ActiveRecord
182
169
  where(key => params.bind).limit(1)
183
170
  }
184
171
 
185
- record = statement.execute([id], connection).first
172
+ record = statement.execute([id], connection)&.first
186
173
  unless record
187
174
  raise RecordNotFound.new("Couldn't find #{name} with '#{primary_key}'=#{id}",
188
175
  name, primary_key, id)
189
176
  end
190
177
  record
191
- rescue ::RangeError
192
- raise RecordNotFound.new("Couldn't find #{name} with an out of range value for '#{primary_key}'",
193
- name, primary_key)
194
178
  end
195
179
 
196
180
  def find_by(*args) # :nodoc:
197
- return super if scope_attributes? || reflect_on_all_aggregations.any? ||
198
- columns_hash.key?(inheritance_column) && base_class != self
181
+ return super if scope_attributes? || reflect_on_all_aggregations.any?
199
182
 
200
183
  hash = args.first
201
184
 
@@ -215,11 +198,9 @@ module ActiveRecord
215
198
  where(wheres).limit(1)
216
199
  }
217
200
  begin
218
- statement.execute(hash.values, connection).first
201
+ statement.execute(hash.values, connection)&.first
219
202
  rescue TypeError
220
203
  raise ActiveRecord::StatementInvalid
221
- rescue ::RangeError
222
- nil
223
204
  end
224
205
  end
225
206
 
@@ -231,7 +212,7 @@ module ActiveRecord
231
212
  generated_association_methods
232
213
  end
233
214
 
234
- def generated_association_methods
215
+ def generated_association_methods # :nodoc:
235
216
  @generated_association_methods ||= begin
236
217
  mod = const_set(:GeneratedAssociationMethods, Module.new)
237
218
  private_constant :GeneratedAssociationMethods
@@ -241,8 +222,20 @@ module ActiveRecord
241
222
  end
242
223
  end
243
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
+
244
237
  # Returns a string like 'Post(id:integer, title:string, body:text)'
245
- def inspect
238
+ def inspect # :nodoc:
246
239
  if self == Base
247
240
  super
248
241
  elsif abstract_class?
@@ -258,7 +251,7 @@ module ActiveRecord
258
251
  end
259
252
 
260
253
  # Overwrite the default class equality method to provide support for decorated models.
261
- def ===(object)
254
+ def ===(object) # :nodoc:
262
255
  object.is_a?(self)
263
256
  end
264
257
 
@@ -284,6 +277,10 @@ module ActiveRecord
284
277
  TypeCaster::Map.new(self)
285
278
  end
286
279
 
280
+ def _internal? # :nodoc:
281
+ false
282
+ end
283
+
287
284
  private
288
285
 
289
286
  def cached_find_by_statement(key, &block)
@@ -342,13 +339,21 @@ module ActiveRecord
342
339
  # post = Post.allocate
343
340
  # post.init_with(coder)
344
341
  # post.title # => 'hello world'
345
- def init_with(coder)
342
+ def init_with(coder, &block)
346
343
  coder = LegacyYamlAdapter.convert(self.class, coder)
347
- @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
348
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:
349
353
  init_internals
350
354
 
351
- @new_record = coder["new_record"]
355
+ @new_record = new_record
356
+ @attributes = attributes
352
357
 
353
358
  self.class.define_attribute_methods
354
359
 
@@ -490,7 +495,14 @@ module ActiveRecord
490
495
  inspection = if defined?(@attributes) && @attributes
491
496
  self.class.attribute_names.collect do |name|
492
497
  if has_attribute?(name)
493
- "#{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}"
494
506
  end
495
507
  end.compact.join(", ")
496
508
  else
@@ -506,15 +518,16 @@ module ActiveRecord
506
518
  return super if custom_inspect_method_defined?
507
519
  pp.object_address_group(self) do
508
520
  if defined?(@attributes) && @attributes
509
- column_names = self.class.column_names.select { |name| has_attribute?(name) || new_record? }
510
- pp.seplist(column_names, proc { pp.text "," }) do |column_name|
511
- 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|
512
523
  pp.breakable " "
513
524
  pp.group(1) do
514
- pp.text column_name
525
+ pp.text attr_name
515
526
  pp.text ":"
516
527
  pp.breakable
517
- 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
518
531
  end
519
532
  end
520
533
  else
@@ -565,5 +578,15 @@ module ActiveRecord
565
578
  def custom_inspect_method_defined?
566
579
  self.class.instance_method(:inspect).owner != ActiveRecord::Base.instance_method(:inspect).owner
567
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
568
591
  end
569
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,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 are:
18
+ #
19
+ # <tt>:env_name</tt> - The Rails environment, ie "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,74 @@
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 are:
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
+ def build_config(original_config, url)
60
+ if /^jdbc:/.match?(url)
61
+ hash = { "url" => url }
62
+ else
63
+ hash = ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(url).to_hash
64
+ end
65
+
66
+ if original_config[env_name]
67
+ original_config[env_name].merge(hash)
68
+ else
69
+ original_config.merge(hash)
70
+ end
71
+ end
72
+ end
73
+ end
74
+ 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