activerecord 3.2.22.5 → 5.2.8

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 (275) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +657 -621
  3. data/MIT-LICENSE +2 -2
  4. data/README.rdoc +41 -46
  5. data/examples/performance.rb +55 -42
  6. data/examples/simple.rb +6 -5
  7. data/lib/active_record/aggregations.rb +264 -236
  8. data/lib/active_record/association_relation.rb +40 -0
  9. data/lib/active_record/associations/alias_tracker.rb +47 -42
  10. data/lib/active_record/associations/association.rb +127 -75
  11. data/lib/active_record/associations/association_scope.rb +126 -92
  12. data/lib/active_record/associations/belongs_to_association.rb +78 -27
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +9 -4
  14. data/lib/active_record/associations/builder/association.rb +117 -32
  15. data/lib/active_record/associations/builder/belongs_to.rb +135 -60
  16. data/lib/active_record/associations/builder/collection_association.rb +61 -54
  17. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +120 -42
  18. data/lib/active_record/associations/builder/has_many.rb +10 -64
  19. data/lib/active_record/associations/builder/has_one.rb +19 -51
  20. data/lib/active_record/associations/builder/singular_association.rb +28 -18
  21. data/lib/active_record/associations/collection_association.rb +226 -293
  22. data/lib/active_record/associations/collection_proxy.rb +1067 -69
  23. data/lib/active_record/associations/foreign_association.rb +13 -0
  24. data/lib/active_record/associations/has_many_association.rb +83 -47
  25. data/lib/active_record/associations/has_many_through_association.rb +98 -65
  26. data/lib/active_record/associations/has_one_association.rb +57 -20
  27. data/lib/active_record/associations/has_one_through_association.rb +18 -9
  28. data/lib/active_record/associations/join_dependency/join_association.rb +48 -126
  29. data/lib/active_record/associations/join_dependency/join_base.rb +11 -12
  30. data/lib/active_record/associations/join_dependency/join_part.rb +35 -42
  31. data/lib/active_record/associations/join_dependency.rb +212 -164
  32. data/lib/active_record/associations/preloader/association.rb +95 -89
  33. data/lib/active_record/associations/preloader/through_association.rb +84 -44
  34. data/lib/active_record/associations/preloader.rb +123 -111
  35. data/lib/active_record/associations/singular_association.rb +33 -24
  36. data/lib/active_record/associations/through_association.rb +60 -26
  37. data/lib/active_record/associations.rb +1759 -1506
  38. data/lib/active_record/attribute_assignment.rb +60 -193
  39. data/lib/active_record/attribute_decorators.rb +90 -0
  40. data/lib/active_record/attribute_methods/before_type_cast.rb +55 -8
  41. data/lib/active_record/attribute_methods/dirty.rb +113 -74
  42. data/lib/active_record/attribute_methods/primary_key.rb +106 -77
  43. data/lib/active_record/attribute_methods/query.rb +8 -5
  44. data/lib/active_record/attribute_methods/read.rb +63 -114
  45. data/lib/active_record/attribute_methods/serialization.rb +60 -90
  46. data/lib/active_record/attribute_methods/time_zone_conversion.rb +69 -43
  47. data/lib/active_record/attribute_methods/write.rb +43 -45
  48. data/lib/active_record/attribute_methods.rb +366 -149
  49. data/lib/active_record/attributes.rb +266 -0
  50. data/lib/active_record/autosave_association.rb +312 -225
  51. data/lib/active_record/base.rb +114 -505
  52. data/lib/active_record/callbacks.rb +145 -67
  53. data/lib/active_record/coders/json.rb +15 -0
  54. data/lib/active_record/coders/yaml_column.rb +32 -23
  55. data/lib/active_record/collection_cache_key.rb +53 -0
  56. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +883 -284
  57. data/lib/active_record/connection_adapters/abstract/database_limits.rb +16 -2
  58. data/lib/active_record/connection_adapters/abstract/database_statements.rb +350 -200
  59. data/lib/active_record/connection_adapters/abstract/query_cache.rb +82 -27
  60. data/lib/active_record/connection_adapters/abstract/quoting.rb +150 -65
  61. data/lib/active_record/connection_adapters/abstract/savepoints.rb +23 -0
  62. data/lib/active_record/connection_adapters/abstract/schema_creation.rb +146 -0
  63. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +477 -284
  64. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +95 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +1100 -310
  66. data/lib/active_record/connection_adapters/abstract/transaction.rb +283 -0
  67. data/lib/active_record/connection_adapters/abstract_adapter.rb +450 -118
  68. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +657 -446
  69. data/lib/active_record/connection_adapters/column.rb +50 -255
  70. data/lib/active_record/connection_adapters/connection_specification.rb +287 -0
  71. data/lib/active_record/connection_adapters/determine_if_preparable_visitor.rb +33 -0
  72. data/lib/active_record/connection_adapters/mysql/column.rb +27 -0
  73. data/lib/active_record/connection_adapters/mysql/database_statements.rb +140 -0
  74. data/lib/active_record/connection_adapters/mysql/explain_pretty_printer.rb +72 -0
  75. data/lib/active_record/connection_adapters/mysql/quoting.rb +44 -0
  76. data/lib/active_record/connection_adapters/mysql/schema_creation.rb +73 -0
  77. data/lib/active_record/connection_adapters/mysql/schema_definitions.rb +87 -0
  78. data/lib/active_record/connection_adapters/mysql/schema_dumper.rb +80 -0
  79. data/lib/active_record/connection_adapters/mysql/schema_statements.rb +148 -0
  80. data/lib/active_record/connection_adapters/mysql/type_metadata.rb +35 -0
  81. data/lib/active_record/connection_adapters/mysql2_adapter.rb +59 -210
  82. data/lib/active_record/connection_adapters/postgresql/column.rb +44 -0
  83. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +163 -0
  84. data/lib/active_record/connection_adapters/postgresql/explain_pretty_printer.rb +44 -0
  85. data/lib/active_record/connection_adapters/postgresql/oid/array.rb +92 -0
  86. data/lib/active_record/connection_adapters/postgresql/oid/bit.rb +56 -0
  87. data/lib/active_record/connection_adapters/postgresql/oid/bit_varying.rb +15 -0
  88. data/lib/active_record/connection_adapters/postgresql/oid/bytea.rb +17 -0
  89. data/lib/active_record/connection_adapters/postgresql/oid/cidr.rb +50 -0
  90. data/lib/active_record/connection_adapters/postgresql/oid/date.rb +23 -0
  91. data/lib/active_record/connection_adapters/postgresql/oid/date_time.rb +23 -0
  92. data/lib/active_record/connection_adapters/postgresql/oid/decimal.rb +15 -0
  93. data/lib/active_record/connection_adapters/postgresql/oid/enum.rb +21 -0
  94. data/lib/active_record/connection_adapters/postgresql/oid/hstore.rb +71 -0
  95. data/lib/active_record/connection_adapters/postgresql/oid/inet.rb +15 -0
  96. data/lib/active_record/connection_adapters/postgresql/oid/jsonb.rb +15 -0
  97. data/lib/active_record/connection_adapters/postgresql/oid/legacy_point.rb +45 -0
  98. data/lib/active_record/connection_adapters/postgresql/oid/money.rb +41 -0
  99. data/lib/active_record/connection_adapters/postgresql/oid/oid.rb +15 -0
  100. data/lib/active_record/connection_adapters/postgresql/oid/point.rb +65 -0
  101. data/lib/active_record/connection_adapters/postgresql/oid/range.rb +97 -0
  102. data/lib/active_record/connection_adapters/postgresql/oid/specialized_string.rb +18 -0
  103. data/lib/active_record/connection_adapters/postgresql/oid/type_map_initializer.rb +111 -0
  104. data/lib/active_record/connection_adapters/postgresql/oid/uuid.rb +23 -0
  105. data/lib/active_record/connection_adapters/postgresql/oid/vector.rb +28 -0
  106. data/lib/active_record/connection_adapters/postgresql/oid/xml.rb +30 -0
  107. data/lib/active_record/connection_adapters/postgresql/oid.rb +34 -0
  108. data/lib/active_record/connection_adapters/postgresql/quoting.rb +168 -0
  109. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +43 -0
  110. data/lib/active_record/connection_adapters/postgresql/schema_creation.rb +65 -0
  111. data/lib/active_record/connection_adapters/postgresql/schema_definitions.rb +206 -0
  112. data/lib/active_record/connection_adapters/postgresql/schema_dumper.rb +50 -0
  113. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +774 -0
  114. data/lib/active_record/connection_adapters/postgresql/type_metadata.rb +39 -0
  115. data/lib/active_record/connection_adapters/postgresql/utils.rb +81 -0
  116. data/lib/active_record/connection_adapters/postgresql_adapter.rb +620 -1080
  117. data/lib/active_record/connection_adapters/schema_cache.rb +85 -36
  118. data/lib/active_record/connection_adapters/sql_type_metadata.rb +34 -0
  119. data/lib/active_record/connection_adapters/sqlite3/explain_pretty_printer.rb +21 -0
  120. data/lib/active_record/connection_adapters/sqlite3/quoting.rb +67 -0
  121. data/lib/active_record/connection_adapters/sqlite3/schema_creation.rb +17 -0
  122. data/lib/active_record/connection_adapters/sqlite3/schema_definitions.rb +19 -0
  123. data/lib/active_record/connection_adapters/sqlite3/schema_dumper.rb +18 -0
  124. data/lib/active_record/connection_adapters/sqlite3/schema_statements.rb +106 -0
  125. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +545 -27
  126. data/lib/active_record/connection_adapters/statement_pool.rb +34 -13
  127. data/lib/active_record/connection_handling.rb +145 -0
  128. data/lib/active_record/core.rb +559 -0
  129. data/lib/active_record/counter_cache.rb +200 -105
  130. data/lib/active_record/define_callbacks.rb +22 -0
  131. data/lib/active_record/dynamic_matchers.rb +107 -69
  132. data/lib/active_record/enum.rb +244 -0
  133. data/lib/active_record/errors.rb +245 -60
  134. data/lib/active_record/explain.rb +35 -71
  135. data/lib/active_record/explain_registry.rb +32 -0
  136. data/lib/active_record/explain_subscriber.rb +18 -9
  137. data/lib/active_record/fixture_set/file.rb +82 -0
  138. data/lib/active_record/fixtures.rb +418 -275
  139. data/lib/active_record/gem_version.rb +17 -0
  140. data/lib/active_record/inheritance.rb +209 -100
  141. data/lib/active_record/integration.rb +116 -21
  142. data/lib/active_record/internal_metadata.rb +45 -0
  143. data/lib/active_record/legacy_yaml_adapter.rb +48 -0
  144. data/lib/active_record/locale/en.yml +9 -1
  145. data/lib/active_record/locking/optimistic.rb +107 -94
  146. data/lib/active_record/locking/pessimistic.rb +20 -8
  147. data/lib/active_record/log_subscriber.rb +99 -34
  148. data/lib/active_record/migration/command_recorder.rb +199 -64
  149. data/lib/active_record/migration/compatibility.rb +217 -0
  150. data/lib/active_record/migration/join_table.rb +17 -0
  151. data/lib/active_record/migration.rb +893 -296
  152. data/lib/active_record/model_schema.rb +328 -175
  153. data/lib/active_record/nested_attributes.rb +338 -242
  154. data/lib/active_record/no_touching.rb +58 -0
  155. data/lib/active_record/null_relation.rb +68 -0
  156. data/lib/active_record/persistence.rb +557 -170
  157. data/lib/active_record/query_cache.rb +14 -43
  158. data/lib/active_record/querying.rb +36 -24
  159. data/lib/active_record/railtie.rb +147 -52
  160. data/lib/active_record/railties/console_sandbox.rb +5 -4
  161. data/lib/active_record/railties/controller_runtime.rb +13 -6
  162. data/lib/active_record/railties/databases.rake +206 -488
  163. data/lib/active_record/readonly_attributes.rb +4 -6
  164. data/lib/active_record/reflection.rb +734 -228
  165. data/lib/active_record/relation/batches/batch_enumerator.rb +69 -0
  166. data/lib/active_record/relation/batches.rb +249 -52
  167. data/lib/active_record/relation/calculations.rb +330 -284
  168. data/lib/active_record/relation/delegation.rb +135 -37
  169. data/lib/active_record/relation/finder_methods.rb +450 -287
  170. data/lib/active_record/relation/from_clause.rb +26 -0
  171. data/lib/active_record/relation/merger.rb +193 -0
  172. data/lib/active_record/relation/predicate_builder/array_handler.rb +48 -0
  173. data/lib/active_record/relation/predicate_builder/association_query_value.rb +46 -0
  174. data/lib/active_record/relation/predicate_builder/base_handler.rb +19 -0
  175. data/lib/active_record/relation/predicate_builder/basic_object_handler.rb +20 -0
  176. data/lib/active_record/relation/predicate_builder/polymorphic_array_value.rb +56 -0
  177. data/lib/active_record/relation/predicate_builder/range_handler.rb +42 -0
  178. data/lib/active_record/relation/predicate_builder/relation_handler.rb +19 -0
  179. data/lib/active_record/relation/predicate_builder.rb +132 -43
  180. data/lib/active_record/relation/query_attribute.rb +45 -0
  181. data/lib/active_record/relation/query_methods.rb +1037 -221
  182. data/lib/active_record/relation/record_fetch_warning.rb +51 -0
  183. data/lib/active_record/relation/spawn_methods.rb +48 -151
  184. data/lib/active_record/relation/where_clause.rb +186 -0
  185. data/lib/active_record/relation/where_clause_factory.rb +34 -0
  186. data/lib/active_record/relation.rb +451 -359
  187. data/lib/active_record/result.rb +129 -20
  188. data/lib/active_record/runtime_registry.rb +24 -0
  189. data/lib/active_record/sanitization.rb +164 -136
  190. data/lib/active_record/schema.rb +31 -19
  191. data/lib/active_record/schema_dumper.rb +154 -107
  192. data/lib/active_record/schema_migration.rb +56 -0
  193. data/lib/active_record/scoping/default.rb +108 -98
  194. data/lib/active_record/scoping/named.rb +125 -112
  195. data/lib/active_record/scoping.rb +77 -123
  196. data/lib/active_record/secure_token.rb +40 -0
  197. data/lib/active_record/serialization.rb +10 -6
  198. data/lib/active_record/statement_cache.rb +121 -0
  199. data/lib/active_record/store.rb +175 -16
  200. data/lib/active_record/suppressor.rb +61 -0
  201. data/lib/active_record/table_metadata.rb +82 -0
  202. data/lib/active_record/tasks/database_tasks.rb +337 -0
  203. data/lib/active_record/tasks/mysql_database_tasks.rb +115 -0
  204. data/lib/active_record/tasks/postgresql_database_tasks.rb +143 -0
  205. data/lib/active_record/tasks/sqlite_database_tasks.rb +83 -0
  206. data/lib/active_record/timestamp.rb +80 -41
  207. data/lib/active_record/touch_later.rb +64 -0
  208. data/lib/active_record/transactions.rb +240 -119
  209. data/lib/active_record/translation.rb +2 -0
  210. data/lib/active_record/type/adapter_specific_registry.rb +136 -0
  211. data/lib/active_record/type/date.rb +9 -0
  212. data/lib/active_record/type/date_time.rb +9 -0
  213. data/lib/active_record/type/decimal_without_scale.rb +15 -0
  214. data/lib/active_record/type/hash_lookup_type_map.rb +25 -0
  215. data/lib/active_record/type/internal/timezone.rb +17 -0
  216. data/lib/active_record/type/json.rb +30 -0
  217. data/lib/active_record/type/serialized.rb +71 -0
  218. data/lib/active_record/type/text.rb +11 -0
  219. data/lib/active_record/type/time.rb +21 -0
  220. data/lib/active_record/type/type_map.rb +62 -0
  221. data/lib/active_record/type/unsigned_integer.rb +17 -0
  222. data/lib/active_record/type.rb +79 -0
  223. data/lib/active_record/type_caster/connection.rb +33 -0
  224. data/lib/active_record/type_caster/map.rb +23 -0
  225. data/lib/active_record/type_caster.rb +9 -0
  226. data/lib/active_record/validations/absence.rb +25 -0
  227. data/lib/active_record/validations/associated.rb +35 -18
  228. data/lib/active_record/validations/length.rb +26 -0
  229. data/lib/active_record/validations/presence.rb +68 -0
  230. data/lib/active_record/validations/uniqueness.rb +133 -75
  231. data/lib/active_record/validations.rb +53 -43
  232. data/lib/active_record/version.rb +7 -7
  233. data/lib/active_record.rb +89 -57
  234. data/lib/rails/generators/active_record/application_record/application_record_generator.rb +27 -0
  235. data/lib/rails/generators/active_record/application_record/templates/application_record.rb.tt +5 -0
  236. data/lib/rails/generators/active_record/migration/migration_generator.rb +61 -8
  237. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb.tt +24 -0
  238. data/lib/rails/generators/active_record/migration/templates/migration.rb.tt +46 -0
  239. data/lib/rails/generators/active_record/migration.rb +28 -8
  240. data/lib/rails/generators/active_record/model/model_generator.rb +23 -22
  241. data/lib/rails/generators/active_record/model/templates/model.rb.tt +13 -0
  242. data/lib/rails/generators/active_record/model/templates/{module.rb → module.rb.tt} +1 -1
  243. data/lib/rails/generators/active_record.rb +10 -16
  244. metadata +141 -62
  245. data/examples/associations.png +0 -0
  246. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +0 -63
  247. data/lib/active_record/associations/join_helper.rb +0 -55
  248. data/lib/active_record/associations/preloader/belongs_to.rb +0 -17
  249. data/lib/active_record/associations/preloader/collection_association.rb +0 -24
  250. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +0 -60
  251. data/lib/active_record/associations/preloader/has_many.rb +0 -17
  252. data/lib/active_record/associations/preloader/has_many_through.rb +0 -15
  253. data/lib/active_record/associations/preloader/has_one.rb +0 -23
  254. data/lib/active_record/associations/preloader/has_one_through.rb +0 -9
  255. data/lib/active_record/associations/preloader/singular_association.rb +0 -21
  256. data/lib/active_record/attribute_methods/deprecated_underscore_read.rb +0 -32
  257. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +0 -191
  258. data/lib/active_record/connection_adapters/mysql_adapter.rb +0 -441
  259. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -583
  260. data/lib/active_record/dynamic_finder_match.rb +0 -68
  261. data/lib/active_record/dynamic_scope_match.rb +0 -23
  262. data/lib/active_record/fixtures/file.rb +0 -65
  263. data/lib/active_record/identity_map.rb +0 -162
  264. data/lib/active_record/observer.rb +0 -121
  265. data/lib/active_record/railties/jdbcmysql_error.rb +0 -16
  266. data/lib/active_record/serializers/xml_serializer.rb +0 -203
  267. data/lib/active_record/session_store.rb +0 -360
  268. data/lib/active_record/test_case.rb +0 -73
  269. data/lib/rails/generators/active_record/migration/templates/migration.rb +0 -34
  270. data/lib/rails/generators/active_record/model/templates/migration.rb +0 -15
  271. data/lib/rails/generators/active_record/model/templates/model.rb +0 -12
  272. data/lib/rails/generators/active_record/observer/observer_generator.rb +0 -15
  273. data/lib/rails/generators/active_record/observer/templates/observer.rb +0 -4
  274. data/lib/rails/generators/active_record/session_migration/session_migration_generator.rb +0 -25
  275. data/lib/rails/generators/active_record/session_migration/templates/migration.rb +0 -12
@@ -1,36 +1,27 @@
1
- require 'erb'
1
+ # frozen_string_literal: true
2
2
 
3
- begin
4
- require 'psych'
5
- rescue LoadError
6
- end
7
-
8
- require 'yaml'
9
- require 'zlib'
10
- require 'active_support/dependencies'
11
- require 'active_support/core_ext/array/wrap'
12
- require 'active_support/core_ext/object/blank'
13
- require 'active_support/core_ext/logger'
14
- require 'active_support/ordered_hash'
15
- require 'active_record/fixtures/file'
3
+ require "erb"
4
+ require "yaml"
5
+ require "zlib"
6
+ require "set"
7
+ require "active_support/dependencies"
8
+ require "active_support/core_ext/digest/uuid"
9
+ require "active_record/fixture_set/file"
10
+ require "active_record/errors"
16
11
 
17
- if defined? ActiveRecord
12
+ module ActiveRecord
18
13
  class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
19
14
  end
20
- else
21
- class FixtureClassNotFound < StandardError #:nodoc:
22
- end
23
- end
24
15
 
25
- module ActiveRecord
26
16
  # \Fixtures are a way of organizing data that you want to test against; in short, sample data.
27
17
  #
28
18
  # They are stored in YAML files, one file per model, which are placed in the directory
29
19
  # appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
30
20
  # configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
31
- # The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
32
- # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a fixture file looks
33
- # like this:
21
+ # The fixture file ends with the +.yml+ file extension, for example:
22
+ # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>).
23
+ #
24
+ # The format of a fixture file looks like this:
34
25
  #
35
26
  # rubyonrails:
36
27
  # id: 1
@@ -46,7 +37,7 @@ module ActiveRecord
46
37
  # is followed by an indented list of key/value pairs in the "key: value" format. Records are
47
38
  # separated by a blank line for your viewing pleasure.
48
39
  #
49
- # Note that fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
40
+ # Note: Fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
50
41
  # See http://yaml.org/type/omap.html
51
42
  # for the specification. You will need ordered fixtures when you have foreign key constraints
52
43
  # on keys in the same table. This is commonly needed for tree structures. Example:
@@ -74,20 +65,39 @@ module ActiveRecord
74
65
  # end
75
66
  # end
76
67
  #
77
- # By default, <tt>test_helper.rb</tt> will load all of your fixtures into your test database,
78
- # so this test will succeed.
68
+ # By default, +test_helper.rb+ will load all of your fixtures into your test
69
+ # database, so this test will succeed.
79
70
  #
80
- # The testing environment will automatically load the all fixtures into the database before each
71
+ # The testing environment will automatically load all the fixtures into the database before each
81
72
  # test. To ensure consistent data, the environment deletes the fixtures before running the load.
82
73
  #
83
74
  # In addition to being available in the database, the fixture's data may also be accessed by
84
- # using a special dynamic method, which has the same name as the model, and accepts the
85
- # name of the fixture to instantiate:
75
+ # using a special dynamic method, which has the same name as the model.
86
76
  #
87
- # test "find" do
77
+ # Passing in a fixture name to this dynamic method returns the fixture matching this name:
78
+ #
79
+ # test "find one" do
88
80
  # assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
89
81
  # end
90
82
  #
83
+ # Passing in multiple fixture names returns all fixtures matching these names:
84
+ #
85
+ # test "find all by name" do
86
+ # assert_equal 2, web_sites(:rubyonrails, :google).length
87
+ # end
88
+ #
89
+ # Passing in no arguments returns all fixtures:
90
+ #
91
+ # test "find all" do
92
+ # assert_equal 2, web_sites.length
93
+ # end
94
+ #
95
+ # Passing in any fixture name that does not exist will raise <tt>StandardError</tt>:
96
+ #
97
+ # test "find by name that does not exist" do
98
+ # assert_raise(StandardError) { web_sites(:reddit) }
99
+ # end
100
+ #
91
101
  # Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the
92
102
  # following tests:
93
103
  #
@@ -96,11 +106,11 @@ module ActiveRecord
96
106
  # end
97
107
  #
98
108
  # test "find_alt_method_2" do
99
- # assert_equal "Ruby on Rails", @rubyonrails.news
109
+ # assert_equal "Ruby on Rails", @rubyonrails.name
100
110
  # end
101
111
  #
102
- # In order to use these methods to access fixtured data within your testcases, you must specify one of the
103
- # following in your <tt>ActiveSupport::TestCase</tt>-derived class:
112
+ # In order to use these methods to access fixtured data within your test cases, you must specify one of the
113
+ # following in your ActiveSupport::TestCase-derived class:
104
114
  #
105
115
  # - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
106
116
  # self.use_instantiated_fixtures = true
@@ -114,14 +124,14 @@ module ActiveRecord
114
124
  #
115
125
  # = Dynamic fixtures with ERB
116
126
  #
117
- # Some times you don't care about the content of the fixtures as much as you care about the volume.
127
+ # Sometimes you don't care about the content of the fixtures as much as you care about the volume.
118
128
  # In these cases, you can mix ERB in with your YAML fixtures to create a bunch of fixtures for load
119
129
  # testing, like:
120
130
  #
121
131
  # <% 1.upto(1000) do |i| %>
122
132
  # fix_<%= i %>:
123
133
  # id: <%= i %>
124
- # name: guy_<%= 1 %>
134
+ # name: guy_<%= i %>
125
135
  # <% end %>
126
136
  #
127
137
  # This will create 1000 very simple fixtures.
@@ -133,34 +143,51 @@ module ActiveRecord
133
143
  # perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
134
144
  # in fixtures are to be considered a code smell.
135
145
  #
136
- # = Transactional Fixtures
146
+ # Helper methods defined in a fixture will not be available in other fixtures, to prevent against
147
+ # unwanted inter-test dependencies. Methods used by multiple fixtures should be defined in a module
148
+ # that is included in ActiveRecord::FixtureSet.context_class.
149
+ #
150
+ # - define a helper method in <tt>test_helper.rb</tt>
151
+ # module FixtureFileHelpers
152
+ # def file_sha(path)
153
+ # Digest::SHA2.hexdigest(File.read(Rails.root.join('test/fixtures', path)))
154
+ # end
155
+ # end
156
+ # ActiveRecord::FixtureSet.context_class.include FixtureFileHelpers
157
+ #
158
+ # - use the helper method in a fixture
159
+ # photo:
160
+ # name: kitten.png
161
+ # sha: <%= file_sha 'files/kitten.png' %>
162
+ #
163
+ # = Transactional Tests
137
164
  #
138
165
  # Test cases can use begin+rollback to isolate their changes to the database instead of having to
139
166
  # delete+insert for every test case.
140
167
  #
141
168
  # class FooTest < ActiveSupport::TestCase
142
- # self.use_transactional_fixtures = true
169
+ # self.use_transactional_tests = true
143
170
  #
144
171
  # test "godzilla" do
145
- # assert !Foo.all.empty?
172
+ # assert_not_empty Foo.all
146
173
  # Foo.destroy_all
147
- # assert Foo.all.empty?
174
+ # assert_empty Foo.all
148
175
  # end
149
176
  #
150
177
  # test "godzilla aftermath" do
151
- # assert !Foo.all.empty?
178
+ # assert_not_empty Foo.all
152
179
  # end
153
180
  # end
154
181
  #
155
182
  # If you preload your test database with all fixture data (probably in the rake task) and use
156
- # transactional fixtures, then you may omit all fixtures declarations in your test cases since
183
+ # transactional tests, then you may omit all fixtures declarations in your test cases since
157
184
  # all the data's already there and every case rolls back its changes.
158
185
  #
159
186
  # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to
160
187
  # true. This will provide access to fixture data for every table that has been loaded through
161
188
  # fixtures (depending on the value of +use_instantiated_fixtures+).
162
189
  #
163
- # When *not* to use transactional fixtures:
190
+ # When *not* to use transactional tests:
164
191
  #
165
192
  # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until
166
193
  # all parent transactions commit, particularly, the fixtures transaction which is begun in setup
@@ -176,6 +203,9 @@ module ActiveRecord
176
203
  # * Stable, autogenerated IDs
177
204
  # * Label references for associations (belongs_to, has_one, has_many)
178
205
  # * HABTM associations as inline lists
206
+ #
207
+ # There are some more advanced features available even if the id is specified:
208
+ #
179
209
  # * Autofilled timestamp columns
180
210
  # * Fixture label interpolation
181
211
  # * Support for YAML defaults
@@ -262,7 +292,7 @@ module ActiveRecord
262
292
  #
263
293
  # ### in fruit.rb
264
294
  #
265
- # belongs_to :eater, :polymorphic => true
295
+ # belongs_to :eater, polymorphic: true
266
296
  #
267
297
  # ### in fruits.yml
268
298
  #
@@ -358,41 +388,68 @@ module ActiveRecord
358
388
  # geeksomnia:
359
389
  # name: Geeksomnia's Account
360
390
  # subdomain: $LABEL
391
+ # email: $LABEL@email.com
361
392
  #
362
393
  # Also, sometimes (like when porting older join table fixtures) you'll need
363
394
  # to be able to get a hold of the identifier for a given label. ERB
364
395
  # to the rescue:
365
396
  #
366
397
  # george_reginald:
367
- # monkey_id: <%= ActiveRecord::Fixtures.identify(:reginald) %>
368
- # pirate_id: <%= ActiveRecord::Fixtures.identify(:george) %>
398
+ # monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
399
+ # pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
369
400
  #
370
401
  # == Support for YAML defaults
371
402
  #
372
- # You probably already know how to use YAML to set and reuse defaults in
373
- # your <tt>database.yml</tt> file. You can use the same technique in your fixtures:
403
+ # You can set and reuse defaults in your fixtures YAML file.
404
+ # This is the same technique used in the +database.yml+ file to specify
405
+ # defaults:
374
406
  #
375
407
  # DEFAULTS: &DEFAULTS
376
408
  # created_on: <%= 3.weeks.ago.to_s(:db) %>
377
409
  #
378
410
  # first:
379
411
  # name: Smurf
380
- # *DEFAULTS
412
+ # <<: *DEFAULTS
381
413
  #
382
414
  # second:
383
415
  # name: Fraggle
384
- # *DEFAULTS
416
+ # <<: *DEFAULTS
385
417
  #
386
418
  # Any fixture labeled "DEFAULTS" is safely ignored.
387
- class Fixtures
388
- MAX_ID = 2 ** 30 - 1
419
+ #
420
+ # == Configure the fixture model class
421
+ #
422
+ # It's possible to set the fixture's model class directly in the YAML file.
423
+ # This is helpful when fixtures are loaded outside tests and
424
+ # +set_fixture_class+ is not available (e.g.
425
+ # when running <tt>rails db:fixtures:load</tt>).
426
+ #
427
+ # _fixture:
428
+ # model_class: User
429
+ # david:
430
+ # name: David
431
+ #
432
+ # Any fixtures labeled "_fixture" are safely ignored.
433
+ class FixtureSet
434
+ #--
435
+ # An instance of FixtureSet is normally stored in a single YAML file and
436
+ # possibly in a folder with the same name.
437
+ #++
438
+
439
+ MAX_ID = 2**30 - 1
440
+
441
+ @@all_cached_fixtures = Hash.new { |h, k| h[k] = {} }
389
442
 
390
- @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
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
391
448
 
392
- def self.find_table_name(table_name) # :nodoc:
393
- ActiveRecord::Base.pluralize_table_names ?
394
- table_name.to_s.singularize.camelize :
395
- table_name.to_s.camelize
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
396
453
  end
397
454
 
398
455
  def self.reset_cache
@@ -419,11 +476,7 @@ module ActiveRecord
419
476
  cache_for_connection(connection).update(fixtures_map)
420
477
  end
421
478
 
422
- #--
423
- # TODO:NOTE: in the next version, the __with_new_arity suffix and
424
- # the method with the old arity will be removed.
425
- #++
426
- def self.instantiate_fixtures__with_new_arity(object, fixture_set, load_instances = true) # :nodoc:
479
+ def self.instantiate_fixtures(object, fixture_set, load_instances = true)
427
480
  if load_instances
428
481
  fixture_set.each do |fixture_name, fixture|
429
482
  begin
@@ -435,124 +488,136 @@ module ActiveRecord
435
488
  end
436
489
  end
437
490
 
438
- # The use with parameters <tt>(object, fixture_set_name, fixture_set, load_instances = true)</tt> is deprecated, +fixture_set_name+ parameter is not used.
439
- # Use as:
440
- #
441
- # instantiate_fixtures(object, fixture_set, load_instances = true)
442
- def self.instantiate_fixtures(object, fixture_set, load_instances = true, rails_3_2_compatibility_argument = true)
443
- unless load_instances == true || load_instances == false
444
- ActiveSupport::Deprecation.warn(
445
- "ActiveRecord::Fixtures.instantiate_fixtures with parameters (object, fixture_set_name, fixture_set, load_instances = true) is deprecated and shall be removed from future releases. Use it with parameters (object, fixture_set, load_instances = true) instead (skip fixture_set_name).",
446
- caller)
447
- fixture_set = load_instances
448
- load_instances = rails_3_2_compatibility_argument
449
- end
450
- instantiate_fixtures__with_new_arity(object, fixture_set, load_instances)
451
- end
452
-
453
491
  def self.instantiate_all_loaded_fixtures(object, load_instances = true)
454
492
  all_loaded_fixtures.each_value do |fixture_set|
455
- ActiveRecord::Fixtures.instantiate_fixtures(object, fixture_set, load_instances)
493
+ instantiate_fixtures(object, fixture_set, load_instances)
456
494
  end
457
495
  end
458
496
 
459
- cattr_accessor :all_loaded_fixtures
460
- self.all_loaded_fixtures = {}
497
+ cattr_accessor :all_loaded_fixtures, default: {}
461
498
 
462
- def self.create_fixtures(fixtures_directory, table_names, class_names = {})
463
- table_names = [table_names].flatten.map { |n| n.to_s }
464
- table_names.each { |n|
465
- class_names[n.tr('/', '_').to_sym] = n.classify if n.include?('/')
466
- }
499
+ class ClassCache
500
+ def initialize(class_names, config)
501
+ @class_names = class_names.stringify_keys
502
+ @config = config
467
503
 
468
- # FIXME: Apparently JK uses this.
469
- connection = block_given? ? yield : ActiveRecord::Base.connection
470
-
471
- files_to_read = table_names.reject { |table_name|
472
- fixture_is_cached?(connection, table_name)
473
- }
504
+ # 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) }
506
+ end
474
507
 
475
- unless files_to_read.empty?
476
- connection.disable_referential_integrity do
477
- fixtures_map = {}
508
+ def [](fs_name)
509
+ @class_names.fetch(fs_name) {
510
+ klass = default_fixture_model(fs_name, @config).safe_constantize
511
+ insert_class(@class_names, fs_name, klass)
512
+ }
513
+ end
478
514
 
479
- fixture_files = files_to_read.map do |path|
480
- table_name = path.tr '/', '_'
515
+ private
481
516
 
482
- fixtures_map[path] = ActiveRecord::Fixtures.new(
483
- connection,
484
- table_name,
485
- class_names[table_name.to_sym] || table_name.classify,
486
- ::File.join(fixtures_directory, path))
517
+ def insert_class(class_names, name, klass)
518
+ # We only want to deal with AR objects.
519
+ if klass && klass < ActiveRecord::Base
520
+ class_names[name] = klass
521
+ else
522
+ class_names[name] = nil
487
523
  end
524
+ end
488
525
 
489
- all_loaded_fixtures.update(fixtures_map)
526
+ def default_fixture_model(fs_name, config)
527
+ ActiveRecord::FixtureSet.default_fixture_model_name(fs_name, config)
528
+ end
529
+ end
490
530
 
491
- connection.transaction(:requires_new => true) do
492
- fixture_files.each do |ff|
493
- conn = ff.model_class.respond_to?(:connection) ? ff.model_class.connection : connection
494
- table_rows = ff.table_rows
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
495
534
 
496
- table_rows.keys.each do |table|
497
- conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
498
- end
535
+ # FIXME: Apparently JK uses this.
536
+ connection = block_given? ? yield : ActiveRecord::Base.connection
499
537
 
500
- table_rows.each do |table_name,rows|
501
- rows.each do |row|
502
- conn.insert_fixture(row, table_name)
503
- end
504
- end
505
- end
538
+ files_to_read = fixture_set_names.reject { |fs_name|
539
+ fixture_is_cached?(connection, fs_name)
540
+ }
506
541
 
507
- # Cap primary key sequences to max(pk).
508
- if connection.respond_to?(:reset_pk_sequence!)
509
- table_names.each do |table_name|
510
- connection.reset_pk_sequence!(table_name.tr('/', '_'))
511
- end
542
+ unless files_to_read.empty?
543
+ fixtures_map = {}
544
+
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))
553
+ end
554
+
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 }
557
+
558
+ fixture_sets_by_connection.each do |conn, set|
559
+ table_rows_for_connection = Hash.new { |h, k| h[k] = [] }
560
+
561
+ set.each do |fs|
562
+ fs.table_rows.each do |table, rows|
563
+ table_rows_for_connection[table].unshift(*rows)
512
564
  end
513
565
  end
566
+ conn.insert_fixtures_set(table_rows_for_connection, table_rows_for_connection.keys)
514
567
 
515
- cache_fixtures(connection, fixtures_map)
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
516
572
  end
573
+
574
+ cache_fixtures(connection, fixtures_map)
517
575
  end
518
- cached_fixtures(connection, table_names)
576
+ cached_fixtures(connection, fixture_set_names)
519
577
  end
520
578
 
521
579
  # Returns a consistent, platform-independent identifier for +label+.
522
- # Identifiers are positive integers less than 2^32.
523
- def self.identify(label)
524
- Zlib.crc32(label.to_s) % MAX_ID
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
586
+ end
587
+ end
588
+
589
+ # Superclass for the evaluation contexts used by ERB fixtures.
590
+ def self.context_class
591
+ @context_class ||= Class.new
525
592
  end
526
593
 
527
- attr_reader :table_name, :name, :fixtures, :model_class
594
+ def self.update_all_loaded_fixtures(fixtures_map) # :nodoc:
595
+ all_loaded_fixtures.update(fixtures_map)
596
+ end
528
597
 
529
- def initialize(connection, table_name, class_name, fixture_path)
530
- @connection = connection
531
- @table_name = table_name
532
- @fixture_path = fixture_path
533
- @name = table_name # preserve fixture base name
534
- @class_name = class_name
598
+ attr_reader :table_name, :name, :fixtures, :model_class, :config
535
599
 
536
- @fixtures = ActiveSupport::OrderedHash.new
537
- @table_name = "#{ActiveRecord::Base.table_name_prefix}#{@table_name}#{ActiveRecord::Base.table_name_suffix}"
600
+ def initialize(connection, name, class_name, path, config = ActiveRecord::Base)
601
+ @name = name
602
+ @path = path
603
+ @config = config
538
604
 
539
- # Should be an AR::Base type class
540
- if class_name.is_a?(Class)
541
- @table_name = class_name.table_name
542
- @connection = class_name.connection
543
- @model_class = class_name
544
- else
545
- @model_class = class_name.constantize rescue nil
546
- end
605
+ self.model_class = class_name
547
606
 
548
- read_fixture_files
607
+ @fixtures = read_fixture_files(path)
608
+
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))
549
614
  end
550
615
 
551
616
  def [](x)
552
617
  fixtures[x]
553
618
  end
554
619
 
555
- def []=(k,v)
620
+ def []=(k, v)
556
621
  fixtures[k] = v
557
622
  end
558
623
 
@@ -564,37 +629,43 @@ module ActiveRecord
564
629
  fixtures.size
565
630
  end
566
631
 
567
- # Return a hash of rows to be inserted. The key is the table, the value is
632
+ # Returns a hash of rows to be inserted. The key is the table, the value is
568
633
  # a list of rows to insert to that table.
569
634
  def table_rows
570
- now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
571
- now = now.to_s(:db)
635
+ now = config.default_timezone == :utc ? Time.now.utc : Time.now
572
636
 
573
637
  # allow a standard key to be used for doing defaults in YAML
574
- fixtures.delete('DEFAULTS')
638
+ fixtures.delete("DEFAULTS")
575
639
 
576
640
  # track any join tables we need to insert later
577
- rows = Hash.new { |h,table| h[table] = [] }
641
+ rows = Hash.new { |h, table| h[table] = [] }
578
642
 
579
643
  rows[table_name] = fixtures.map do |label, fixture|
580
644
  row = fixture.to_hash
581
645
 
582
- if model_class && model_class < ActiveRecord::Base
646
+ if model_class
583
647
  # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
584
648
  if model_class.record_timestamps
585
- timestamp_column_names.each do |name|
586
- row[name] = now unless row.key?(name)
649
+ timestamp_column_names.each do |c_name|
650
+ row[c_name] = now unless row.key?(c_name)
587
651
  end
588
652
  end
589
653
 
590
654
  # interpolate the fixture label
591
655
  row.each do |key, value|
592
- row[key] = label if value.is_a?(String) && value == "$LABEL"
656
+ row[key] = value.gsub("$LABEL", label.to_s) if value.is_a?(String)
593
657
  end
594
658
 
595
659
  # generate a primary key if necessary
596
660
  if has_primary_key_column? && !row.include?(primary_key_name)
597
- row[primary_key_name] = ActiveRecord::Fixtures.identify(label)
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
598
669
  end
599
670
 
600
671
  # If STI is used, find the correct subclass for association reflection
@@ -605,28 +676,24 @@ module ActiveRecord
605
676
  model_class
606
677
  end
607
678
 
608
- reflection_class.reflect_on_all_associations.each do |association|
679
+ reflection_class._reflections.each_value do |association|
609
680
  case association.macro
610
681
  when :belongs_to
611
682
  # Do not replace association name with association foreign key if they are named the same
612
683
  fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
613
684
 
614
685
  if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
615
- if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
686
+ if association.polymorphic? && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
616
687
  # support polymorphic belongs_to as "label (Type)"
617
688
  row[association.foreign_type] = $1
618
689
  end
619
690
 
620
- row[fk_name] = ActiveRecord::Fixtures.identify(value)
691
+ fk_type = reflection_class.type_for_attribute(fk_name).type
692
+ row[fk_name] = ActiveRecord::FixtureSet.identify(value, fk_type)
621
693
  end
622
- when :has_and_belongs_to_many
623
- if (targets = row.delete(association.name.to_s))
624
- targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
625
- table_name = association.options[:join_table]
626
- rows[table_name].concat targets.map { |target|
627
- { association.foreign_key => row[primary_key_name],
628
- association.association_foreign_key => ActiveRecord::Fixtures.identify(target) }
629
- }
694
+ when :has_many
695
+ if association.options[:through]
696
+ add_join_records(rows, row, HasManyThroughProxy.new(association))
630
697
  end
631
698
  end
632
699
  end
@@ -637,11 +704,63 @@ module ActiveRecord
637
704
  rows
638
705
  end
639
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
737
+ end
738
+
640
739
  private
641
740
  def primary_key_name
642
741
  @primary_key_name ||= model_class && model_class.primary_key
643
742
  end
644
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
+
645
764
  def has_primary_key_column?
646
765
  @has_primary_key_column ||= primary_key_name &&
647
766
  model_class.columns.any? { |c| c.name == primary_key_name }
@@ -657,27 +776,38 @@ module ActiveRecord
657
776
  end
658
777
 
659
778
  def column_names
660
- @column_names ||= @connection.columns(@table_name).collect { |c| c.name }
779
+ @column_names ||= @connection.columns(@table_name).collect(&:name)
661
780
  end
662
781
 
663
- def read_fixture_files
664
- yaml_files = Dir["#{@fixture_path}/{**,*}/*.yml"].select { |f|
782
+ def model_class=(class_name)
783
+ if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
784
+ @model_class = class_name
785
+ else
786
+ @model_class = class_name.safe_constantize if class_name
787
+ end
788
+ end
789
+
790
+ # Loads the fixtures from the YAML file at +path+.
791
+ # If the file sets the +model_class+ and current instance value is not set,
792
+ # it uses the file value.
793
+ def read_fixture_files(path)
794
+ yaml_files = Dir["#{path}/{**,*}/*.yml"].select { |f|
665
795
  ::File.file?(f)
666
- } + [yaml_file_path]
796
+ } + [yaml_file_path(path)]
667
797
 
668
- yaml_files.each do |file|
669
- Fixtures::File.open(file) do |fh|
670
- fh.each do |name, row|
671
- fixtures[name] = ActiveRecord::Fixture.new(row, model_class)
798
+ yaml_files.each_with_object({}) do |file, fixtures|
799
+ FixtureSet::File.open(file) do |fh|
800
+ self.model_class ||= fh.model_class if fh.model_class
801
+ fh.each do |fixture_name, row|
802
+ fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
672
803
  end
673
804
  end
674
805
  end
675
806
  end
676
807
 
677
- def yaml_file_path
678
- "#{@fixture_path}.yml"
808
+ def yaml_file_path(path)
809
+ "#{path}.yml"
679
810
  end
680
-
681
811
  end
682
812
 
683
813
  class Fixture #:nodoc:
@@ -712,7 +842,9 @@ module ActiveRecord
712
842
 
713
843
  def find
714
844
  if model_class
715
- model_class.find(fixture[model_class.primary_key])
845
+ model_class.unscoped do
846
+ model_class.find(fixture[model_class.primary_key])
847
+ end
716
848
  else
717
849
  raise FixtureClassNotFound, "No class attached to find."
718
850
  end
@@ -724,91 +856,79 @@ module ActiveRecord
724
856
  module TestFixtures
725
857
  extend ActiveSupport::Concern
726
858
 
727
- included do
728
- setup :setup_fixtures
729
- teardown :teardown_fixtures
730
-
731
- class_attribute :fixture_path
732
- class_attribute :fixture_table_names
733
- class_attribute :fixture_class_names
734
- class_attribute :use_transactional_fixtures
735
- class_attribute :use_instantiated_fixtures # true, false, or :no_instances
736
- class_attribute :pre_loaded_fixtures
859
+ def before_setup # :nodoc:
860
+ setup_fixtures
861
+ super
862
+ end
737
863
 
738
- self.fixture_table_names = []
739
- self.use_transactional_fixtures = true
740
- self.use_instantiated_fixtures = false
741
- self.pre_loaded_fixtures = false
864
+ def after_teardown # :nodoc:
865
+ super
866
+ teardown_fixtures
867
+ end
742
868
 
743
- self.fixture_class_names = Hash.new do |h, table_name|
744
- h[table_name] = ActiveRecord::Fixtures.find_table_name(table_name)
745
- end
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
746
877
  end
747
878
 
748
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.
749
888
  def set_fixture_class(class_names = {})
750
- self.fixture_class_names = self.fixture_class_names.merge(class_names)
889
+ self.fixture_class_names = fixture_class_names.merge(class_names.stringify_keys)
751
890
  end
752
891
 
753
- def fixtures(*fixture_names)
754
- if fixture_names.first == :all
755
- fixture_names = Dir["#{fixture_path}/{**,*}/*.{yml}"]
756
- fixture_names.map! { |f| f[(fixture_path.size + 1)..-5] }
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] }
757
896
  else
758
- fixture_names = fixture_names.flatten.map { |n| n.to_s }
897
+ fixture_set_names = fixture_set_names.flatten.map(&:to_s)
759
898
  end
760
899
 
761
- self.fixture_table_names |= fixture_names
762
- require_fixture_classes(fixture_names)
763
- setup_fixture_accessors(fixture_names)
764
- end
765
-
766
- def try_to_load_dependency(file_name)
767
- require_dependency file_name
768
- rescue LoadError => e
769
- # Let's hope the developer has included it himself
770
-
771
- # Let's warn in case this is a subdependency, otherwise
772
- # subdependency error messages are totally cryptic
773
- if ActiveRecord::Base.logger
774
- ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
775
- end
776
- end
777
-
778
- def require_fixture_classes(fixture_names = nil)
779
- (fixture_names || fixture_table_names).each do |fixture_name|
780
- file_name = fixture_name.to_s
781
- file_name = file_name.singularize if ActiveRecord::Base.pluralize_table_names
782
- try_to_load_dependency(file_name)
783
- end
900
+ self.fixture_table_names |= fixture_set_names
901
+ setup_fixture_accessors(fixture_set_names)
784
902
  end
785
903
 
786
- def setup_fixture_accessors(fixture_names = nil)
787
- fixture_names = Array.wrap(fixture_names || fixture_table_names)
904
+ def setup_fixture_accessors(fixture_set_names = nil)
905
+ fixture_set_names = Array(fixture_set_names || fixture_table_names)
788
906
  methods = Module.new do
789
- fixture_names.each do |fixture_name|
790
- fixture_name = fixture_name.to_s.tr('./', '_')
907
+ fixture_set_names.each do |fs_name|
908
+ fs_name = fs_name.to_s
909
+ accessor_name = fs_name.tr("/", "_").to_sym
791
910
 
792
- define_method(fixture_name) do |*fixtures|
793
- force_reload = fixtures.pop if fixtures.last == true || fixtures.last == :reload
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?
794
915
 
795
- @fixture_cache[fixture_name] ||= {}
916
+ @fixture_cache[fs_name] ||= {}
796
917
 
797
- instances = fixtures.map do |fixture|
798
- @fixture_cache[fixture_name].delete(fixture) if force_reload
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
799
921
 
800
- if @loaded_fixtures[fixture_name][fixture.to_s]
801
- ActiveRecord::IdentityMap.without do
802
- @fixture_cache[fixture_name][fixture] ||= @loaded_fixtures[fixture_name][fixture.to_s].find
803
- end
922
+ if @loaded_fixtures[fs_name][f_name]
923
+ @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
804
924
  else
805
- raise StandardError, "No fixture with name '#{fixture}' found for table '#{fixture_name}'"
925
+ raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
806
926
  end
807
927
  end
808
928
 
809
- instances.size == 1 ? instances.first : instances
929
+ return_single_record ? instances.first : instances
810
930
  end
811
- private fixture_name
931
+ private accessor_name
812
932
  end
813
933
  end
814
934
  include methods
@@ -816,7 +936,7 @@ module ActiveRecord
816
936
 
817
937
  def uses_transaction(*methods)
818
938
  @uses_transaction = [] unless defined?(@uses_transaction)
819
- @uses_transaction.concat methods.map { |m| m.to_s }
939
+ @uses_transaction.concat methods.map(&:to_s)
820
940
  end
821
941
 
822
942
  def uses_transaction?(method)
@@ -826,40 +946,60 @@ module ActiveRecord
826
946
  end
827
947
 
828
948
  def run_in_transaction?
829
- use_transactional_fixtures &&
949
+ use_transactional_tests &&
830
950
  !self.class.uses_transaction?(method_name)
831
951
  end
832
952
 
833
- def setup_fixtures
834
- return unless !ActiveRecord::Base.configurations.blank?
835
-
836
- if pre_loaded_fixtures && !use_transactional_fixtures
837
- raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
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"
838
956
  end
839
957
 
840
958
  @fixture_cache = {}
841
959
  @fixture_connections = []
842
960
  @@already_loaded_fixtures ||= {}
961
+ @connection_subscriber = nil
843
962
 
844
963
  # Load fixtures once and begin transaction.
845
964
  if run_in_transaction?
846
965
  if @@already_loaded_fixtures[self.class]
847
966
  @loaded_fixtures = @@already_loaded_fixtures[self.class]
848
967
  else
849
- @loaded_fixtures = load_fixtures
968
+ @loaded_fixtures = load_fixtures(config)
850
969
  @@already_loaded_fixtures[self.class] = @loaded_fixtures
851
970
  end
971
+
972
+ # Begin transactions for connections already established
852
973
  @fixture_connections = enlist_fixture_connections
853
974
  @fixture_connections.each do |connection|
854
- connection.increment_open_transactions
855
- connection.transaction_joinable = false
856
- connection.begin_db_transaction
975
+ connection.begin_transaction joinable: false
976
+ connection.pool.lock_thread = true
857
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
+
858
998
  # Load fixtures for every test.
859
999
  else
860
- ActiveRecord::Fixtures.reset_cache
1000
+ ActiveRecord::FixtureSet.reset_cache
861
1001
  @@already_loaded_fixtures[self.class] = nil
862
- @loaded_fixtures = load_fixtures
1002
+ @loaded_fixtures = load_fixtures(config)
863
1003
  end
864
1004
 
865
1005
  # Instantiate fixtures for every test if requested.
@@ -867,50 +1007,39 @@ module ActiveRecord
867
1007
  end
868
1008
 
869
1009
  def teardown_fixtures
870
- return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
871
-
872
- unless run_in_transaction?
873
- ActiveRecord::Fixtures.reset_cache
874
- end
875
-
876
1010
  # Rollback changes if a transaction is active.
877
1011
  if run_in_transaction?
1012
+ ActiveSupport::Notifications.unsubscribe(@connection_subscriber) if @connection_subscriber
878
1013
  @fixture_connections.each do |connection|
879
- if connection.open_transactions != 0
880
- connection.rollback_db_transaction
881
- connection.decrement_open_transactions
882
- end
1014
+ connection.rollback_transaction if connection.transaction_open?
1015
+ connection.pool.lock_thread = false
883
1016
  end
884
1017
  @fixture_connections.clear
1018
+ else
1019
+ ActiveRecord::FixtureSet.reset_cache
885
1020
  end
1021
+
886
1022
  ActiveRecord::Base.clear_active_connections!
887
1023
  end
888
1024
 
889
1025
  def enlist_fixture_connections
890
- ActiveRecord::Base.connection_handler.connection_pools.values.map(&:connection)
1026
+ ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
891
1027
  end
892
1028
 
893
1029
  private
894
- def load_fixtures
895
- fixtures = ActiveRecord::Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
1030
+ def load_fixtures(config)
1031
+ fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names, config)
896
1032
  Hash[fixtures.map { |f| [f.name, f] }]
897
1033
  end
898
1034
 
899
- # for pre_loaded_fixtures, only require the classes once. huge speed improvement
900
- @@required_fixture_classes = false
901
-
902
1035
  def instantiate_fixtures
903
1036
  if pre_loaded_fixtures
904
- raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::Fixtures.all_loaded_fixtures.empty?
905
- unless @@required_fixture_classes
906
- self.class.require_fixture_classes ActiveRecord::Fixtures.all_loaded_fixtures.keys
907
- @@required_fixture_classes = true
908
- end
909
- ActiveRecord::Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
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?)
910
1039
  else
911
- raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
1040
+ raise RuntimeError, "Load fixtures before instantiating them." if @loaded_fixtures.nil?
912
1041
  @loaded_fixtures.each_value do |fixture_set|
913
- ActiveRecord::Fixtures.instantiate_fixtures(self, fixture_set, load_instances?)
1042
+ ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
914
1043
  end
915
1044
  end
916
1045
  end
@@ -920,3 +1049,17 @@ module ActiveRecord
920
1049
  end
921
1050
  end
922
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))}")
1062
+ end
1063
+ end
1064
+ end
1065
+ end