activerecord 5.2.6 → 6.0.0

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 (268) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +609 -622
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +4 -2
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record/aggregations.rb +4 -2
  7. data/lib/active_record/associations/association.rb +52 -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/association.rb +14 -18
  12. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  13. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  14. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  15. data/lib/active_record/associations/builder/has_many.rb +2 -0
  16. data/lib/active_record/associations/builder/has_one.rb +35 -1
  17. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  18. data/lib/active_record/associations/collection_association.rb +6 -21
  19. data/lib/active_record/associations/collection_proxy.rb +12 -15
  20. data/lib/active_record/associations/foreign_association.rb +7 -0
  21. data/lib/active_record/associations/has_many_association.rb +2 -10
  22. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  23. data/lib/active_record/associations/has_one_association.rb +28 -30
  24. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  25. data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
  26. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  27. data/lib/active_record/associations/join_dependency.rb +24 -28
  28. data/lib/active_record/associations/preloader/association.rb +38 -36
  29. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  30. data/lib/active_record/associations/preloader.rb +40 -32
  31. data/lib/active_record/associations/singular_association.rb +2 -16
  32. data/lib/active_record/associations.rb +19 -14
  33. data/lib/active_record/attribute_assignment.rb +7 -10
  34. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -1
  35. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  36. data/lib/active_record/attribute_methods/primary_key.rb +15 -22
  37. data/lib/active_record/attribute_methods/query.rb +2 -3
  38. data/lib/active_record/attribute_methods/read.rb +15 -53
  39. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  40. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  41. data/lib/active_record/attribute_methods/write.rb +17 -24
  42. data/lib/active_record/attribute_methods.rb +28 -100
  43. data/lib/active_record/attributes.rb +13 -0
  44. data/lib/active_record/autosave_association.rb +5 -9
  45. data/lib/active_record/base.rb +2 -3
  46. data/lib/active_record/callbacks.rb +5 -19
  47. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +94 -16
  48. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  49. data/lib/active_record/connection_adapters/abstract/database_statements.rb +95 -123
  50. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -8
  51. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  52. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +19 -12
  53. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +76 -48
  54. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  55. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +132 -53
  56. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -56
  57. data/lib/active_record/connection_adapters/abstract_adapter.rb +180 -47
  58. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +128 -194
  59. data/lib/active_record/connection_adapters/column.rb +17 -13
  60. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  61. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  62. data/lib/active_record/connection_adapters/mysql/database_statements.rb +73 -13
  63. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  64. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  65. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  66. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  67. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +129 -13
  68. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  69. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -9
  70. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  71. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +20 -1
  72. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  73. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  74. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  75. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  76. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  77. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  78. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -1
  79. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  80. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +6 -3
  81. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  82. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +12 -1
  83. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  84. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +55 -53
  85. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  86. data/lib/active_record/connection_adapters/postgresql_adapter.rb +160 -74
  87. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  88. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  89. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +118 -0
  90. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -6
  91. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +42 -11
  92. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +125 -141
  93. data/lib/active_record/connection_handling.rb +149 -27
  94. data/lib/active_record/core.rb +100 -60
  95. data/lib/active_record/counter_cache.rb +4 -29
  96. data/lib/active_record/database_configurations/database_config.rb +37 -0
  97. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  98. data/lib/active_record/database_configurations/url_config.rb +79 -0
  99. data/lib/active_record/database_configurations.rb +233 -0
  100. data/lib/active_record/dynamic_matchers.rb +1 -1
  101. data/lib/active_record/enum.rb +37 -7
  102. data/lib/active_record/errors.rb +15 -7
  103. data/lib/active_record/explain.rb +1 -1
  104. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  105. data/lib/active_record/fixture_set/render_context.rb +17 -0
  106. data/lib/active_record/fixture_set/table_row.rb +153 -0
  107. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  108. data/lib/active_record/fixtures.rb +145 -472
  109. data/lib/active_record/gem_version.rb +3 -3
  110. data/lib/active_record/inheritance.rb +13 -3
  111. data/lib/active_record/insert_all.rb +179 -0
  112. data/lib/active_record/integration.rb +68 -16
  113. data/lib/active_record/internal_metadata.rb +10 -2
  114. data/lib/active_record/locking/optimistic.rb +5 -6
  115. data/lib/active_record/locking/pessimistic.rb +3 -3
  116. data/lib/active_record/log_subscriber.rb +7 -26
  117. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  118. data/lib/active_record/middleware/database_selector/resolver.rb +92 -0
  119. data/lib/active_record/middleware/database_selector.rb +75 -0
  120. data/lib/active_record/migration/command_recorder.rb +50 -6
  121. data/lib/active_record/migration/compatibility.rb +76 -49
  122. data/lib/active_record/migration.rb +100 -81
  123. data/lib/active_record/model_schema.rb +30 -9
  124. data/lib/active_record/nested_attributes.rb +2 -2
  125. data/lib/active_record/no_touching.rb +7 -0
  126. data/lib/active_record/persistence.rb +228 -24
  127. data/lib/active_record/query_cache.rb +11 -4
  128. data/lib/active_record/querying.rb +32 -20
  129. data/lib/active_record/railtie.rb +80 -43
  130. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  131. data/lib/active_record/railties/controller_runtime.rb +30 -35
  132. data/lib/active_record/railties/databases.rake +196 -46
  133. data/lib/active_record/reflection.rb +32 -30
  134. data/lib/active_record/relation/batches.rb +13 -10
  135. data/lib/active_record/relation/calculations.rb +53 -47
  136. data/lib/active_record/relation/delegation.rb +26 -43
  137. data/lib/active_record/relation/finder_methods.rb +13 -26
  138. data/lib/active_record/relation/merger.rb +11 -20
  139. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  140. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  141. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  142. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  143. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  144. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  145. data/lib/active_record/relation/predicate_builder.rb +4 -6
  146. data/lib/active_record/relation/query_attribute.rb +13 -8
  147. data/lib/active_record/relation/query_methods.rb +189 -63
  148. data/lib/active_record/relation/spawn_methods.rb +1 -1
  149. data/lib/active_record/relation/where_clause.rb +14 -10
  150. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  151. data/lib/active_record/relation.rb +310 -80
  152. data/lib/active_record/result.rb +30 -11
  153. data/lib/active_record/sanitization.rb +32 -40
  154. data/lib/active_record/schema.rb +2 -11
  155. data/lib/active_record/schema_dumper.rb +22 -7
  156. data/lib/active_record/schema_migration.rb +5 -1
  157. data/lib/active_record/scoping/default.rb +4 -5
  158. data/lib/active_record/scoping/named.rb +19 -15
  159. data/lib/active_record/scoping.rb +8 -8
  160. data/lib/active_record/statement_cache.rb +30 -3
  161. data/lib/active_record/store.rb +87 -8
  162. data/lib/active_record/table_metadata.rb +10 -17
  163. data/lib/active_record/tasks/database_tasks.rb +194 -25
  164. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -5
  165. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  166. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  167. data/lib/active_record/test_databases.rb +23 -0
  168. data/lib/active_record/test_fixtures.rb +224 -0
  169. data/lib/active_record/timestamp.rb +39 -25
  170. data/lib/active_record/touch_later.rb +4 -2
  171. data/lib/active_record/transactions.rb +57 -66
  172. data/lib/active_record/translation.rb +1 -1
  173. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  174. data/lib/active_record/type.rb +3 -4
  175. data/lib/active_record/type_caster/connection.rb +15 -14
  176. data/lib/active_record/type_caster/map.rb +1 -4
  177. data/lib/active_record/validations/uniqueness.rb +15 -27
  178. data/lib/active_record/validations.rb +1 -0
  179. data/lib/active_record.rb +9 -2
  180. data/lib/arel/alias_predication.rb +9 -0
  181. data/lib/arel/attributes/attribute.rb +37 -0
  182. data/lib/arel/attributes.rb +22 -0
  183. data/lib/arel/collectors/bind.rb +24 -0
  184. data/lib/arel/collectors/composite.rb +31 -0
  185. data/lib/arel/collectors/plain_string.rb +20 -0
  186. data/lib/arel/collectors/sql_string.rb +20 -0
  187. data/lib/arel/collectors/substitute_binds.rb +28 -0
  188. data/lib/arel/crud.rb +42 -0
  189. data/lib/arel/delete_manager.rb +18 -0
  190. data/lib/arel/errors.rb +9 -0
  191. data/lib/arel/expressions.rb +29 -0
  192. data/lib/arel/factory_methods.rb +49 -0
  193. data/lib/arel/insert_manager.rb +49 -0
  194. data/lib/arel/math.rb +45 -0
  195. data/lib/arel/nodes/and.rb +32 -0
  196. data/lib/arel/nodes/ascending.rb +23 -0
  197. data/lib/arel/nodes/binary.rb +52 -0
  198. data/lib/arel/nodes/bind_param.rb +36 -0
  199. data/lib/arel/nodes/case.rb +55 -0
  200. data/lib/arel/nodes/casted.rb +50 -0
  201. data/lib/arel/nodes/comment.rb +29 -0
  202. data/lib/arel/nodes/count.rb +12 -0
  203. data/lib/arel/nodes/delete_statement.rb +45 -0
  204. data/lib/arel/nodes/descending.rb +23 -0
  205. data/lib/arel/nodes/equality.rb +18 -0
  206. data/lib/arel/nodes/extract.rb +24 -0
  207. data/lib/arel/nodes/false.rb +16 -0
  208. data/lib/arel/nodes/full_outer_join.rb +8 -0
  209. data/lib/arel/nodes/function.rb +44 -0
  210. data/lib/arel/nodes/grouping.rb +8 -0
  211. data/lib/arel/nodes/in.rb +8 -0
  212. data/lib/arel/nodes/infix_operation.rb +80 -0
  213. data/lib/arel/nodes/inner_join.rb +8 -0
  214. data/lib/arel/nodes/insert_statement.rb +37 -0
  215. data/lib/arel/nodes/join_source.rb +20 -0
  216. data/lib/arel/nodes/matches.rb +18 -0
  217. data/lib/arel/nodes/named_function.rb +23 -0
  218. data/lib/arel/nodes/node.rb +50 -0
  219. data/lib/arel/nodes/node_expression.rb +13 -0
  220. data/lib/arel/nodes/outer_join.rb +8 -0
  221. data/lib/arel/nodes/over.rb +15 -0
  222. data/lib/arel/nodes/regexp.rb +16 -0
  223. data/lib/arel/nodes/right_outer_join.rb +8 -0
  224. data/lib/arel/nodes/select_core.rb +67 -0
  225. data/lib/arel/nodes/select_statement.rb +41 -0
  226. data/lib/arel/nodes/sql_literal.rb +16 -0
  227. data/lib/arel/nodes/string_join.rb +11 -0
  228. data/lib/arel/nodes/table_alias.rb +27 -0
  229. data/lib/arel/nodes/terminal.rb +16 -0
  230. data/lib/arel/nodes/true.rb +16 -0
  231. data/lib/arel/nodes/unary.rb +45 -0
  232. data/lib/arel/nodes/unary_operation.rb +20 -0
  233. data/lib/arel/nodes/unqualified_column.rb +22 -0
  234. data/lib/arel/nodes/update_statement.rb +41 -0
  235. data/lib/arel/nodes/values_list.rb +9 -0
  236. data/lib/arel/nodes/window.rb +126 -0
  237. data/lib/arel/nodes/with.rb +11 -0
  238. data/lib/arel/nodes.rb +68 -0
  239. data/lib/arel/order_predications.rb +13 -0
  240. data/lib/arel/predications.rb +257 -0
  241. data/lib/arel/select_manager.rb +271 -0
  242. data/lib/arel/table.rb +110 -0
  243. data/lib/arel/tree_manager.rb +72 -0
  244. data/lib/arel/update_manager.rb +34 -0
  245. data/lib/arel/visitors/depth_first.rb +204 -0
  246. data/lib/arel/visitors/dot.rb +297 -0
  247. data/lib/arel/visitors/ibm_db.rb +34 -0
  248. data/lib/arel/visitors/informix.rb +62 -0
  249. data/lib/arel/visitors/mssql.rb +157 -0
  250. data/lib/arel/visitors/mysql.rb +83 -0
  251. data/lib/arel/visitors/oracle.rb +159 -0
  252. data/lib/arel/visitors/oracle12.rb +66 -0
  253. data/lib/arel/visitors/postgresql.rb +110 -0
  254. data/lib/arel/visitors/sqlite.rb +39 -0
  255. data/lib/arel/visitors/to_sql.rb +889 -0
  256. data/lib/arel/visitors/visitor.rb +46 -0
  257. data/lib/arel/visitors/where_sql.rb +23 -0
  258. data/lib/arel/visitors.rb +20 -0
  259. data/lib/arel/window_predications.rb +9 -0
  260. data/lib/arel.rb +51 -0
  261. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  262. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  263. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  264. data/lib/rails/generators/active_record/migration.rb +14 -1
  265. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  266. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  267. metadata +108 -26
  268. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -61,8 +61,9 @@ module ActiveRecord
61
61
  # # => "id ASC"
62
62
  def sanitize_sql_for_order(condition)
63
63
  if condition.is_a?(Array) && condition.first.to_s.include?("?")
64
- enforce_raw_sql_whitelist([condition.first],
65
- whitelist: AttributeMethods::ClassMethods::COLUMN_NAME_ORDER_WHITELIST
64
+ disallow_raw_sql!(
65
+ [condition.first],
66
+ permit: connection.column_name_with_order_matcher
66
67
  )
67
68
 
68
69
  # Ensure we aren't dealing with a subclass of String that might
@@ -133,44 +134,34 @@ module ActiveRecord
133
134
  end
134
135
  end
135
136
 
136
- private
137
- # Accepts a hash of SQL conditions and replaces those attributes
138
- # that correspond to a {#composed_of}[rdoc-ref:Aggregations::ClassMethods#composed_of]
139
- # relationship with their expanded aggregate attribute values.
140
- #
141
- # Given:
142
- #
143
- # class Person < ActiveRecord::Base
144
- # composed_of :address, class_name: "Address",
145
- # mapping: [%w(address_street street), %w(address_city city)]
146
- # end
147
- #
148
- # Then:
149
- #
150
- # { address: Address.new("813 abc st.", "chicago") }
151
- # # => { address_street: "813 abc st.", address_city: "chicago" }
152
- def expand_hash_conditions_for_aggregates(attrs) # :doc:
153
- expanded_attrs = {}
154
- attrs.each do |attr, value|
155
- if aggregation = reflect_on_aggregation(attr.to_sym)
156
- mapping = aggregation.mapping
157
- mapping.each do |field_attr, aggregate_attr|
158
- expanded_attrs[field_attr] = if value.is_a?(Array)
159
- value.map { |it| it.send(aggregate_attr) }
160
- elsif mapping.size == 1 && !value.respond_to?(aggregate_attr)
161
- value
162
- else
163
- value.send(aggregate_attr)
164
- end
165
- end
166
- else
167
- expanded_attrs[attr] = value
168
- end
169
- end
170
- expanded_attrs
137
+ def disallow_raw_sql!(args, permit: connection.column_name_matcher) # :nodoc:
138
+ unexpected = nil
139
+ args.each do |arg|
140
+ next if arg.is_a?(Symbol) || Arel.arel_node?(arg) || permit.match?(arg.to_s)
141
+ (unexpected ||= []) << arg
171
142
  end
172
- deprecate :expand_hash_conditions_for_aggregates
173
143
 
144
+ return unless unexpected
145
+
146
+ if allow_unsafe_raw_sql == :deprecated
147
+ ActiveSupport::Deprecation.warn(
148
+ "Dangerous query method (method whose arguments are used as raw " \
149
+ "SQL) called with non-attribute argument(s): " \
150
+ "#{unexpected.map(&:inspect).join(", ")}. Non-attribute " \
151
+ "arguments will be disallowed in Rails 6.1. This method should " \
152
+ "not be called with user-provided values, such as request " \
153
+ "parameters or model attributes. Known-safe values can be passed " \
154
+ "by wrapping them in Arel.sql()."
155
+ )
156
+ else
157
+ raise(ActiveRecord::UnknownAttributeReference,
158
+ "Query method called with non-attribute argument(s): " +
159
+ unexpected.map(&:inspect).join(", ")
160
+ )
161
+ end
162
+ end
163
+
164
+ private
174
165
  def replace_bind_variables(statement, values)
175
166
  raise_if_bind_arity_mismatch(statement, statement.count("?"), values.size)
176
167
  bound = values.dup
@@ -202,10 +193,11 @@ module ActiveRecord
202
193
 
203
194
  def quote_bound_value(value, c = connection)
204
195
  if value.respond_to?(:map) && !value.acts_like?(:string)
205
- if value.respond_to?(:empty?) && value.empty?
196
+ quoted = value.map { |v| c.quote(v) }
197
+ if quoted.empty?
206
198
  c.quote(nil)
207
199
  else
208
- value.map { |v| c.quote(v) }.join(",")
200
+ quoted.join(",")
209
201
  end
210
202
  else
211
203
  c.quote(value)
@@ -50,21 +50,12 @@ module ActiveRecord
50
50
  instance_eval(&block)
51
51
 
52
52
  if info[:version].present?
53
- ActiveRecord::SchemaMigration.create_table
54
- connection.assume_migrated_upto_version(info[:version], migrations_paths)
53
+ connection.schema_migration.create_table
54
+ connection.assume_migrated_upto_version(info[:version])
55
55
  end
56
56
 
57
57
  ActiveRecord::InternalMetadata.create_table
58
58
  ActiveRecord::InternalMetadata[:environment] = connection.migration_context.current_environment
59
59
  end
60
-
61
- private
62
- # Returns the migrations paths.
63
- #
64
- # ActiveRecord::Schema.new.migrations_paths
65
- # # => ["db/migrate"] # Rails migration path by default.
66
- def migrations_paths
67
- ActiveRecord::Migrator.migrations_paths
68
- end
69
60
  end
70
61
  end
@@ -17,6 +17,12 @@ module ActiveRecord
17
17
  # Only strings are accepted if ActiveRecord::Base.schema_format == :sql.
18
18
  cattr_accessor :ignore_tables, default: []
19
19
 
20
+ ##
21
+ # :singleton-method:
22
+ # Specify a custom regular expression matching foreign keys which name
23
+ # should not be dumped to db/schema.rb.
24
+ cattr_accessor :fk_ignore_pattern, default: /^fk_rails_[0-9a-f]{10}$/
25
+
20
26
  class << self
21
27
  def dump(connection = ActiveRecord::Base.connection, stream = STDOUT, config = ActiveRecord::Base)
22
28
  connection.create_schema_dumper(generate_options(config)).dump(stream)
@@ -41,6 +47,7 @@ module ActiveRecord
41
47
  end
42
48
 
43
49
  private
50
+ attr_accessor :table_name
44
51
 
45
52
  def initialize(connection, options = {})
46
53
  @connection = connection
@@ -65,11 +72,11 @@ module ActiveRecord
65
72
  # of editing this file, please use the migrations feature of Active Record to
66
73
  # incrementally modify your database, and then regenerate this schema definition.
67
74
  #
68
- # Note that this schema.rb definition is the authoritative source for your
69
- # database schema. If you need to create the application database on another
70
- # system, you should be using db:schema:load, not running all the migrations
71
- # from scratch. The latter is a flawed and unsustainable approach (the more migrations
72
- # you'll amass, the slower it'll run and the greater likelihood for issues).
75
+ # This file is the source Rails uses to define your schema when running `rails
76
+ # db:schema:load`. When creating a new database, `rails db:schema:load` tends to
77
+ # be faster and is potentially less error prone than running all of your
78
+ # migrations from scratch. Old migrations may fail to apply correctly if those
79
+ # migrations use external dependencies or application code.
73
80
  #
74
81
  # It's strongly recommended that you check this file into your version control system.
75
82
 
@@ -104,6 +111,8 @@ HEADER
104
111
  def table(table, stream)
105
112
  columns = @connection.columns(table)
106
113
  begin
114
+ self.table_name = table
115
+
107
116
  tbl = StringIO.new
108
117
 
109
118
  # first dump primary key column
@@ -137,7 +146,11 @@ HEADER
137
146
  raise StandardError, "Unknown type '#{column.sql_type}' for column '#{column.name}'" unless @connection.valid_type?(column.type)
138
147
  next if column.name == pk
139
148
  type, colspec = column_spec(column)
140
- tbl.print " t.#{type} #{column.name.inspect}"
149
+ if type.is_a?(Symbol)
150
+ tbl.print " t.#{type} #{column.name.inspect}"
151
+ else
152
+ tbl.print " t.column #{column.name.inspect}, #{type.inspect}"
153
+ end
141
154
  tbl.print ", #{format_colspec(colspec)}" if colspec.present?
142
155
  tbl.puts
143
156
  end
@@ -153,6 +166,8 @@ HEADER
153
166
  stream.puts "# Could not dump table #{table.inspect} because of following #{e.class}"
154
167
  stream.puts "# #{e.message}"
155
168
  stream.puts
169
+ ensure
170
+ self.table_name = nil
156
171
  end
157
172
  end
158
173
 
@@ -210,7 +225,7 @@ HEADER
210
225
  parts << "primary_key: #{foreign_key.primary_key.inspect}"
211
226
  end
212
227
 
213
- if foreign_key.name !~ /^fk_rails_[0-9a-f]{10}$/
228
+ if foreign_key.export_name_on_schema_dump?
214
229
  parts << "name: #{foreign_key.name.inspect}"
215
230
  end
216
231
 
@@ -10,12 +10,16 @@ module ActiveRecord
10
10
  # to be executed the next time.
11
11
  class SchemaMigration < ActiveRecord::Base # :nodoc:
12
12
  class << self
13
+ def _internal?
14
+ true
15
+ end
16
+
13
17
  def primary_key
14
18
  "version"
15
19
  end
16
20
 
17
21
  def table_name
18
- "#{table_name_prefix}#{ActiveRecord::Base.schema_migrations_table_name}#{table_name_suffix}"
22
+ "#{table_name_prefix}#{schema_migrations_table_name}#{table_name_suffix}"
19
23
  end
20
24
 
21
25
  def table_exists?
@@ -100,7 +100,7 @@ module ActiveRecord
100
100
  self.default_scopes += [scope]
101
101
  end
102
102
 
103
- def build_default_scope(base_rel = nil)
103
+ def build_default_scope(relation = relation())
104
104
  return if abstract_class?
105
105
 
106
106
  if default_scope_override.nil?
@@ -111,15 +111,14 @@ module ActiveRecord
111
111
  # The user has defined their own default scope method, so call that
112
112
  evaluate_default_scope do
113
113
  if scope = default_scope
114
- (base_rel ||= relation).merge!(scope)
114
+ relation.merge!(scope)
115
115
  end
116
116
  end
117
117
  elsif default_scopes.any?
118
- base_rel ||= relation
119
118
  evaluate_default_scope do
120
- default_scopes.inject(base_rel) do |default_scope, scope|
119
+ default_scopes.inject(relation) do |default_scope, scope|
121
120
  scope = scope.respond_to?(:to_proc) ? scope : scope.method(:call)
122
- default_scope.merge!(base_rel.instance_exec(&scope))
121
+ default_scope.instance_exec(&scope) || default_scope
123
122
  end
124
123
  end
125
124
  end
@@ -24,13 +24,21 @@ module ActiveRecord
24
24
  # You can define a scope that applies to all finders using
25
25
  # {default_scope}[rdoc-ref:Scoping::Default::ClassMethods#default_scope].
26
26
  def all
27
- current_scope = self.current_scope
27
+ scope = current_scope
28
+
29
+ if scope
30
+ if scope._deprecated_scope_source
31
+ ActiveSupport::Deprecation.warn(<<~MSG.squish)
32
+ Class level methods will no longer inherit scoping from `#{scope._deprecated_scope_source}`
33
+ in Rails 6.1. To continue using the scoped relation, pass it into the block directly.
34
+ To instead access the full set of models, as Rails 6.1 will, use `#{name}.unscoped`.
35
+ MSG
36
+ end
28
37
 
29
- if current_scope
30
- if self == current_scope.klass
31
- current_scope.clone
38
+ if self == scope.klass
39
+ scope.clone
32
40
  else
33
- relation.merge!(current_scope)
41
+ relation.merge!(scope)
34
42
  end
35
43
  else
36
44
  default_scoped
@@ -38,9 +46,7 @@ module ActiveRecord
38
46
  end
39
47
 
40
48
  def scope_for_association(scope = relation) # :nodoc:
41
- current_scope = self.current_scope
42
-
43
- if current_scope && current_scope.empty_scope?
49
+ if current_scope&.empty_scope?
44
50
  scope
45
51
  else
46
52
  default_scoped(scope)
@@ -52,7 +58,7 @@ module ActiveRecord
52
58
  end
53
59
 
54
60
  def default_extensions # :nodoc:
55
- if scope = current_scope || build_default_scope
61
+ if scope = scope_for_association || build_default_scope
56
62
  scope.extensions
57
63
  else
58
64
  []
@@ -181,16 +187,14 @@ module ActiveRecord
181
187
  extension = Module.new(&block) if block
182
188
 
183
189
  if body.respond_to?(:to_proc)
184
- singleton_class.send(:define_method, name) do |*args|
185
- scope = all
186
- scope = scope._exec_scope(*args, &body)
190
+ singleton_class.define_method(name) do |*args|
191
+ scope = all._exec_scope(name, *args, &body)
187
192
  scope = scope.extending(extension) if extension
188
193
  scope
189
194
  end
190
195
  else
191
- singleton_class.send(:define_method, name) do |*args|
192
- scope = all
193
- scope = scope.scoping { body.call(*args) || scope }
196
+ singleton_class.define_method(name) do |*args|
197
+ scope = body.call(*args) || all
194
198
  scope = scope.extending(extension) if extension
195
199
  scope
196
200
  end
@@ -12,14 +12,6 @@ module ActiveRecord
12
12
  end
13
13
 
14
14
  module ClassMethods # :nodoc:
15
- def current_scope(skip_inherited_scope = false)
16
- ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope)
17
- end
18
-
19
- def current_scope=(scope)
20
- ScopeRegistry.set_value_for(:current_scope, self, scope)
21
- end
22
-
23
15
  # Collects attributes from scopes that should be applied when creating
24
16
  # an AR instance for the particular class this is called on.
25
17
  def scope_attributes
@@ -30,6 +22,14 @@ module ActiveRecord
30
22
  def scope_attributes?
31
23
  current_scope
32
24
  end
25
+
26
+ def current_scope(skip_inherited_scope = false)
27
+ ScopeRegistry.value_for(:current_scope, self, skip_inherited_scope)
28
+ end
29
+
30
+ def current_scope=(scope)
31
+ ScopeRegistry.set_value_for(:current_scope, self, scope)
32
+ end
33
33
  end
34
34
 
35
35
  def populate_with_current_scope_attributes # :nodoc:
@@ -44,7 +44,7 @@ module ActiveRecord
44
44
  def initialize(values)
45
45
  @values = values
46
46
  @indexes = values.each_with_index.find_all { |thing, i|
47
- Arel::Nodes::BindParam === thing
47
+ Substitute === thing
48
48
  }.map(&:last)
49
49
  end
50
50
 
@@ -56,6 +56,28 @@ module ActiveRecord
56
56
  end
57
57
  end
58
58
 
59
+ class PartialQueryCollector
60
+ def initialize
61
+ @parts = []
62
+ @binds = []
63
+ end
64
+
65
+ def <<(str)
66
+ @parts << str
67
+ self
68
+ end
69
+
70
+ def add_bind(obj)
71
+ @binds << obj
72
+ @parts << Substitute.new
73
+ self
74
+ end
75
+
76
+ def value
77
+ [@parts, @binds]
78
+ end
79
+ end
80
+
59
81
  def self.query(sql)
60
82
  Query.new(sql)
61
83
  end
@@ -64,6 +86,10 @@ module ActiveRecord
64
86
  PartialQuery.new(values)
65
87
  end
66
88
 
89
+ def self.partial_query_collector
90
+ PartialQueryCollector.new
91
+ end
92
+
67
93
  class Params # :nodoc:
68
94
  def bind; Substitute.new; end
69
95
  end
@@ -106,6 +132,8 @@ module ActiveRecord
106
132
  sql = query_builder.sql_for bind_values, connection
107
133
 
108
134
  klass.find_by_sql(sql, bind_values, preparable: true, &block)
135
+ rescue ::RangeError
136
+ nil
109
137
  end
110
138
 
111
139
  def self.unsupported_value?(value)
@@ -114,8 +142,7 @@ module ActiveRecord
114
142
  end
115
143
  end
116
144
 
117
- protected
118
-
145
+ private
119
146
  attr_reader :query_builder, :bind_map, :klass
120
147
  end
121
148
  end
@@ -11,14 +11,20 @@ module ActiveRecord
11
11
  # of the model. This is very helpful for easily exposing store keys to a form or elsewhere that's
12
12
  # already built around just accessing attributes on the model.
13
13
  #
14
+ # Every accessor comes with dirty tracking methods (+key_changed?+, +key_was+ and +key_change+) and
15
+ # methods to access the changes made during the last save (+saved_change_to_key?+, +saved_change_to_key+ and
16
+ # +key_before_last_save+).
17
+ #
18
+ # NOTE: There is no +key_will_change!+ method for accessors, use +store_will_change!+ instead.
19
+ #
14
20
  # Make sure that you declare the database column used for the serialized store as a text, so there's
15
21
  # plenty of room.
16
22
  #
17
23
  # You can set custom coder to encode/decode your serialized attributes to/from different formats.
18
24
  # JSON, YAML, Marshal are supported out of the box. Generally it can be any wrapper that provides +load+ and +dump+.
19
25
  #
20
- # NOTE: If you are using PostgreSQL specific columns like +hstore+ or +json+ there is no need for
21
- # the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
26
+ # NOTE: If you are using structured database data types (eg. PostgreSQL +hstore+/+json+, or MySQL 5.7+
27
+ # +json+) there is no need for the serialization provided by {.store}[rdoc-ref:rdoc-ref:ClassMethods#store].
22
28
  # Simply use {.store_accessor}[rdoc-ref:ClassMethods#store_accessor] instead to generate
23
29
  # the accessor methods. Be aware that these columns use a string keyed hash and do not allow access
24
30
  # using a symbol.
@@ -31,24 +37,40 @@ module ActiveRecord
31
37
  #
32
38
  # class User < ActiveRecord::Base
33
39
  # store :settings, accessors: [ :color, :homepage ], coder: JSON
40
+ # store :parent, accessors: [ :name ], coder: JSON, prefix: true
41
+ # store :spouse, accessors: [ :name ], coder: JSON, prefix: :partner
42
+ # store :settings, accessors: [ :two_factor_auth ], suffix: true
43
+ # store :settings, accessors: [ :login_retry ], suffix: :config
34
44
  # end
35
45
  #
36
- # u = User.new(color: 'black', homepage: '37signals.com')
46
+ # u = User.new(color: 'black', homepage: '37signals.com', parent_name: 'Mary', partner_name: 'Lily')
37
47
  # u.color # Accessor stored attribute
48
+ # u.parent_name # Accessor stored attribute with prefix
49
+ # u.partner_name # Accessor stored attribute with custom prefix
50
+ # u.two_factor_auth_settings # Accessor stored attribute with suffix
51
+ # u.login_retry_config # Accessor stored attribute with custom suffix
38
52
  # u.settings[:country] = 'Denmark' # Any attribute, even if not specified with an accessor
39
53
  #
40
54
  # # There is no difference between strings and symbols for accessing custom attributes
41
55
  # u.settings[:country] # => 'Denmark'
42
56
  # u.settings['country'] # => 'Denmark'
43
57
  #
58
+ # # Dirty tracking
59
+ # u.color = 'green'
60
+ # u.color_changed? # => true
61
+ # u.color_was # => 'black'
62
+ # u.color_change # => ['black', 'red']
63
+ #
44
64
  # # Add additional accessors to an existing store through store_accessor
45
65
  # class SuperUser < User
46
66
  # store_accessor :settings, :privileges, :servants
67
+ # store_accessor :parent, :birthday, prefix: true
68
+ # store_accessor :settings, :secret_question, suffix: :config
47
69
  # end
48
70
  #
49
71
  # The stored attribute names can be retrieved using {.stored_attributes}[rdoc-ref:rdoc-ref:ClassMethods#stored_attributes].
50
72
  #
51
- # User.stored_attributes[:settings] # [:color, :homepage]
73
+ # User.stored_attributes[:settings] # [:color, :homepage, :two_factor_auth, :login_retry]
52
74
  #
53
75
  # == Overwriting default accessors
54
76
  #
@@ -81,21 +103,78 @@ module ActiveRecord
81
103
  module ClassMethods
82
104
  def store(store_attribute, options = {})
83
105
  serialize store_attribute, IndifferentCoder.new(store_attribute, options[:coder])
84
- store_accessor(store_attribute, options[:accessors]) if options.has_key? :accessors
106
+ store_accessor(store_attribute, options[:accessors], options.slice(:prefix, :suffix)) if options.has_key? :accessors
85
107
  end
86
108
 
87
- def store_accessor(store_attribute, *keys)
109
+ def store_accessor(store_attribute, *keys, prefix: nil, suffix: nil)
88
110
  keys = keys.flatten
89
111
 
112
+ accessor_prefix =
113
+ case prefix
114
+ when String, Symbol
115
+ "#{prefix}_"
116
+ when TrueClass
117
+ "#{store_attribute}_"
118
+ else
119
+ ""
120
+ end
121
+ accessor_suffix =
122
+ case suffix
123
+ when String, Symbol
124
+ "_#{suffix}"
125
+ when TrueClass
126
+ "_#{store_attribute}"
127
+ else
128
+ ""
129
+ end
130
+
90
131
  _store_accessors_module.module_eval do
91
132
  keys.each do |key|
92
- define_method("#{key}=") do |value|
133
+ accessor_key = "#{accessor_prefix}#{key}#{accessor_suffix}"
134
+
135
+ define_method("#{accessor_key}=") do |value|
93
136
  write_store_attribute(store_attribute, key, value)
94
137
  end
95
138
 
96
- define_method(key) do
139
+ define_method(accessor_key) do
97
140
  read_store_attribute(store_attribute, key)
98
141
  end
142
+
143
+ define_method("#{accessor_key}_changed?") do
144
+ return false unless attribute_changed?(store_attribute)
145
+ prev_store, new_store = changes[store_attribute]
146
+ prev_store&.dig(key) != new_store&.dig(key)
147
+ end
148
+
149
+ define_method("#{accessor_key}_change") do
150
+ return unless attribute_changed?(store_attribute)
151
+ prev_store, new_store = changes[store_attribute]
152
+ [prev_store&.dig(key), new_store&.dig(key)]
153
+ end
154
+
155
+ define_method("#{accessor_key}_was") do
156
+ return unless attribute_changed?(store_attribute)
157
+ prev_store, _new_store = changes[store_attribute]
158
+ prev_store&.dig(key)
159
+ end
160
+
161
+ define_method("saved_change_to_#{accessor_key}?") do
162
+ return false unless saved_change_to_attribute?(store_attribute)
163
+ prev_store, new_store = saved_change_to_attribute(store_attribute)
164
+ prev_store&.dig(key) != new_store&.dig(key)
165
+ end
166
+
167
+ define_method("saved_change_to_#{accessor_key}") do
168
+ return unless saved_change_to_attribute?(store_attribute)
169
+ prev_store, new_store = saved_change_to_attribute(store_attribute)
170
+ [prev_store&.dig(key), new_store&.dig(key)]
171
+ end
172
+
173
+ define_method("#{accessor_key}_before_last_save") do
174
+ return unless saved_change_to_attribute?(store_attribute)
175
+ prev_store, _new_store = saved_change_to_attribute(store_attribute)
176
+ prev_store&.dig(key)
177
+ end
99
178
  end
100
179
  end
101
180
 
@@ -4,17 +4,18 @@ module ActiveRecord
4
4
  class TableMetadata # :nodoc:
5
5
  delegate :foreign_type, :foreign_key, :join_primary_key, :join_foreign_key, to: :association, prefix: true
6
6
 
7
- def initialize(klass, arel_table, association = nil)
7
+ def initialize(klass, arel_table, association = nil, types = klass)
8
8
  @klass = klass
9
+ @types = types
9
10
  @arel_table = arel_table
10
11
  @association = association
11
12
  end
12
13
 
13
14
  def resolve_column_aliases(hash)
14
15
  new_hash = hash.dup
15
- hash.each do |key, _|
16
- if (key.is_a?(Symbol)) && klass.attribute_alias?(key)
17
- new_hash[klass.attribute_alias(key)] = new_hash.delete(key)
16
+ hash.each_key do |key|
17
+ if key.is_a?(Symbol) && new_key = klass.attribute_aliases[key.to_s]
18
+ new_hash[new_key] = new_hash.delete(key)
18
19
  end
19
20
  end
20
21
  new_hash
@@ -29,11 +30,7 @@ module ActiveRecord
29
30
  end
30
31
 
31
32
  def type(column_name)
32
- if klass
33
- klass.type_for_attribute(column_name)
34
- else
35
- Type.default_value
36
- end
33
+ types.type_for_attribute(column_name)
37
34
  end
38
35
 
39
36
  def has_column?(column_name)
@@ -52,13 +49,12 @@ module ActiveRecord
52
49
  elsif association && !association.polymorphic?
53
50
  association_klass = association.klass
54
51
  arel_table = association_klass.arel_table.alias(table_name)
52
+ TableMetadata.new(association_klass, arel_table, association)
55
53
  else
56
54
  type_caster = TypeCaster::Connection.new(klass, table_name)
57
- association_klass = nil
58
55
  arel_table = Arel::Table.new(table_name, type_caster: type_caster)
56
+ TableMetadata.new(nil, arel_table, association, type_caster)
59
57
  end
60
-
61
- TableMetadata.new(association_klass, arel_table, association)
62
58
  end
63
59
 
64
60
  def polymorphic_association?
@@ -73,10 +69,7 @@ module ActiveRecord
73
69
  klass.reflect_on_aggregation(aggregation_name)
74
70
  end
75
71
 
76
- # TODO Change this to private once we've dropped Ruby 2.2 support.
77
- # Workaround for Ruby 2.2 "private attribute?" warning.
78
- protected
79
-
80
- attr_reader :klass, :arel_table, :association
72
+ private
73
+ attr_reader :klass, :types, :arel_table, :association
81
74
  end
82
75
  end