activerecord 5.2.4.4 → 6.0.3.4

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 (292) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +777 -552
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +5 -3
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +10 -2
  7. data/lib/active_record/advisory_lock_base.rb +18 -0
  8. data/lib/active_record/aggregations.rb +4 -3
  9. data/lib/active_record/association_relation.rb +10 -8
  10. data/lib/active_record/associations.rb +21 -16
  11. data/lib/active_record/associations/alias_tracker.rb +0 -1
  12. data/lib/active_record/associations/association.rb +56 -19
  13. data/lib/active_record/associations/association_scope.rb +4 -6
  14. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  15. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  16. data/lib/active_record/associations/builder/association.rb +14 -18
  17. data/lib/active_record/associations/builder/belongs_to.rb +19 -52
  18. data/lib/active_record/associations/builder/collection_association.rb +3 -13
  19. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -40
  20. data/lib/active_record/associations/builder/has_many.rb +2 -0
  21. data/lib/active_record/associations/builder/has_one.rb +35 -1
  22. data/lib/active_record/associations/builder/singular_association.rb +2 -0
  23. data/lib/active_record/associations/collection_association.rb +12 -23
  24. data/lib/active_record/associations/collection_proxy.rb +13 -17
  25. data/lib/active_record/associations/foreign_association.rb +7 -0
  26. data/lib/active_record/associations/has_many_association.rb +2 -11
  27. data/lib/active_record/associations/has_many_through_association.rb +14 -14
  28. data/lib/active_record/associations/has_one_association.rb +28 -30
  29. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  30. data/lib/active_record/associations/join_dependency.rb +37 -28
  31. data/lib/active_record/associations/join_dependency/join_association.rb +9 -10
  32. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  33. data/lib/active_record/associations/preloader.rb +39 -32
  34. data/lib/active_record/associations/preloader/association.rb +38 -36
  35. data/lib/active_record/associations/preloader/through_association.rb +48 -39
  36. data/lib/active_record/associations/singular_association.rb +2 -16
  37. data/lib/active_record/attribute_assignment.rb +7 -11
  38. data/lib/active_record/attribute_decorators.rb +0 -2
  39. data/lib/active_record/attribute_methods.rb +28 -100
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +4 -2
  41. data/lib/active_record/attribute_methods/dirty.rb +111 -40
  42. data/lib/active_record/attribute_methods/primary_key.rb +15 -24
  43. data/lib/active_record/attribute_methods/query.rb +2 -3
  44. data/lib/active_record/attribute_methods/read.rb +15 -54
  45. data/lib/active_record/attribute_methods/serialization.rb +1 -2
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -3
  47. data/lib/active_record/attribute_methods/write.rb +17 -25
  48. data/lib/active_record/attributes.rb +13 -1
  49. data/lib/active_record/autosave_association.rb +3 -5
  50. data/lib/active_record/base.rb +2 -3
  51. data/lib/active_record/callbacks.rb +6 -21
  52. data/lib/active_record/coders/yaml_column.rb +0 -1
  53. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +103 -18
  54. data/lib/active_record/connection_adapters/abstract/database_limits.rb +17 -4
  55. data/lib/active_record/connection_adapters/abstract/database_statements.rb +102 -124
  56. data/lib/active_record/connection_adapters/abstract/query_cache.rb +18 -9
  57. data/lib/active_record/connection_adapters/abstract/quoting.rb +68 -17
  58. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +20 -14
  59. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +100 -72
  60. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +1 -3
  61. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +175 -79
  62. data/lib/active_record/connection_adapters/abstract/transaction.rb +96 -57
  63. data/lib/active_record/connection_adapters/abstract_adapter.rb +191 -43
  64. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +142 -215
  65. data/lib/active_record/connection_adapters/column.rb +17 -13
  66. data/lib/active_record/connection_adapters/connection_specification.rb +54 -45
  67. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +6 -10
  68. data/lib/active_record/connection_adapters/mysql/column.rb +1 -1
  69. data/lib/active_record/connection_adapters/mysql/database_statements.rb +70 -14
  70. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +0 -1
  71. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -7
  72. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +4 -6
  73. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +40 -32
  74. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +14 -6
  75. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +132 -16
  76. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +6 -10
  77. data/lib/active_record/connection_adapters/mysql2_adapter.rb +26 -10
  78. data/lib/active_record/connection_adapters/postgresql/column.rb +17 -31
  79. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +26 -1
  80. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -2
  81. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  82. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +0 -1
  83. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -2
  84. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -2
  85. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  86. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +1 -1
  87. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -2
  88. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +1 -2
  89. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +1 -1
  90. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  91. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +5 -3
  92. data/lib/active_record/connection_adapters/postgresql/quoting.rb +44 -7
  93. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +1 -1
  94. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +14 -3
  95. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +107 -91
  96. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +0 -1
  97. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +63 -75
  98. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +24 -27
  99. data/lib/active_record/connection_adapters/postgresql/utils.rb +0 -1
  100. data/lib/active_record/connection_adapters/postgresql_adapter.rb +168 -75
  101. data/lib/active_record/connection_adapters/schema_cache.rb +37 -14
  102. data/lib/active_record/connection_adapters/sql_type_metadata.rb +11 -8
  103. data/lib/active_record/connection_adapters/sqlite3/database_statements.rb +119 -0
  104. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +42 -7
  105. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +43 -12
  106. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +135 -146
  107. data/lib/active_record/connection_adapters/statement_pool.rb +0 -1
  108. data/lib/active_record/connection_handling.rb +139 -26
  109. data/lib/active_record/core.rb +103 -61
  110. data/lib/active_record/counter_cache.rb +8 -30
  111. data/lib/active_record/database_configurations.rb +233 -0
  112. data/lib/active_record/database_configurations/database_config.rb +37 -0
  113. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  114. data/lib/active_record/database_configurations/url_config.rb +78 -0
  115. data/lib/active_record/dynamic_matchers.rb +3 -4
  116. data/lib/active_record/enum.rb +37 -7
  117. data/lib/active_record/errors.rb +15 -7
  118. data/lib/active_record/explain.rb +1 -2
  119. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  120. data/lib/active_record/fixture_set/render_context.rb +17 -0
  121. data/lib/active_record/fixture_set/table_row.rb +152 -0
  122. data/lib/active_record/fixture_set/table_rows.rb +46 -0
  123. data/lib/active_record/fixtures.rb +144 -474
  124. data/lib/active_record/gem_version.rb +3 -3
  125. data/lib/active_record/inheritance.rb +13 -6
  126. data/lib/active_record/insert_all.rb +179 -0
  127. data/lib/active_record/integration.rb +68 -16
  128. data/lib/active_record/internal_metadata.rb +11 -3
  129. data/lib/active_record/locking/optimistic.rb +5 -7
  130. data/lib/active_record/locking/pessimistic.rb +3 -3
  131. data/lib/active_record/log_subscriber.rb +8 -27
  132. data/lib/active_record/middleware/database_selector.rb +74 -0
  133. data/lib/active_record/middleware/database_selector/resolver.rb +87 -0
  134. data/lib/active_record/middleware/database_selector/resolver/session.rb +45 -0
  135. data/lib/active_record/migration.rb +104 -85
  136. data/lib/active_record/migration/command_recorder.rb +54 -22
  137. data/lib/active_record/migration/compatibility.rb +79 -52
  138. data/lib/active_record/migration/join_table.rb +0 -1
  139. data/lib/active_record/model_schema.rb +33 -11
  140. data/lib/active_record/nested_attributes.rb +2 -4
  141. data/lib/active_record/no_touching.rb +9 -2
  142. data/lib/active_record/null_relation.rb +0 -1
  143. data/lib/active_record/persistence.rb +232 -29
  144. data/lib/active_record/query_cache.rb +11 -4
  145. data/lib/active_record/querying.rb +33 -21
  146. data/lib/active_record/railtie.rb +80 -43
  147. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  148. data/lib/active_record/railties/controller_runtime.rb +30 -35
  149. data/lib/active_record/railties/databases.rake +199 -46
  150. data/lib/active_record/reflection.rb +40 -38
  151. data/lib/active_record/relation.rb +322 -80
  152. data/lib/active_record/relation/batches.rb +13 -11
  153. data/lib/active_record/relation/calculations.rb +54 -48
  154. data/lib/active_record/relation/delegation.rb +33 -49
  155. data/lib/active_record/relation/finder_methods.rb +23 -28
  156. data/lib/active_record/relation/from_clause.rb +4 -0
  157. data/lib/active_record/relation/merger.rb +11 -21
  158. data/lib/active_record/relation/predicate_builder.rb +5 -11
  159. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  160. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  161. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  162. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  163. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  164. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  165. data/lib/active_record/relation/query_attribute.rb +13 -8
  166. data/lib/active_record/relation/query_methods.rb +221 -70
  167. data/lib/active_record/relation/spawn_methods.rb +1 -2
  168. data/lib/active_record/relation/where_clause.rb +14 -11
  169. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  170. data/lib/active_record/result.rb +30 -12
  171. data/lib/active_record/sanitization.rb +32 -40
  172. data/lib/active_record/schema.rb +2 -11
  173. data/lib/active_record/schema_dumper.rb +22 -7
  174. data/lib/active_record/schema_migration.rb +6 -2
  175. data/lib/active_record/scoping.rb +8 -9
  176. data/lib/active_record/scoping/default.rb +4 -6
  177. data/lib/active_record/scoping/named.rb +21 -17
  178. data/lib/active_record/statement_cache.rb +30 -3
  179. data/lib/active_record/store.rb +87 -8
  180. data/lib/active_record/suppressor.rb +2 -2
  181. data/lib/active_record/table_metadata.rb +23 -15
  182. data/lib/active_record/tasks/database_tasks.rb +194 -25
  183. data/lib/active_record/tasks/mysql_database_tasks.rb +5 -6
  184. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -8
  185. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -9
  186. data/lib/active_record/test_databases.rb +23 -0
  187. data/lib/active_record/test_fixtures.rb +225 -0
  188. data/lib/active_record/timestamp.rb +39 -26
  189. data/lib/active_record/touch_later.rb +5 -4
  190. data/lib/active_record/transactions.rb +64 -73
  191. data/lib/active_record/translation.rb +1 -1
  192. data/lib/active_record/type.rb +3 -5
  193. data/lib/active_record/type/adapter_specific_registry.rb +3 -13
  194. data/lib/active_record/type/hash_lookup_type_map.rb +0 -1
  195. data/lib/active_record/type/serialized.rb +0 -1
  196. data/lib/active_record/type/type_map.rb +0 -1
  197. data/lib/active_record/type/unsigned_integer.rb +0 -1
  198. data/lib/active_record/type_caster/connection.rb +15 -14
  199. data/lib/active_record/type_caster/map.rb +1 -4
  200. data/lib/active_record/validations.rb +3 -3
  201. data/lib/active_record/validations/associated.rb +1 -2
  202. data/lib/active_record/validations/uniqueness.rb +15 -27
  203. data/lib/arel.rb +62 -0
  204. data/lib/arel/alias_predication.rb +9 -0
  205. data/lib/arel/attributes.rb +22 -0
  206. data/lib/arel/attributes/attribute.rb +37 -0
  207. data/lib/arel/collectors/bind.rb +24 -0
  208. data/lib/arel/collectors/composite.rb +31 -0
  209. data/lib/arel/collectors/plain_string.rb +20 -0
  210. data/lib/arel/collectors/sql_string.rb +20 -0
  211. data/lib/arel/collectors/substitute_binds.rb +28 -0
  212. data/lib/arel/crud.rb +42 -0
  213. data/lib/arel/delete_manager.rb +18 -0
  214. data/lib/arel/errors.rb +9 -0
  215. data/lib/arel/expressions.rb +29 -0
  216. data/lib/arel/factory_methods.rb +49 -0
  217. data/lib/arel/insert_manager.rb +49 -0
  218. data/lib/arel/math.rb +45 -0
  219. data/lib/arel/nodes.rb +68 -0
  220. data/lib/arel/nodes/and.rb +32 -0
  221. data/lib/arel/nodes/ascending.rb +23 -0
  222. data/lib/arel/nodes/binary.rb +52 -0
  223. data/lib/arel/nodes/bind_param.rb +36 -0
  224. data/lib/arel/nodes/case.rb +55 -0
  225. data/lib/arel/nodes/casted.rb +50 -0
  226. data/lib/arel/nodes/comment.rb +29 -0
  227. data/lib/arel/nodes/count.rb +12 -0
  228. data/lib/arel/nodes/delete_statement.rb +45 -0
  229. data/lib/arel/nodes/descending.rb +23 -0
  230. data/lib/arel/nodes/equality.rb +18 -0
  231. data/lib/arel/nodes/extract.rb +24 -0
  232. data/lib/arel/nodes/false.rb +16 -0
  233. data/lib/arel/nodes/full_outer_join.rb +8 -0
  234. data/lib/arel/nodes/function.rb +44 -0
  235. data/lib/arel/nodes/grouping.rb +8 -0
  236. data/lib/arel/nodes/in.rb +8 -0
  237. data/lib/arel/nodes/infix_operation.rb +80 -0
  238. data/lib/arel/nodes/inner_join.rb +8 -0
  239. data/lib/arel/nodes/insert_statement.rb +37 -0
  240. data/lib/arel/nodes/join_source.rb +20 -0
  241. data/lib/arel/nodes/matches.rb +18 -0
  242. data/lib/arel/nodes/named_function.rb +23 -0
  243. data/lib/arel/nodes/node.rb +50 -0
  244. data/lib/arel/nodes/node_expression.rb +13 -0
  245. data/lib/arel/nodes/outer_join.rb +8 -0
  246. data/lib/arel/nodes/over.rb +15 -0
  247. data/lib/arel/nodes/regexp.rb +16 -0
  248. data/lib/arel/nodes/right_outer_join.rb +8 -0
  249. data/lib/arel/nodes/select_core.rb +67 -0
  250. data/lib/arel/nodes/select_statement.rb +41 -0
  251. data/lib/arel/nodes/sql_literal.rb +16 -0
  252. data/lib/arel/nodes/string_join.rb +11 -0
  253. data/lib/arel/nodes/table_alias.rb +27 -0
  254. data/lib/arel/nodes/terminal.rb +16 -0
  255. data/lib/arel/nodes/true.rb +16 -0
  256. data/lib/arel/nodes/unary.rb +45 -0
  257. data/lib/arel/nodes/unary_operation.rb +20 -0
  258. data/lib/arel/nodes/unqualified_column.rb +22 -0
  259. data/lib/arel/nodes/update_statement.rb +41 -0
  260. data/lib/arel/nodes/values_list.rb +9 -0
  261. data/lib/arel/nodes/window.rb +126 -0
  262. data/lib/arel/nodes/with.rb +11 -0
  263. data/lib/arel/order_predications.rb +13 -0
  264. data/lib/arel/predications.rb +256 -0
  265. data/lib/arel/select_manager.rb +271 -0
  266. data/lib/arel/table.rb +110 -0
  267. data/lib/arel/tree_manager.rb +72 -0
  268. data/lib/arel/update_manager.rb +34 -0
  269. data/lib/arel/visitors.rb +20 -0
  270. data/lib/arel/visitors/depth_first.rb +203 -0
  271. data/lib/arel/visitors/dot.rb +296 -0
  272. data/lib/arel/visitors/ibm_db.rb +34 -0
  273. data/lib/arel/visitors/informix.rb +62 -0
  274. data/lib/arel/visitors/mssql.rb +156 -0
  275. data/lib/arel/visitors/mysql.rb +83 -0
  276. data/lib/arel/visitors/oracle.rb +158 -0
  277. data/lib/arel/visitors/oracle12.rb +65 -0
  278. data/lib/arel/visitors/postgresql.rb +109 -0
  279. data/lib/arel/visitors/sqlite.rb +38 -0
  280. data/lib/arel/visitors/to_sql.rb +888 -0
  281. data/lib/arel/visitors/visitor.rb +45 -0
  282. data/lib/arel/visitors/where_sql.rb +22 -0
  283. data/lib/arel/window_predications.rb +9 -0
  284. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +0 -1
  285. data/lib/rails/generators/active_record/migration.rb +14 -2
  286. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  287. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +1 -1
  288. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +4 -2
  289. data/lib/rails/generators/active_record/model/model_generator.rb +1 -1
  290. data/lib/rails/generators/active_record/model/templates/model.rb.tt +10 -1
  291. metadata +115 -29
  292. data/lib/active_record/collection_cache_key.rb +0 -53
@@ -3,6 +3,8 @@
3
3
  module ActiveRecord
4
4
  module Tasks # :nodoc:
5
5
  class MySQLDatabaseTasks # :nodoc:
6
+ ER_DB_CREATE_EXISTS = 1007
7
+
6
8
  delegate :connection, :establish_connection, to: ActiveRecord::Base
7
9
 
8
10
  def initialize(configuration)
@@ -14,7 +16,7 @@ module ActiveRecord
14
16
  connection.create_database configuration["database"], creation_options
15
17
  establish_connection configuration
16
18
  rescue ActiveRecord::StatementInvalid => error
17
- if error.message.include?("database exists")
19
+ if connection.error_number(error.cause) == ER_DB_CREATE_EXISTS
18
20
  raise DatabaseAlreadyExists
19
21
  else
20
22
  raise
@@ -67,10 +69,7 @@ module ActiveRecord
67
69
  end
68
70
 
69
71
  private
70
-
71
- def configuration
72
- @configuration
73
- end
72
+ attr_reader :configuration
74
73
 
75
74
  def configuration_without_database
76
75
  configuration.merge("database" => nil)
@@ -106,7 +105,7 @@ module ActiveRecord
106
105
  end
107
106
 
108
107
  def run_cmd_error(cmd, args, action)
109
- msg = "failed to execute: `#{cmd}`\n".dup
108
+ msg = +"failed to execute: `#{cmd}`\n"
110
109
  msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
111
110
  msg
112
111
  end
@@ -6,8 +6,8 @@ module ActiveRecord
6
6
  module Tasks # :nodoc:
7
7
  class PostgreSQLDatabaseTasks # :nodoc:
8
8
  DEFAULT_ENCODING = ENV["CHARSET"] || "utf8"
9
- ON_ERROR_STOP_1 = "ON_ERROR_STOP=1".freeze
10
- SQL_COMMENT_BEGIN = "--".freeze
9
+ ON_ERROR_STOP_1 = "ON_ERROR_STOP=1"
10
+ SQL_COMMENT_BEGIN = "--"
11
11
 
12
12
  delegate :connection, :establish_connection, :clear_active_connections!,
13
13
  to: ActiveRecord::Base
@@ -82,17 +82,14 @@ module ActiveRecord
82
82
 
83
83
  def structure_load(filename, extra_flags)
84
84
  set_psql_env
85
- args = ["-v", ON_ERROR_STOP_1, "-q", "-f", filename]
85
+ args = ["-v", ON_ERROR_STOP_1, "-q", "-X", "-f", filename]
86
86
  args.concat(Array(extra_flags)) if extra_flags
87
87
  args << configuration["database"]
88
88
  run_cmd("psql", args, "loading")
89
89
  end
90
90
 
91
91
  private
92
-
93
- def configuration
94
- @configuration
95
- end
92
+ attr_reader :configuration
96
93
 
97
94
  def encoding
98
95
  configuration["encoding"] || DEFAULT_ENCODING
@@ -117,7 +114,7 @@ module ActiveRecord
117
114
  end
118
115
 
119
116
  def run_cmd_error(cmd, args, action)
120
- msg = "failed to execute:\n".dup
117
+ msg = +"failed to execute:\n"
121
118
  msg << "#{cmd} #{args.join(' ')}\n\n"
122
119
  msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
123
120
  msg
@@ -59,21 +59,14 @@ module ActiveRecord
59
59
  end
60
60
 
61
61
  private
62
-
63
- def configuration
64
- @configuration
65
- end
66
-
67
- def root
68
- @root
69
- end
62
+ attr_reader :configuration, :root
70
63
 
71
64
  def run_cmd(cmd, args, out)
72
65
  fail run_cmd_error(cmd, args) unless Kernel.system(cmd, *args, out: out)
73
66
  end
74
67
 
75
68
  def run_cmd_error(cmd, args)
76
- msg = "failed to execute:\n".dup
69
+ msg = +"failed to execute:\n"
77
70
  msg << "#{cmd} #{args.join(' ')}\n\n"
78
71
  msg << "Please check the output above for any errors and make sure that `#{cmd}` is installed in your PATH and has proper permissions.\n\n"
79
72
  msg
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/testing/parallelization"
4
+
5
+ module ActiveRecord
6
+ module TestDatabases # :nodoc:
7
+ ActiveSupport::Testing::Parallelization.after_fork_hook do |i|
8
+ create_and_load_schema(i, env_name: Rails.env)
9
+ end
10
+
11
+ def self.create_and_load_schema(i, env_name:)
12
+ old, ENV["VERBOSE"] = ENV["VERBOSE"], "false"
13
+
14
+ ActiveRecord::Base.configurations.configs_for(env_name: env_name).each do |db_config|
15
+ db_config.config["database"] += "-#{i}"
16
+ ActiveRecord::Tasks::DatabaseTasks.reconstruct_from_schema(db_config.config, ActiveRecord::Base.schema_format, nil, env_name, db_config.spec_name)
17
+ end
18
+ ensure
19
+ ActiveRecord::Base.establish_connection(Rails.env.to_sym)
20
+ ENV["VERBOSE"] = old
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,225 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveRecord
4
+ module TestFixtures
5
+ extend ActiveSupport::Concern
6
+
7
+ def before_setup # :nodoc:
8
+ setup_fixtures
9
+ super
10
+ end
11
+
12
+ def after_teardown # :nodoc:
13
+ super
14
+ teardown_fixtures
15
+ end
16
+
17
+ included do
18
+ class_attribute :fixture_path, instance_writer: false
19
+ class_attribute :fixture_table_names, default: []
20
+ class_attribute :fixture_class_names, default: {}
21
+ class_attribute :use_transactional_tests, default: true
22
+ class_attribute :use_instantiated_fixtures, default: false # true, false, or :no_instances
23
+ class_attribute :pre_loaded_fixtures, default: false
24
+ class_attribute :config, default: ActiveRecord::Base
25
+ class_attribute :lock_threads, default: true
26
+ end
27
+
28
+ module ClassMethods
29
+ # Sets the model class for a fixture when the class name cannot be inferred from the fixture name.
30
+ #
31
+ # Examples:
32
+ #
33
+ # set_fixture_class some_fixture: SomeModel,
34
+ # 'namespaced/fixture' => Another::Model
35
+ #
36
+ # The keys must be the fixture names, that coincide with the short paths to the fixture files.
37
+ def set_fixture_class(class_names = {})
38
+ self.fixture_class_names = fixture_class_names.merge(class_names.stringify_keys)
39
+ end
40
+
41
+ def fixtures(*fixture_set_names)
42
+ if fixture_set_names.first == :all
43
+ raise StandardError, "No fixture path found. Please set `#{self}.fixture_path`." if fixture_path.blank?
44
+ fixture_set_names = Dir["#{fixture_path}/{**,*}/*.{yml}"].uniq
45
+ fixture_set_names.map! { |f| f[(fixture_path.to_s.size + 1)..-5] }
46
+ else
47
+ fixture_set_names = fixture_set_names.flatten.map(&:to_s)
48
+ end
49
+
50
+ self.fixture_table_names |= fixture_set_names
51
+ setup_fixture_accessors(fixture_set_names)
52
+ end
53
+
54
+ def setup_fixture_accessors(fixture_set_names = nil)
55
+ fixture_set_names = Array(fixture_set_names || fixture_table_names)
56
+ methods = Module.new do
57
+ fixture_set_names.each do |fs_name|
58
+ fs_name = fs_name.to_s
59
+ accessor_name = fs_name.tr("/", "_").to_sym
60
+
61
+ define_method(accessor_name) do |*fixture_names|
62
+ force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
63
+ return_single_record = fixture_names.size == 1
64
+ fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty?
65
+
66
+ @fixture_cache[fs_name] ||= {}
67
+
68
+ instances = fixture_names.map do |f_name|
69
+ f_name = f_name.to_s if f_name.is_a?(Symbol)
70
+ @fixture_cache[fs_name].delete(f_name) if force_reload
71
+
72
+ if @loaded_fixtures[fs_name][f_name]
73
+ @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
74
+ else
75
+ raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
76
+ end
77
+ end
78
+
79
+ return_single_record ? instances.first : instances
80
+ end
81
+ private accessor_name
82
+ end
83
+ end
84
+ include methods
85
+ end
86
+
87
+ def uses_transaction(*methods)
88
+ @uses_transaction = [] unless defined?(@uses_transaction)
89
+ @uses_transaction.concat methods.map(&:to_s)
90
+ end
91
+
92
+ def uses_transaction?(method)
93
+ @uses_transaction = [] unless defined?(@uses_transaction)
94
+ @uses_transaction.include?(method.to_s)
95
+ end
96
+ end
97
+
98
+ def run_in_transaction?
99
+ use_transactional_tests &&
100
+ !self.class.uses_transaction?(method_name)
101
+ end
102
+
103
+ def setup_fixtures(config = ActiveRecord::Base)
104
+ if pre_loaded_fixtures && !use_transactional_tests
105
+ raise RuntimeError, "pre_loaded_fixtures requires use_transactional_tests"
106
+ end
107
+
108
+ @fixture_cache = {}
109
+ @fixture_connections = []
110
+ @@already_loaded_fixtures ||= {}
111
+ @connection_subscriber = nil
112
+
113
+ # Load fixtures once and begin transaction.
114
+ if run_in_transaction?
115
+ if @@already_loaded_fixtures[self.class]
116
+ @loaded_fixtures = @@already_loaded_fixtures[self.class]
117
+ else
118
+ @loaded_fixtures = load_fixtures(config)
119
+ @@already_loaded_fixtures[self.class] = @loaded_fixtures
120
+ end
121
+
122
+ # Begin transactions for connections already established
123
+ @fixture_connections = enlist_fixture_connections
124
+ @fixture_connections.each do |connection|
125
+ connection.begin_transaction joinable: false, _lazy: false
126
+ connection.pool.lock_thread = true if lock_threads
127
+ end
128
+
129
+ # When connections are established in the future, begin a transaction too
130
+ @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
131
+ spec_name = payload[:spec_name] if payload.key?(:spec_name)
132
+ setup_shared_connection_pool
133
+
134
+ if spec_name
135
+ begin
136
+ connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name)
137
+ rescue ConnectionNotEstablished
138
+ connection = nil
139
+ end
140
+
141
+ if connection && !@fixture_connections.include?(connection)
142
+ connection.begin_transaction joinable: false, _lazy: false
143
+ connection.pool.lock_thread = true if lock_threads
144
+ @fixture_connections << connection
145
+ end
146
+ end
147
+ end
148
+
149
+ # Load fixtures for every test.
150
+ else
151
+ ActiveRecord::FixtureSet.reset_cache
152
+ @@already_loaded_fixtures[self.class] = nil
153
+ @loaded_fixtures = load_fixtures(config)
154
+ end
155
+
156
+ # Instantiate fixtures for every test if requested.
157
+ instantiate_fixtures if use_instantiated_fixtures
158
+ end
159
+
160
+ def teardown_fixtures
161
+ # Rollback changes if a transaction is active.
162
+ if run_in_transaction?
163
+ ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber
164
+ @fixture_connections.each do |connection|
165
+ connection.rollback_transaction if connection.transaction_open?
166
+ connection.pool.lock_thread = false
167
+ end
168
+ @fixture_connections.clear
169
+ else
170
+ ActiveRecord::FixtureSet.reset_cache
171
+ end
172
+
173
+ ActiveRecord::Base.clear_active_connections!
174
+ end
175
+
176
+ def enlist_fixture_connections
177
+ setup_shared_connection_pool
178
+
179
+ ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
180
+ end
181
+
182
+ private
183
+ # Shares the writing connection pool with connections on
184
+ # other handlers.
185
+ #
186
+ # In an application with a primary and replica the test fixtures
187
+ # need to share a connection pool so that the reading connection
188
+ # can see data in the open transaction on the writing connection.
189
+ def setup_shared_connection_pool
190
+ writing_handler = ActiveRecord::Base.connection_handler
191
+
192
+ ActiveRecord::Base.connection_handlers.values.each do |handler|
193
+ if handler != writing_handler
194
+ handler.connection_pool_list.each do |pool|
195
+ name = pool.spec.name
196
+ writing_connection = writing_handler.retrieve_connection_pool(name)
197
+ return unless writing_connection
198
+ handler.send(:owner_to_pool)[name] = writing_connection
199
+ end
200
+ end
201
+ end
202
+ end
203
+
204
+ def load_fixtures(config)
205
+ fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config)
206
+ Hash[fixtures.map { |f| [f.name, f] }]
207
+ end
208
+
209
+ def instantiate_fixtures
210
+ if pre_loaded_fixtures
211
+ raise RuntimeError, "Load fixtures before instantiating them." if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
212
+ ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
213
+ else
214
+ raise RuntimeError, "Load fixtures before instantiating them." if @loaded_fixtures.nil?
215
+ @loaded_fixtures.each_value do |fixture_set|
216
+ ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
217
+ end
218
+ end
219
+ end
220
+
221
+ def load_instances?
222
+ use_instantiated_fixtures != :no_instances
223
+ end
224
+ end
225
+ end
@@ -56,23 +56,29 @@ module ActiveRecord
56
56
  def touch_attributes_with_time(*names, time: nil)
57
57
  attribute_names = timestamp_attributes_for_update_in_model
58
58
  attribute_names |= names.map(&:to_s)
59
- time ||= current_time_from_proper_timezone
60
- attribute_names.each_with_object({}) { |attr_name, result| result[attr_name] = time }
59
+ attribute_names.index_with(time || current_time_from_proper_timezone)
61
60
  end
62
61
 
63
- private
64
- def timestamp_attributes_for_create_in_model
65
- timestamp_attributes_for_create.select { |c| column_names.include?(c) }
66
- end
62
+ def timestamp_attributes_for_create_in_model
63
+ @timestamp_attributes_for_create_in_model ||=
64
+ (timestamp_attributes_for_create & column_names).freeze
65
+ end
67
66
 
68
- def timestamp_attributes_for_update_in_model
69
- timestamp_attributes_for_update.select { |c| column_names.include?(c) }
70
- end
67
+ def timestamp_attributes_for_update_in_model
68
+ @timestamp_attributes_for_update_in_model ||=
69
+ (timestamp_attributes_for_update & column_names).freeze
70
+ end
71
71
 
72
- def all_timestamp_attributes_in_model
73
- timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model
74
- end
72
+ def all_timestamp_attributes_in_model
73
+ @all_timestamp_attributes_in_model ||=
74
+ (timestamp_attributes_for_create_in_model + timestamp_attributes_for_update_in_model).freeze
75
+ end
76
+
77
+ def current_time_from_proper_timezone
78
+ default_timezone == :utc ? Time.now.utc : Time.now
79
+ end
75
80
 
81
+ private
76
82
  def timestamp_attributes_for_create
77
83
  ["created_at", "created_on"]
78
84
  end
@@ -81,13 +87,15 @@ module ActiveRecord
81
87
  ["updated_at", "updated_on"]
82
88
  end
83
89
 
84
- def current_time_from_proper_timezone
85
- default_timezone == :utc ? Time.now.utc : Time.now
90
+ def reload_schema_from_cache
91
+ @timestamp_attributes_for_create_in_model = nil
92
+ @timestamp_attributes_for_update_in_model = nil
93
+ @all_timestamp_attributes_in_model = nil
94
+ super
86
95
  end
87
96
  end
88
97
 
89
98
  private
90
-
91
99
  def _create_record
92
100
  if record_timestamps
93
101
  current_time = current_time_from_proper_timezone
@@ -102,8 +110,8 @@ module ActiveRecord
102
110
  super
103
111
  end
104
112
 
105
- def _update_record(*args, touch: true, **options)
106
- if touch && should_record_timestamps?
113
+ def _update_record
114
+ if @_touch_record && should_record_timestamps?
107
115
  current_time = current_time_from_proper_timezone
108
116
 
109
117
  timestamp_attributes_for_update_in_model.each do |column|
@@ -111,7 +119,13 @@ module ActiveRecord
111
119
  _write_attribute(column, current_time)
112
120
  end
113
121
  end
114
- super(*args)
122
+
123
+ super
124
+ end
125
+
126
+ def create_or_update(touch: true, **)
127
+ @_touch_record = touch
128
+ super
115
129
  end
116
130
 
117
131
  def should_record_timestamps?
@@ -119,26 +133,25 @@ module ActiveRecord
119
133
  end
120
134
 
121
135
  def timestamp_attributes_for_create_in_model
122
- self.class.send(:timestamp_attributes_for_create_in_model)
136
+ self.class.timestamp_attributes_for_create_in_model
123
137
  end
124
138
 
125
139
  def timestamp_attributes_for_update_in_model
126
- self.class.send(:timestamp_attributes_for_update_in_model)
140
+ self.class.timestamp_attributes_for_update_in_model
127
141
  end
128
142
 
129
143
  def all_timestamp_attributes_in_model
130
- self.class.send(:all_timestamp_attributes_in_model)
144
+ self.class.all_timestamp_attributes_in_model
131
145
  end
132
146
 
133
147
  def current_time_from_proper_timezone
134
- self.class.send(:current_time_from_proper_timezone)
148
+ self.class.current_time_from_proper_timezone
135
149
  end
136
150
 
137
- def max_updated_column_timestamp(timestamp_names = timestamp_attributes_for_update_in_model)
138
- timestamp_names
139
- .map { |attr| self[attr] }
151
+ def max_updated_column_timestamp
152
+ timestamp_attributes_for_update_in_model
153
+ .map { |attr| self[attr]&.to_time }
140
154
  .compact
141
- .map(&:to_time)
142
155
  .max
143
156
  end
144
157