activerecord 5.2.4.5 → 6.0.0.beta1

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of activerecord might be problematic. Click here for more details.

Files changed (241) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +299 -739
  3. data/MIT-LICENSE +3 -1
  4. data/README.rdoc +1 -1
  5. data/examples/performance.rb +1 -1
  6. data/lib/active_record.rb +2 -1
  7. data/lib/active_record/aggregations.rb +4 -2
  8. data/lib/active_record/associations.rb +16 -12
  9. data/lib/active_record/associations/association.rb +35 -19
  10. data/lib/active_record/associations/association_scope.rb +4 -6
  11. data/lib/active_record/associations/belongs_to_association.rb +36 -42
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +0 -4
  13. data/lib/active_record/associations/builder/belongs_to.rb +14 -50
  14. data/lib/active_record/associations/builder/collection_association.rb +3 -3
  15. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +17 -38
  16. data/lib/active_record/associations/collection_association.rb +11 -25
  17. data/lib/active_record/associations/collection_proxy.rb +32 -6
  18. data/lib/active_record/associations/foreign_association.rb +7 -0
  19. data/lib/active_record/associations/has_many_association.rb +1 -1
  20. data/lib/active_record/associations/has_many_through_association.rb +25 -18
  21. data/lib/active_record/associations/has_one_association.rb +28 -30
  22. data/lib/active_record/associations/has_one_through_association.rb +5 -5
  23. data/lib/active_record/associations/join_dependency.rb +15 -20
  24. data/lib/active_record/associations/join_dependency/join_association.rb +11 -26
  25. data/lib/active_record/associations/join_dependency/join_part.rb +2 -2
  26. data/lib/active_record/associations/preloader.rb +32 -29
  27. data/lib/active_record/associations/preloader/association.rb +1 -2
  28. data/lib/active_record/associations/singular_association.rb +2 -16
  29. data/lib/active_record/attribute_assignment.rb +7 -10
  30. data/lib/active_record/attribute_methods.rb +34 -56
  31. data/lib/active_record/attribute_methods/dirty.rb +64 -26
  32. data/lib/active_record/attribute_methods/primary_key.rb +8 -7
  33. data/lib/active_record/attribute_methods/read.rb +16 -48
  34. data/lib/active_record/attribute_methods/serialization.rb +1 -1
  35. data/lib/active_record/attribute_methods/time_zone_conversion.rb +1 -1
  36. data/lib/active_record/attribute_methods/write.rb +15 -16
  37. data/lib/active_record/autosave_association.rb +7 -21
  38. data/lib/active_record/base.rb +2 -2
  39. data/lib/active_record/callbacks.rb +3 -17
  40. data/lib/active_record/collection_cache_key.rb +1 -1
  41. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +13 -36
  42. data/lib/active_record/connection_adapters/abstract/database_limits.rb +9 -0
  43. data/lib/active_record/connection_adapters/abstract/database_statements.rb +25 -84
  44. data/lib/active_record/connection_adapters/abstract/query_cache.rb +17 -14
  45. data/lib/active_record/connection_adapters/abstract/quoting.rb +5 -11
  46. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +15 -11
  47. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +30 -13
  48. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +0 -2
  49. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +41 -27
  50. data/lib/active_record/connection_adapters/abstract/transaction.rb +81 -52
  51. data/lib/active_record/connection_adapters/abstract_adapter.rb +95 -31
  52. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +65 -90
  53. data/lib/active_record/connection_adapters/connection_specification.rb +52 -42
  54. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +5 -9
  55. data/lib/active_record/connection_adapters/mysql/database_statements.rb +29 -7
  56. data/lib/active_record/connection_adapters/mysql/quoting.rb +1 -1
  57. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +3 -4
  58. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +65 -10
  59. data/lib/active_record/connection_adapters/mysql2_adapter.rb +8 -4
  60. data/lib/active_record/connection_adapters/postgresql/column.rb +1 -2
  61. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +16 -1
  62. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +1 -1
  63. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +1 -4
  64. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +1 -1
  65. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +1 -1
  66. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +2 -2
  67. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +1 -1
  68. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +9 -7
  69. data/lib/active_record/connection_adapters/postgresql/quoting.rb +4 -4
  70. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +11 -36
  71. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +9 -2
  72. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +38 -20
  73. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +2 -1
  74. data/lib/active_record/connection_adapters/postgresql_adapter.rb +75 -56
  75. data/lib/active_record/connection_adapters/schema_cache.rb +5 -0
  76. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +5 -5
  77. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +14 -9
  78. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +95 -62
  79. data/lib/active_record/connection_handling.rb +132 -26
  80. data/lib/active_record/core.rb +76 -43
  81. data/lib/active_record/counter_cache.rb +4 -29
  82. data/lib/active_record/database_configurations.rb +184 -0
  83. data/lib/active_record/database_configurations/database_config.rb +37 -0
  84. data/lib/active_record/database_configurations/hash_config.rb +50 -0
  85. data/lib/active_record/database_configurations/url_config.rb +74 -0
  86. data/lib/active_record/enum.rb +22 -7
  87. data/lib/active_record/errors.rb +24 -21
  88. data/lib/active_record/explain.rb +1 -1
  89. data/lib/active_record/fixture_set/model_metadata.rb +33 -0
  90. data/lib/active_record/fixture_set/render_context.rb +17 -0
  91. data/lib/active_record/fixture_set/table_row.rb +153 -0
  92. data/lib/active_record/fixture_set/table_rows.rb +47 -0
  93. data/lib/active_record/fixtures.rb +140 -472
  94. data/lib/active_record/gem_version.rb +4 -4
  95. data/lib/active_record/inheritance.rb +12 -2
  96. data/lib/active_record/integration.rb +56 -16
  97. data/lib/active_record/internal_metadata.rb +5 -1
  98. data/lib/active_record/locking/optimistic.rb +2 -2
  99. data/lib/active_record/locking/pessimistic.rb +3 -3
  100. data/lib/active_record/log_subscriber.rb +7 -26
  101. data/lib/active_record/migration.rb +38 -37
  102. data/lib/active_record/migration/command_recorder.rb +35 -5
  103. data/lib/active_record/migration/compatibility.rb +34 -16
  104. data/lib/active_record/model_schema.rb +30 -9
  105. data/lib/active_record/nested_attributes.rb +2 -2
  106. data/lib/active_record/no_touching.rb +7 -0
  107. data/lib/active_record/persistence.rb +18 -7
  108. data/lib/active_record/query_cache.rb +11 -4
  109. data/lib/active_record/querying.rb +19 -11
  110. data/lib/active_record/railtie.rb +71 -42
  111. data/lib/active_record/railties/collection_cache_association_loading.rb +34 -0
  112. data/lib/active_record/railties/controller_runtime.rb +30 -35
  113. data/lib/active_record/railties/databases.rake +94 -43
  114. data/lib/active_record/reflection.rb +60 -44
  115. data/lib/active_record/relation.rb +150 -69
  116. data/lib/active_record/relation/batches.rb +13 -10
  117. data/lib/active_record/relation/calculations.rb +38 -28
  118. data/lib/active_record/relation/delegation.rb +4 -13
  119. data/lib/active_record/relation/finder_methods.rb +12 -25
  120. data/lib/active_record/relation/merger.rb +2 -6
  121. data/lib/active_record/relation/predicate_builder.rb +4 -6
  122. data/lib/active_record/relation/predicate_builder/array_handler.rb +5 -4
  123. data/lib/active_record/relation/predicate_builder/association_query_value.rb +1 -4
  124. data/lib/active_record/relation/predicate_builder/base_handler.rb +1 -2
  125. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +1 -2
  126. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +1 -4
  127. data/lib/active_record/relation/predicate_builder/range_handler.rb +3 -23
  128. data/lib/active_record/relation/query_attribute.rb +15 -12
  129. data/lib/active_record/relation/query_methods.rb +29 -52
  130. data/lib/active_record/relation/where_clause.rb +4 -0
  131. data/lib/active_record/relation/where_clause_factory.rb +1 -2
  132. data/lib/active_record/result.rb +30 -11
  133. data/lib/active_record/sanitization.rb +2 -39
  134. data/lib/active_record/schema.rb +1 -10
  135. data/lib/active_record/schema_dumper.rb +12 -6
  136. data/lib/active_record/schema_migration.rb +4 -0
  137. data/lib/active_record/scoping.rb +9 -8
  138. data/lib/active_record/scoping/default.rb +10 -3
  139. data/lib/active_record/scoping/named.rb +10 -14
  140. data/lib/active_record/statement_cache.rb +32 -5
  141. data/lib/active_record/store.rb +39 -8
  142. data/lib/active_record/table_metadata.rb +1 -4
  143. data/lib/active_record/tasks/database_tasks.rb +89 -23
  144. data/lib/active_record/tasks/mysql_database_tasks.rb +2 -4
  145. data/lib/active_record/tasks/postgresql_database_tasks.rb +5 -7
  146. data/lib/active_record/tasks/sqlite_database_tasks.rb +2 -8
  147. data/lib/active_record/test_databases.rb +38 -0
  148. data/lib/active_record/test_fixtures.rb +224 -0
  149. data/lib/active_record/timestamp.rb +4 -6
  150. data/lib/active_record/transactions.rb +3 -22
  151. data/lib/active_record/translation.rb +1 -1
  152. data/lib/active_record/type.rb +3 -4
  153. data/lib/active_record/type/adapter_specific_registry.rb +1 -8
  154. data/lib/active_record/type_caster/connection.rb +1 -6
  155. data/lib/active_record/type_caster/map.rb +1 -4
  156. data/lib/active_record/validations/uniqueness.rb +13 -25
  157. data/lib/arel.rb +44 -0
  158. data/lib/arel/alias_predication.rb +9 -0
  159. data/lib/arel/attributes.rb +22 -0
  160. data/lib/arel/attributes/attribute.rb +37 -0
  161. data/lib/arel/collectors/bind.rb +24 -0
  162. data/lib/arel/collectors/composite.rb +31 -0
  163. data/lib/arel/collectors/plain_string.rb +20 -0
  164. data/lib/arel/collectors/sql_string.rb +20 -0
  165. data/lib/arel/collectors/substitute_binds.rb +28 -0
  166. data/lib/arel/crud.rb +42 -0
  167. data/lib/arel/delete_manager.rb +18 -0
  168. data/lib/arel/errors.rb +9 -0
  169. data/lib/arel/expressions.rb +29 -0
  170. data/lib/arel/factory_methods.rb +49 -0
  171. data/lib/arel/insert_manager.rb +49 -0
  172. data/lib/arel/math.rb +45 -0
  173. data/lib/arel/nodes.rb +67 -0
  174. data/lib/arel/nodes/and.rb +32 -0
  175. data/lib/arel/nodes/ascending.rb +23 -0
  176. data/lib/arel/nodes/binary.rb +52 -0
  177. data/lib/arel/nodes/bind_param.rb +36 -0
  178. data/lib/arel/nodes/case.rb +55 -0
  179. data/lib/arel/nodes/casted.rb +50 -0
  180. data/lib/arel/nodes/count.rb +12 -0
  181. data/lib/arel/nodes/delete_statement.rb +45 -0
  182. data/lib/arel/nodes/descending.rb +23 -0
  183. data/lib/arel/nodes/equality.rb +18 -0
  184. data/lib/arel/nodes/extract.rb +24 -0
  185. data/lib/arel/nodes/false.rb +16 -0
  186. data/lib/arel/nodes/full_outer_join.rb +8 -0
  187. data/lib/arel/nodes/function.rb +44 -0
  188. data/lib/arel/nodes/grouping.rb +8 -0
  189. data/lib/arel/nodes/in.rb +8 -0
  190. data/lib/arel/nodes/infix_operation.rb +80 -0
  191. data/lib/arel/nodes/inner_join.rb +8 -0
  192. data/lib/arel/nodes/insert_statement.rb +37 -0
  193. data/lib/arel/nodes/join_source.rb +20 -0
  194. data/lib/arel/nodes/matches.rb +18 -0
  195. data/lib/arel/nodes/named_function.rb +23 -0
  196. data/lib/arel/nodes/node.rb +50 -0
  197. data/lib/arel/nodes/node_expression.rb +13 -0
  198. data/lib/arel/nodes/outer_join.rb +8 -0
  199. data/lib/arel/nodes/over.rb +15 -0
  200. data/lib/arel/nodes/regexp.rb +16 -0
  201. data/lib/arel/nodes/right_outer_join.rb +8 -0
  202. data/lib/arel/nodes/select_core.rb +63 -0
  203. data/lib/arel/nodes/select_statement.rb +41 -0
  204. data/lib/arel/nodes/sql_literal.rb +16 -0
  205. data/lib/arel/nodes/string_join.rb +11 -0
  206. data/lib/arel/nodes/table_alias.rb +27 -0
  207. data/lib/arel/nodes/terminal.rb +16 -0
  208. data/lib/arel/nodes/true.rb +16 -0
  209. data/lib/arel/nodes/unary.rb +44 -0
  210. data/lib/arel/nodes/unary_operation.rb +20 -0
  211. data/lib/arel/nodes/unqualified_column.rb +22 -0
  212. data/lib/arel/nodes/update_statement.rb +41 -0
  213. data/lib/arel/nodes/values.rb +16 -0
  214. data/lib/arel/nodes/values_list.rb +24 -0
  215. data/lib/arel/nodes/window.rb +126 -0
  216. data/lib/arel/nodes/with.rb +11 -0
  217. data/lib/arel/order_predications.rb +13 -0
  218. data/lib/arel/predications.rb +257 -0
  219. data/lib/arel/select_manager.rb +271 -0
  220. data/lib/arel/table.rb +110 -0
  221. data/lib/arel/tree_manager.rb +72 -0
  222. data/lib/arel/update_manager.rb +34 -0
  223. data/lib/arel/visitors.rb +20 -0
  224. data/lib/arel/visitors/depth_first.rb +199 -0
  225. data/lib/arel/visitors/dot.rb +292 -0
  226. data/lib/arel/visitors/ibm_db.rb +21 -0
  227. data/lib/arel/visitors/informix.rb +56 -0
  228. data/lib/arel/visitors/mssql.rb +143 -0
  229. data/lib/arel/visitors/mysql.rb +83 -0
  230. data/lib/arel/visitors/oracle.rb +159 -0
  231. data/lib/arel/visitors/oracle12.rb +67 -0
  232. data/lib/arel/visitors/postgresql.rb +116 -0
  233. data/lib/arel/visitors/sqlite.rb +39 -0
  234. data/lib/arel/visitors/to_sql.rb +913 -0
  235. data/lib/arel/visitors/visitor.rb +42 -0
  236. data/lib/arel/visitors/where_sql.rb +23 -0
  237. data/lib/arel/window_predications.rb +9 -0
  238. data/lib/rails/generators/active_record/migration.rb +14 -1
  239. data/lib/rails/generators/active_record/migration/migration_generator.rb +2 -5
  240. data/lib/rails/generators/active_record/model/model_generator.rb +1 -0
  241. metadata +104 -26
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record/fixture_set/table_row"
4
+ require "active_record/fixture_set/model_metadata"
5
+
6
+ module ActiveRecord
7
+ class FixtureSet
8
+ class TableRows # :nodoc:
9
+ def initialize(table_name, model_class:, fixtures:, config:)
10
+ @model_class = model_class
11
+
12
+ # track any join tables we need to insert later
13
+ @tables = Hash.new { |h, table| h[table] = [] }
14
+
15
+ # ensure this table is loaded before any HABTM associations
16
+ @tables[table_name] = nil
17
+
18
+ build_table_rows_from(table_name, fixtures, config)
19
+ end
20
+
21
+ attr_reader :tables, :model_class
22
+
23
+ def to_hash
24
+ @tables.transform_values { |rows| rows.map(&:to_hash) }
25
+ end
26
+
27
+ def model_metadata
28
+ @model_metadata ||= ModelMetadata.new(model_class)
29
+ end
30
+
31
+ private
32
+
33
+ def build_table_rows_from(table_name, fixtures, config)
34
+ now = config.default_timezone == :utc ? Time.now.utc : Time.now
35
+
36
+ @tables[table_name] = fixtures.map do |label, fixture|
37
+ TableRow.new(
38
+ fixture,
39
+ table_rows: self,
40
+ label: label,
41
+ now: now,
42
+ )
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -7,6 +7,9 @@ require "set"
7
7
  require "active_support/dependencies"
8
8
  require "active_support/core_ext/digest/uuid"
9
9
  require "active_record/fixture_set/file"
10
+ require "active_record/fixture_set/render_context"
11
+ require "active_record/fixture_set/table_rows"
12
+ require "active_record/test_fixtures"
10
13
  require "active_record/errors"
11
14
 
12
15
  module ActiveRecord
@@ -179,8 +182,8 @@ module ActiveRecord
179
182
  # end
180
183
  # end
181
184
  #
182
- # If you preload your test database with all fixture data (probably in the rake task) and use
183
- # transactional tests, then you may omit all fixtures declarations in your test cases since
185
+ # If you preload your test database with all fixture data (probably by running `rails db:fixtures:load`)
186
+ # and use transactional tests, then you may omit all fixtures declarations in your test cases since
184
187
  # all the data's already there and every case rolls back its changes.
185
188
  #
186
189
  # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to
@@ -440,60 +443,6 @@ module ActiveRecord
440
443
 
441
444
  @@all_cached_fixtures = Hash.new { |h, k| h[k] = {} }
442
445
 
443
- def self.default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
444
- config.pluralize_table_names ?
445
- fixture_set_name.singularize.camelize :
446
- fixture_set_name.camelize
447
- end
448
-
449
- def self.default_fixture_table_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
450
- "#{ config.table_name_prefix }"\
451
- "#{ fixture_set_name.tr('/', '_') }"\
452
- "#{ config.table_name_suffix }".to_sym
453
- end
454
-
455
- def self.reset_cache
456
- @@all_cached_fixtures.clear
457
- end
458
-
459
- def self.cache_for_connection(connection)
460
- @@all_cached_fixtures[connection]
461
- end
462
-
463
- def self.fixture_is_cached?(connection, table_name)
464
- cache_for_connection(connection)[table_name]
465
- end
466
-
467
- def self.cached_fixtures(connection, keys_to_fetch = nil)
468
- if keys_to_fetch
469
- cache_for_connection(connection).values_at(*keys_to_fetch)
470
- else
471
- cache_for_connection(connection).values
472
- end
473
- end
474
-
475
- def self.cache_fixtures(connection, fixtures_map)
476
- cache_for_connection(connection).update(fixtures_map)
477
- end
478
-
479
- def self.instantiate_fixtures(object, fixture_set, load_instances = true)
480
- if load_instances
481
- fixture_set.each do |fixture_name, fixture|
482
- begin
483
- object.instance_variable_set "@#{fixture_name}", fixture.find
484
- rescue FixtureClassNotFound
485
- nil
486
- end
487
- end
488
- end
489
- end
490
-
491
- def self.instantiate_all_loaded_fixtures(object, load_instances = true)
492
- all_loaded_fixtures.each_value do |fixture_set|
493
- instantiate_fixtures(object, fixture_set, load_instances)
494
- end
495
- end
496
-
497
446
  cattr_accessor :all_loaded_fixtures, default: {}
498
447
 
499
448
  class ClassCache
@@ -502,14 +451,16 @@ module ActiveRecord
502
451
  @config = config
503
452
 
504
453
  # Remove string values that aren't constants or subclasses of AR
505
- @class_names.delete_if { |klass_name, klass| !insert_class(@class_names, klass_name, klass) }
454
+ @class_names.delete_if do |klass_name, klass|
455
+ !insert_class(@class_names, klass_name, klass)
456
+ end
506
457
  end
507
458
 
508
459
  def [](fs_name)
509
- @class_names.fetch(fs_name) {
460
+ @class_names.fetch(fs_name) do
510
461
  klass = default_fixture_model(fs_name, @config).safe_constantize
511
462
  insert_class(@class_names, fs_name, klass)
512
- }
463
+ end
513
464
  end
514
465
 
515
466
  private
@@ -528,76 +479,146 @@ module ActiveRecord
528
479
  end
529
480
  end
530
481
 
531
- def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
532
- fixture_set_names = Array(fixture_set_names).map(&:to_s)
533
- class_names = ClassCache.new class_names, config
482
+ class << self
483
+ def default_fixture_model_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
484
+ config.pluralize_table_names ?
485
+ fixture_set_name.singularize.camelize :
486
+ fixture_set_name.camelize
487
+ end
534
488
 
535
- # FIXME: Apparently JK uses this.
536
- connection = block_given? ? yield : ActiveRecord::Base.connection
489
+ def default_fixture_table_name(fixture_set_name, config = ActiveRecord::Base) # :nodoc:
490
+ "#{ config.table_name_prefix }"\
491
+ "#{ fixture_set_name.tr('/', '_') }"\
492
+ "#{ config.table_name_suffix }".to_sym
493
+ end
537
494
 
538
- files_to_read = fixture_set_names.reject { |fs_name|
539
- fixture_is_cached?(connection, fs_name)
540
- }
495
+ def reset_cache
496
+ @@all_cached_fixtures.clear
497
+ end
541
498
 
542
- unless files_to_read.empty?
543
- fixtures_map = {}
499
+ def cache_for_connection(connection)
500
+ @@all_cached_fixtures[connection]
501
+ end
544
502
 
545
- fixture_sets = files_to_read.map do |fs_name|
546
- klass = class_names[fs_name]
547
- conn = klass ? klass.connection : connection
548
- fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new
549
- conn,
550
- fs_name,
551
- klass,
552
- ::File.join(fixtures_directory, fs_name))
503
+ def fixture_is_cached?(connection, table_name)
504
+ cache_for_connection(connection)[table_name]
505
+ end
506
+
507
+ def cached_fixtures(connection, keys_to_fetch = nil)
508
+ if keys_to_fetch
509
+ cache_for_connection(connection).values_at(*keys_to_fetch)
510
+ else
511
+ cache_for_connection(connection).values
553
512
  end
513
+ end
554
514
 
555
- update_all_loaded_fixtures fixtures_map
556
- fixture_sets_by_connection = fixture_sets.group_by { |fs| fs.model_class ? fs.model_class.connection : connection }
515
+ def cache_fixtures(connection, fixtures_map)
516
+ cache_for_connection(connection).update(fixtures_map)
517
+ end
557
518
 
558
- fixture_sets_by_connection.each do |conn, set|
559
- table_rows_for_connection = Hash.new { |h, k| h[k] = [] }
519
+ def instantiate_fixtures(object, fixture_set, load_instances = true)
520
+ return unless load_instances
521
+ fixture_set.each do |fixture_name, fixture|
522
+ object.instance_variable_set "@#{fixture_name}", fixture.find
523
+ rescue FixtureClassNotFound
524
+ nil
525
+ end
526
+ end
560
527
 
561
- set.each do |fs|
562
- fs.table_rows.each do |table, rows|
563
- table_rows_for_connection[table].unshift(*rows)
564
- end
565
- end
566
- conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
528
+ def instantiate_all_loaded_fixtures(object, load_instances = true)
529
+ all_loaded_fixtures.each_value do |fixture_set|
530
+ instantiate_fixtures(object, fixture_set, load_instances)
531
+ end
532
+ end
567
533
 
568
- # Cap primary key sequences to max(pk).
569
- if conn.respond_to?(:reset_pk_sequence!)
570
- set.each { |fs| conn.reset_pk_sequence!(fs.table_name) }
571
- end
534
+ def create_fixtures(fixtures_directory, fixture_set_names, class_names = {}, config = ActiveRecord::Base)
535
+ fixture_set_names = Array(fixture_set_names).map(&:to_s)
536
+ class_names = ClassCache.new class_names, config
537
+
538
+ # FIXME: Apparently JK uses this.
539
+ connection = block_given? ? yield : ActiveRecord::Base.connection
540
+
541
+ fixture_files_to_read = fixture_set_names.reject do |fs_name|
542
+ fixture_is_cached?(connection, fs_name)
572
543
  end
573
544
 
574
- cache_fixtures(connection, fixtures_map)
545
+ if fixture_files_to_read.any?
546
+ fixtures_map = read_and_insert(
547
+ fixtures_directory,
548
+ fixture_files_to_read,
549
+ class_names,
550
+ connection,
551
+ )
552
+ cache_fixtures(connection, fixtures_map)
553
+ end
554
+ cached_fixtures(connection, fixture_set_names)
575
555
  end
576
- cached_fixtures(connection, fixture_set_names)
577
- end
578
556
 
579
- # Returns a consistent, platform-independent identifier for +label+.
580
- # Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
581
- def self.identify(label, column_type = :integer)
582
- if column_type == :uuid
583
- Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
584
- else
585
- Zlib.crc32(label.to_s) % MAX_ID
557
+ # Returns a consistent, platform-independent identifier for +label+.
558
+ # Integer identifiers are values less than 2^30. UUIDs are RFC 4122 version 5 SHA-1 hashes.
559
+ def identify(label, column_type = :integer)
560
+ if column_type == :uuid
561
+ Digest::UUID.uuid_v5(Digest::UUID::OID_NAMESPACE, label.to_s)
562
+ else
563
+ Zlib.crc32(label.to_s) % MAX_ID
564
+ end
586
565
  end
587
- end
588
566
 
589
- # Superclass for the evaluation contexts used by ERB fixtures.
590
- def self.context_class
591
- @context_class ||= Class.new
592
- end
567
+ # Superclass for the evaluation contexts used by ERB fixtures.
568
+ def context_class
569
+ @context_class ||= Class.new
570
+ end
593
571
 
594
- def self.update_all_loaded_fixtures(fixtures_map) # :nodoc:
595
- all_loaded_fixtures.update(fixtures_map)
572
+ private
573
+
574
+ def read_and_insert(fixtures_directory, fixture_files, class_names, connection) # :nodoc:
575
+ fixtures_map = {}
576
+ fixture_sets = fixture_files.map do |fixture_set_name|
577
+ klass = class_names[fixture_set_name]
578
+ fixtures_map[fixture_set_name] = new( # ActiveRecord::FixtureSet.new
579
+ nil,
580
+ fixture_set_name,
581
+ klass,
582
+ ::File.join(fixtures_directory, fixture_set_name)
583
+ )
584
+ end
585
+ update_all_loaded_fixtures(fixtures_map)
586
+
587
+ insert(fixture_sets, connection)
588
+
589
+ fixtures_map
590
+ end
591
+
592
+ def insert(fixture_sets, connection) # :nodoc:
593
+ fixture_sets_by_connection = fixture_sets.group_by do |fixture_set|
594
+ fixture_set.model_class&.connection || connection
595
+ end
596
+
597
+ fixture_sets_by_connection.each do |conn, set|
598
+ table_rows_for_connection = Hash.new { |h, k| h[k] = [] }
599
+
600
+ set.each do |fixture_set|
601
+ fixture_set.table_rows.each do |table, rows|
602
+ table_rows_for_connection[table].unshift(*rows)
603
+ end
604
+ end
605
+ conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
606
+
607
+ # Cap primary key sequences to max(pk).
608
+ if conn.respond_to?(:reset_pk_sequence!)
609
+ set.each { |fs| conn.reset_pk_sequence!(fs.table_name) }
610
+ end
611
+ end
612
+ end
613
+
614
+ def update_all_loaded_fixtures(fixtures_map) # :nodoc:
615
+ all_loaded_fixtures.update(fixtures_map)
616
+ end
596
617
  end
597
618
 
598
619
  attr_reader :table_name, :name, :fixtures, :model_class, :config
599
620
 
600
- def initialize(connection, name, class_name, path, config = ActiveRecord::Base)
621
+ def initialize(_, name, class_name, path, config = ActiveRecord::Base)
601
622
  @name = name
602
623
  @path = path
603
624
  @config = config
@@ -606,11 +627,7 @@ module ActiveRecord
606
627
 
607
628
  @fixtures = read_fixture_files(path)
608
629
 
609
- @connection = connection
610
-
611
- @table_name = (model_class.respond_to?(:table_name) ?
612
- model_class.table_name :
613
- self.class.default_fixture_table_name(name, config))
630
+ @table_name = model_class&.table_name || self.class.default_fixture_table_name(name, config)
614
631
  end
615
632
 
616
633
  def [](x)
@@ -632,152 +649,18 @@ module ActiveRecord
632
649
  # Returns a hash of rows to be inserted. The key is the table, the value is
633
650
  # a list of rows to insert to that table.
634
651
  def table_rows
635
- now = config.default_timezone == :utc ? Time.now.utc : Time.now
636
-
637
652
  # allow a standard key to be used for doing defaults in YAML
638
653
  fixtures.delete("DEFAULTS")
639
654
 
640
- # track any join tables we need to insert later
641
- rows = Hash.new { |h, table| h[table] = [] }
642
-
643
- rows[table_name] = fixtures.map do |label, fixture|
644
- row = fixture.to_hash
645
-
646
- if model_class
647
- # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
648
- if model_class.record_timestamps
649
- timestamp_column_names.each do |c_name|
650
- row[c_name] = now unless row.key?(c_name)
651
- end
652
- end
653
-
654
- # interpolate the fixture label
655
- row.each do |key, value|
656
- row[key] = value.gsub("$LABEL", label.to_s) if value.is_a?(String)
657
- end
658
-
659
- # generate a primary key if necessary
660
- if has_primary_key_column? && !row.include?(primary_key_name)
661
- row[primary_key_name] = ActiveRecord::FixtureSet.identify(label, primary_key_type)
662
- end
663
-
664
- # Resolve enums
665
- model_class.defined_enums.each do |name, values|
666
- if row.include?(name)
667
- row[name] = values.fetch(row[name], row[name])
668
- end
669
- end
670
-
671
- # If STI is used, find the correct subclass for association reflection
672
- reflection_class =
673
- if row.include?(inheritance_column_name)
674
- row[inheritance_column_name].constantize rescue model_class
675
- else
676
- model_class
677
- end
678
-
679
- reflection_class._reflections.each_value do |association|
680
- case association.macro
681
- when :belongs_to
682
- # Do not replace association name with association foreign key if they are named the same
683
- fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
684
-
685
- if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
686
- if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
687
- # support polymorphic belongs_to as "label (Type)"
688
- row[association.foreign_type] = $1
689
- end
690
-
691
- fk_type = reflection_class.type_for_attribute(fk_name).type
692
- row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
693
- end
694
- when :has_many
695
- if association.options[:through]
696
- add_join_records(rows, row, HasManyThroughProxy.new(association))
697
- end
698
- end
699
- end
700
- end
701
-
702
- row
703
- end
704
- rows
705
- end
706
-
707
- class ReflectionProxy # :nodoc:
708
- def initialize(association)
709
- @association = association
710
- end
711
-
712
- def join_table
713
- @association.join_table
714
- end
715
-
716
- def name
717
- @association.name
718
- end
719
-
720
- def primary_key_type
721
- @association.klass.type_for_attribute(@association.klass.primary_key).type
722
- end
723
- end
724
-
725
- class HasManyThroughProxy < ReflectionProxy # :nodoc:
726
- def rhs_key
727
- @association.foreign_key
728
- end
729
-
730
- def lhs_key
731
- @association.through_reflection.foreign_key
732
- end
733
-
734
- def join_table
735
- @association.through_reflection.table_name
736
- end
655
+ TableRows.new(
656
+ table_name,
657
+ model_class: model_class,
658
+ fixtures: fixtures,
659
+ config: config,
660
+ ).to_hash
737
661
  end
738
662
 
739
663
  private
740
- def primary_key_name
741
- @primary_key_name ||= model_class && model_class.primary_key
742
- end
743
-
744
- def primary_key_type
745
- @primary_key_type ||= model_class && model_class.type_for_attribute(model_class.primary_key).type
746
- end
747
-
748
- def add_join_records(rows, row, association)
749
- # This is the case when the join table has no fixtures file
750
- if (targets = row.delete(association.name.to_s))
751
- table_name = association.join_table
752
- column_type = association.primary_key_type
753
- lhs_key = association.lhs_key
754
- rhs_key = association.rhs_key
755
-
756
- targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
757
- rows[table_name].concat targets.map { |target|
758
- { lhs_key => row[primary_key_name],
759
- rhs_key => ActiveRecord::FixtureSet.identify(target, column_type) }
760
- }
761
- end
762
- end
763
-
764
- def has_primary_key_column?
765
- @has_primary_key_column ||= primary_key_name &&
766
- model_class.columns.any? { |c| c.name == primary_key_name }
767
- end
768
-
769
- def timestamp_column_names
770
- @timestamp_column_names ||=
771
- %w(created_at created_on updated_at updated_on) & column_names
772
- end
773
-
774
- def inheritance_column_name
775
- @inheritance_column_name ||= model_class && model_class.inheritance_column
776
- end
777
-
778
- def column_names
779
- @column_names ||= @connection.columns(@table_name).collect(&:name)
780
- end
781
664
 
782
665
  def model_class=(class_name)
783
666
  if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
@@ -841,224 +724,9 @@ module ActiveRecord
841
724
  alias :to_hash :fixture
842
725
 
843
726
  def find
844
- if model_class
845
- model_class.unscoped do
846
- model_class.find(fixture[model_class.primary_key])
847
- end
848
- else
849
- raise FixtureClassNotFound, "No class attached to find."
850
- end
851
- end
852
- end
853
- end
854
-
855
- module ActiveRecord
856
- module TestFixtures
857
- extend ActiveSupport::Concern
858
-
859
- def before_setup # :nodoc:
860
- setup_fixtures
861
- super
862
- end
863
-
864
- def after_teardown # :nodoc:
865
- super
866
- teardown_fixtures
867
- end
868
-
869
- included do
870
- class_attribute :fixture_path, instance_writer: false
871
- class_attribute :fixture_table_names, default: []
872
- class_attribute :fixture_class_names, default: {}
873
- class_attribute :use_transactional_tests, default: true
874
- class_attribute :use_instantiated_fixtures, default: false # true, false, or :no_instances
875
- class_attribute :pre_loaded_fixtures, default: false
876
- class_attribute :config, default: ActiveRecord::Base
877
- end
878
-
879
- module ClassMethods
880
- # Sets the model class for a fixture when the class name cannot be inferred from the fixture name.
881
- #
882
- # Examples:
883
- #
884
- # set_fixture_class some_fixture: SomeModel,
885
- # 'namespaced/fixture' => Another::Model
886
- #
887
- # The keys must be the fixture names, that coincide with the short paths to the fixture files.
888
- def set_fixture_class(class_names = {})
889
- self.fixture_class_names = fixture_class_names.merge(class_names.stringify_keys)
890
- end
891
-
892
- def fixtures(*fixture_set_names)
893
- if fixture_set_names.first == :all
894
- fixture_set_names = Dir["#{fixture_path}/{**,*}/*.{yml}"].uniq
895
- fixture_set_names.map! { |f| f[(fixture_path.to_s.size + 1)..-5] }
896
- else
897
- fixture_set_names = fixture_set_names.flatten.map(&:to_s)
898
- end
899
-
900
- self.fixture_table_names |= fixture_set_names
901
- setup_fixture_accessors(fixture_set_names)
902
- end
903
-
904
- def setup_fixture_accessors(fixture_set_names = nil)
905
- fixture_set_names = Array(fixture_set_names || fixture_table_names)
906
- methods = Module.new do
907
- fixture_set_names.each do |fs_name|
908
- fs_name = fs_name.to_s
909
- accessor_name = fs_name.tr("/", "_").to_sym
910
-
911
- define_method(accessor_name) do |*fixture_names|
912
- force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
913
- return_single_record = fixture_names.size == 1
914
- fixture_names = @loaded_fixtures[fs_name].fixtures.keys if fixture_names.empty?
915
-
916
- @fixture_cache[fs_name] ||= {}
917
-
918
- instances = fixture_names.map do |f_name|
919
- f_name = f_name.to_s if f_name.is_a?(Symbol)
920
- @fixture_cache[fs_name].delete(f_name) if force_reload
921
-
922
- if @loaded_fixtures[fs_name][f_name]
923
- @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
924
- else
925
- raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
926
- end
927
- end
928
-
929
- return_single_record ? instances.first : instances
930
- end
931
- private accessor_name
932
- end
933
- end
934
- include methods
935
- end
936
-
937
- def uses_transaction(*methods)
938
- @uses_transaction = [] unless defined?(@uses_transaction)
939
- @uses_transaction.concat methods.map(&:to_s)
940
- end
941
-
942
- def uses_transaction?(method)
943
- @uses_transaction = [] unless defined?(@uses_transaction)
944
- @uses_transaction.include?(method.to_s)
945
- end
946
- end
947
-
948
- def run_in_transaction?
949
- use_transactional_tests &&
950
- !self.class.uses_transaction?(method_name)
951
- end
952
-
953
- def setup_fixtures(config = ActiveRecord::Base)
954
- if pre_loaded_fixtures && !use_transactional_tests
955
- raise RuntimeError, "pre_loaded_fixtures requires use_transactional_tests"
956
- end
957
-
958
- @fixture_cache = {}
959
- @fixture_connections = []
960
- @@already_loaded_fixtures ||= {}
961
- @connection_subscriber = nil
962
-
963
- # Load fixtures once and begin transaction.
964
- if run_in_transaction?
965
- if @@already_loaded_fixtures[self.class]
966
- @loaded_fixtures = @@already_loaded_fixtures[self.class]
967
- else
968
- @loaded_fixtures = load_fixtures(config)
969
- @@already_loaded_fixtures[self.class] = @loaded_fixtures
970
- end
971
-
972
- # Begin transactions for connections already established
973
- @fixture_connections = enlist_fixture_connections
974
- @fixture_connections.each do |connection|
975
- connection.begin_transaction joinable: false
976
- connection.pool.lock_thread = true
977
- end
978
-
979
- # When connections are established in the future, begin a transaction too
980
- @connection_subscriber = ActiveSupport::Notifications.subscribe("!connection.active_record") do |_, _, _, _, payload|
981
- spec_name = payload[:spec_name] if payload.key?(:spec_name)
982
-
983
- if spec_name
984
- begin
985
- connection = ActiveRecord::Base.connection_handler.retrieve_connection(spec_name)
986
- rescue ConnectionNotEstablished
987
- connection = nil
988
- end
989
-
990
- if connection && !@fixture_connections.include?(connection)
991
- connection.begin_transaction joinable: false
992
- connection.pool.lock_thread = true
993
- @fixture_connections << connection
994
- end
995
- end
996
- end
997
-
998
- # Load fixtures for every test.
999
- else
1000
- ActiveRecord::FixtureSet.reset_cache
1001
- @@already_loaded_fixtures[self.class] = nil
1002
- @loaded_fixtures = load_fixtures(config)
1003
- end
1004
-
1005
- # Instantiate fixtures for every test if requested.
1006
- instantiate_fixtures if use_instantiated_fixtures
1007
- end
1008
-
1009
- def teardown_fixtures
1010
- # Rollback changes if a transaction is active.
1011
- if run_in_transaction?
1012
- ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber
1013
- @fixture_connections.each do |connection|
1014
- connection.rollback_transaction if connection.transaction_open?
1015
- connection.pool.lock_thread = false
1016
- end
1017
- @fixture_connections.clear
1018
- else
1019
- ActiveRecord::FixtureSet.reset_cache
1020
- end
1021
-
1022
- ActiveRecord::Base.clear_active_connections!
1023
- end
1024
-
1025
- def enlist_fixture_connections
1026
- ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
1027
- end
1028
-
1029
- private
1030
- def load_fixtures(config)
1031
- fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config)
1032
- Hash[fixtures.map { |f| [f.name, f] }]
1033
- end
1034
-
1035
- def instantiate_fixtures
1036
- if pre_loaded_fixtures
1037
- raise RuntimeError, "Load fixtures before instantiating them." if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
1038
- ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
1039
- else
1040
- raise RuntimeError, "Load fixtures before instantiating them." if @loaded_fixtures.nil?
1041
- @loaded_fixtures.each_value do |fixture_set|
1042
- ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
1043
- end
1044
- end
1045
- end
1046
-
1047
- def load_instances?
1048
- use_instantiated_fixtures != :no_instances
1049
- end
1050
- end
1051
- end
1052
-
1053
- class ActiveRecord::FixtureSet::RenderContext # :nodoc:
1054
- def self.create_subclass
1055
- Class.new ActiveRecord::FixtureSet.context_class do
1056
- def get_binding
1057
- binding()
1058
- end
1059
-
1060
- def binary(path)
1061
- %(!!binary "#{Base64.strict_encode64(File.read(path))}")
727
+ raise FixtureClassNotFound, "No class attached to find." unless model_class
728
+ model_class.unscoped do
729
+ model_class.find(fixture[model_class.primary_key])
1062
730
  end
1063
731
  end
1064
732
  end