activerecord 1.0.0 → 4.0.0

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

Potentially problematic release.


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

Files changed (255) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +2102 -0
  3. data/MIT-LICENSE +20 -0
  4. data/README.rdoc +213 -0
  5. data/examples/performance.rb +172 -0
  6. data/examples/simple.rb +14 -0
  7. data/lib/active_record/aggregations.rb +180 -84
  8. data/lib/active_record/associations/alias_tracker.rb +76 -0
  9. data/lib/active_record/associations/association.rb +248 -0
  10. data/lib/active_record/associations/association_scope.rb +135 -0
  11. data/lib/active_record/associations/belongs_to_association.rb +92 -0
  12. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +35 -0
  13. data/lib/active_record/associations/builder/association.rb +108 -0
  14. data/lib/active_record/associations/builder/belongs_to.rb +98 -0
  15. data/lib/active_record/associations/builder/collection_association.rb +89 -0
  16. data/lib/active_record/associations/builder/has_and_belongs_to_many.rb +39 -0
  17. data/lib/active_record/associations/builder/has_many.rb +15 -0
  18. data/lib/active_record/associations/builder/has_one.rb +25 -0
  19. data/lib/active_record/associations/builder/singular_association.rb +32 -0
  20. data/lib/active_record/associations/collection_association.rb +608 -0
  21. data/lib/active_record/associations/collection_proxy.rb +986 -0
  22. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +58 -39
  23. data/lib/active_record/associations/has_many_association.rb +116 -85
  24. data/lib/active_record/associations/has_many_through_association.rb +197 -0
  25. data/lib/active_record/associations/has_one_association.rb +102 -0
  26. data/lib/active_record/associations/has_one_through_association.rb +36 -0
  27. data/lib/active_record/associations/join_dependency/join_association.rb +174 -0
  28. data/lib/active_record/associations/join_dependency/join_base.rb +24 -0
  29. data/lib/active_record/associations/join_dependency/join_part.rb +78 -0
  30. data/lib/active_record/associations/join_dependency.rb +235 -0
  31. data/lib/active_record/associations/join_helper.rb +45 -0
  32. data/lib/active_record/associations/preloader/association.rb +121 -0
  33. data/lib/active_record/associations/preloader/belongs_to.rb +17 -0
  34. data/lib/active_record/associations/preloader/collection_association.rb +24 -0
  35. data/lib/active_record/associations/preloader/has_and_belongs_to_many.rb +60 -0
  36. data/lib/active_record/associations/preloader/has_many.rb +17 -0
  37. data/lib/active_record/associations/preloader/has_many_through.rb +19 -0
  38. data/lib/active_record/associations/preloader/has_one.rb +23 -0
  39. data/lib/active_record/associations/preloader/has_one_through.rb +9 -0
  40. data/lib/active_record/associations/preloader/singular_association.rb +21 -0
  41. data/lib/active_record/associations/preloader/through_association.rb +63 -0
  42. data/lib/active_record/associations/preloader.rb +178 -0
  43. data/lib/active_record/associations/singular_association.rb +64 -0
  44. data/lib/active_record/associations/through_association.rb +87 -0
  45. data/lib/active_record/associations.rb +1437 -431
  46. data/lib/active_record/attribute_assignment.rb +201 -0
  47. data/lib/active_record/attribute_methods/before_type_cast.rb +70 -0
  48. data/lib/active_record/attribute_methods/dirty.rb +118 -0
  49. data/lib/active_record/attribute_methods/primary_key.rb +122 -0
  50. data/lib/active_record/attribute_methods/query.rb +40 -0
  51. data/lib/active_record/attribute_methods/read.rb +107 -0
  52. data/lib/active_record/attribute_methods/serialization.rb +162 -0
  53. data/lib/active_record/attribute_methods/time_zone_conversion.rb +59 -0
  54. data/lib/active_record/attribute_methods/write.rb +63 -0
  55. data/lib/active_record/attribute_methods.rb +393 -0
  56. data/lib/active_record/autosave_association.rb +426 -0
  57. data/lib/active_record/base.rb +268 -930
  58. data/lib/active_record/callbacks.rb +203 -230
  59. data/lib/active_record/coders/yaml_column.rb +38 -0
  60. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +638 -0
  61. data/lib/active_record/connection_adapters/abstract/database_limits.rb +67 -0
  62. data/lib/active_record/connection_adapters/abstract/database_statements.rb +390 -0
  63. data/lib/active_record/connection_adapters/abstract/query_cache.rb +95 -0
  64. data/lib/active_record/connection_adapters/abstract/quoting.rb +129 -0
  65. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +501 -0
  66. data/lib/active_record/connection_adapters/abstract/schema_dumper.rb +70 -0
  67. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +873 -0
  68. data/lib/active_record/connection_adapters/abstract/transaction.rb +203 -0
  69. data/lib/active_record/connection_adapters/abstract_adapter.rb +389 -275
  70. data/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +782 -0
  71. data/lib/active_record/connection_adapters/column.rb +318 -0
  72. data/lib/active_record/connection_adapters/connection_specification.rb +96 -0
  73. data/lib/active_record/connection_adapters/mysql2_adapter.rb +273 -0
  74. data/lib/active_record/connection_adapters/mysql_adapter.rb +517 -90
  75. data/lib/active_record/connection_adapters/postgresql/array_parser.rb +97 -0
  76. data/lib/active_record/connection_adapters/postgresql/cast.rb +152 -0
  77. data/lib/active_record/connection_adapters/postgresql/database_statements.rb +242 -0
  78. data/lib/active_record/connection_adapters/postgresql/oid.rb +366 -0
  79. data/lib/active_record/connection_adapters/postgresql/quoting.rb +171 -0
  80. data/lib/active_record/connection_adapters/postgresql/referential_integrity.rb +30 -0
  81. data/lib/active_record/connection_adapters/postgresql/schema_statements.rb +489 -0
  82. data/lib/active_record/connection_adapters/postgresql_adapter.rb +911 -138
  83. data/lib/active_record/connection_adapters/schema_cache.rb +129 -0
  84. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +624 -0
  85. data/lib/active_record/connection_adapters/statement_pool.rb +40 -0
  86. data/lib/active_record/connection_handling.rb +98 -0
  87. data/lib/active_record/core.rb +463 -0
  88. data/lib/active_record/counter_cache.rb +122 -0
  89. data/lib/active_record/dynamic_matchers.rb +131 -0
  90. data/lib/active_record/errors.rb +213 -0
  91. data/lib/active_record/explain.rb +38 -0
  92. data/lib/active_record/explain_registry.rb +30 -0
  93. data/lib/active_record/explain_subscriber.rb +29 -0
  94. data/lib/active_record/fixture_set/file.rb +55 -0
  95. data/lib/active_record/fixtures.rb +892 -138
  96. data/lib/active_record/inheritance.rb +200 -0
  97. data/lib/active_record/integration.rb +60 -0
  98. data/lib/active_record/locale/en.yml +47 -0
  99. data/lib/active_record/locking/optimistic.rb +181 -0
  100. data/lib/active_record/locking/pessimistic.rb +77 -0
  101. data/lib/active_record/log_subscriber.rb +82 -0
  102. data/lib/active_record/migration/command_recorder.rb +164 -0
  103. data/lib/active_record/migration/join_table.rb +15 -0
  104. data/lib/active_record/migration.rb +1015 -0
  105. data/lib/active_record/model_schema.rb +345 -0
  106. data/lib/active_record/nested_attributes.rb +546 -0
  107. data/lib/active_record/null_relation.rb +65 -0
  108. data/lib/active_record/persistence.rb +509 -0
  109. data/lib/active_record/query_cache.rb +56 -0
  110. data/lib/active_record/querying.rb +62 -0
  111. data/lib/active_record/railtie.rb +205 -0
  112. data/lib/active_record/railties/console_sandbox.rb +5 -0
  113. data/lib/active_record/railties/controller_runtime.rb +50 -0
  114. data/lib/active_record/railties/databases.rake +402 -0
  115. data/lib/active_record/railties/jdbcmysql_error.rb +16 -0
  116. data/lib/active_record/readonly_attributes.rb +30 -0
  117. data/lib/active_record/reflection.rb +544 -87
  118. data/lib/active_record/relation/batches.rb +93 -0
  119. data/lib/active_record/relation/calculations.rb +399 -0
  120. data/lib/active_record/relation/delegation.rb +125 -0
  121. data/lib/active_record/relation/finder_methods.rb +349 -0
  122. data/lib/active_record/relation/merger.rb +161 -0
  123. data/lib/active_record/relation/predicate_builder.rb +106 -0
  124. data/lib/active_record/relation/query_methods.rb +1044 -0
  125. data/lib/active_record/relation/spawn_methods.rb +73 -0
  126. data/lib/active_record/relation.rb +655 -0
  127. data/lib/active_record/result.rb +67 -0
  128. data/lib/active_record/runtime_registry.rb +17 -0
  129. data/lib/active_record/sanitization.rb +168 -0
  130. data/lib/active_record/schema.rb +65 -0
  131. data/lib/active_record/schema_dumper.rb +204 -0
  132. data/lib/active_record/schema_migration.rb +39 -0
  133. data/lib/active_record/scoping/default.rb +146 -0
  134. data/lib/active_record/scoping/named.rb +175 -0
  135. data/lib/active_record/scoping.rb +82 -0
  136. data/lib/active_record/serialization.rb +22 -0
  137. data/lib/active_record/serializers/xml_serializer.rb +197 -0
  138. data/lib/active_record/statement_cache.rb +26 -0
  139. data/lib/active_record/store.rb +156 -0
  140. data/lib/active_record/tasks/database_tasks.rb +203 -0
  141. data/lib/active_record/tasks/firebird_database_tasks.rb +56 -0
  142. data/lib/active_record/tasks/mysql_database_tasks.rb +143 -0
  143. data/lib/active_record/tasks/oracle_database_tasks.rb +45 -0
  144. data/lib/active_record/tasks/postgresql_database_tasks.rb +90 -0
  145. data/lib/active_record/tasks/sqlite_database_tasks.rb +51 -0
  146. data/lib/active_record/tasks/sqlserver_database_tasks.rb +48 -0
  147. data/lib/active_record/test_case.rb +96 -0
  148. data/lib/active_record/timestamp.rb +119 -0
  149. data/lib/active_record/transactions.rb +366 -69
  150. data/lib/active_record/translation.rb +22 -0
  151. data/lib/active_record/validations/associated.rb +49 -0
  152. data/lib/active_record/validations/presence.rb +65 -0
  153. data/lib/active_record/validations/uniqueness.rb +225 -0
  154. data/lib/active_record/validations.rb +64 -185
  155. data/lib/active_record/version.rb +11 -0
  156. data/lib/active_record.rb +149 -24
  157. data/lib/rails/generators/active_record/migration/migration_generator.rb +62 -0
  158. data/lib/rails/generators/active_record/migration/templates/create_table_migration.rb +19 -0
  159. data/lib/rails/generators/active_record/migration/templates/migration.rb +39 -0
  160. data/lib/rails/generators/active_record/model/model_generator.rb +48 -0
  161. data/lib/rails/generators/active_record/model/templates/model.rb +10 -0
  162. data/lib/rails/generators/active_record/model/templates/module.rb +7 -0
  163. data/lib/rails/generators/active_record.rb +23 -0
  164. metadata +261 -161
  165. data/CHANGELOG +0 -581
  166. data/README +0 -361
  167. data/RUNNING_UNIT_TESTS +0 -36
  168. data/dev-utils/eval_debugger.rb +0 -9
  169. data/examples/associations.png +0 -0
  170. data/examples/associations.rb +0 -87
  171. data/examples/shared_setup.rb +0 -15
  172. data/examples/validation.rb +0 -88
  173. data/install.rb +0 -60
  174. data/lib/active_record/associations/association_collection.rb +0 -70
  175. data/lib/active_record/connection_adapters/sqlite_adapter.rb +0 -107
  176. data/lib/active_record/deprecated_associations.rb +0 -70
  177. data/lib/active_record/observer.rb +0 -71
  178. data/lib/active_record/support/class_attribute_accessors.rb +0 -43
  179. data/lib/active_record/support/class_inheritable_attributes.rb +0 -37
  180. data/lib/active_record/support/clean_logger.rb +0 -10
  181. data/lib/active_record/support/inflector.rb +0 -70
  182. data/lib/active_record/vendor/mysql.rb +0 -1117
  183. data/lib/active_record/vendor/simple.rb +0 -702
  184. data/lib/active_record/wrappers/yaml_wrapper.rb +0 -15
  185. data/lib/active_record/wrappings.rb +0 -59
  186. data/rakefile +0 -122
  187. data/test/abstract_unit.rb +0 -16
  188. data/test/aggregations_test.rb +0 -34
  189. data/test/all.sh +0 -8
  190. data/test/associations_test.rb +0 -477
  191. data/test/base_test.rb +0 -513
  192. data/test/class_inheritable_attributes_test.rb +0 -33
  193. data/test/connections/native_mysql/connection.rb +0 -24
  194. data/test/connections/native_postgresql/connection.rb +0 -24
  195. data/test/connections/native_sqlite/connection.rb +0 -24
  196. data/test/deprecated_associations_test.rb +0 -336
  197. data/test/finder_test.rb +0 -67
  198. data/test/fixtures/accounts/signals37 +0 -3
  199. data/test/fixtures/accounts/unknown +0 -2
  200. data/test/fixtures/auto_id.rb +0 -4
  201. data/test/fixtures/column_name.rb +0 -3
  202. data/test/fixtures/companies/first_client +0 -6
  203. data/test/fixtures/companies/first_firm +0 -4
  204. data/test/fixtures/companies/second_client +0 -6
  205. data/test/fixtures/company.rb +0 -37
  206. data/test/fixtures/company_in_module.rb +0 -33
  207. data/test/fixtures/course.rb +0 -3
  208. data/test/fixtures/courses/java +0 -2
  209. data/test/fixtures/courses/ruby +0 -2
  210. data/test/fixtures/customer.rb +0 -30
  211. data/test/fixtures/customers/david +0 -6
  212. data/test/fixtures/db_definitions/mysql.sql +0 -96
  213. data/test/fixtures/db_definitions/mysql2.sql +0 -4
  214. data/test/fixtures/db_definitions/postgresql.sql +0 -113
  215. data/test/fixtures/db_definitions/postgresql2.sql +0 -4
  216. data/test/fixtures/db_definitions/sqlite.sql +0 -85
  217. data/test/fixtures/db_definitions/sqlite2.sql +0 -4
  218. data/test/fixtures/default.rb +0 -2
  219. data/test/fixtures/developer.rb +0 -8
  220. data/test/fixtures/developers/david +0 -2
  221. data/test/fixtures/developers/jamis +0 -2
  222. data/test/fixtures/developers_projects/david_action_controller +0 -2
  223. data/test/fixtures/developers_projects/david_active_record +0 -2
  224. data/test/fixtures/developers_projects/jamis_active_record +0 -2
  225. data/test/fixtures/entrant.rb +0 -3
  226. data/test/fixtures/entrants/first +0 -3
  227. data/test/fixtures/entrants/second +0 -3
  228. data/test/fixtures/entrants/third +0 -3
  229. data/test/fixtures/fixture_database.sqlite +0 -0
  230. data/test/fixtures/fixture_database_2.sqlite +0 -0
  231. data/test/fixtures/movie.rb +0 -5
  232. data/test/fixtures/movies/first +0 -2
  233. data/test/fixtures/movies/second +0 -2
  234. data/test/fixtures/project.rb +0 -3
  235. data/test/fixtures/projects/action_controller +0 -2
  236. data/test/fixtures/projects/active_record +0 -2
  237. data/test/fixtures/reply.rb +0 -21
  238. data/test/fixtures/subscriber.rb +0 -5
  239. data/test/fixtures/subscribers/first +0 -2
  240. data/test/fixtures/subscribers/second +0 -2
  241. data/test/fixtures/topic.rb +0 -20
  242. data/test/fixtures/topics/first +0 -9
  243. data/test/fixtures/topics/second +0 -8
  244. data/test/fixtures_test.rb +0 -20
  245. data/test/inflector_test.rb +0 -104
  246. data/test/inheritance_test.rb +0 -125
  247. data/test/lifecycle_test.rb +0 -110
  248. data/test/modules_test.rb +0 -21
  249. data/test/multiple_db_test.rb +0 -46
  250. data/test/pk_test.rb +0 -57
  251. data/test/reflection_test.rb +0 -78
  252. data/test/thread_safety_test.rb +0 -33
  253. data/test/transactions_test.rb +0 -83
  254. data/test/unconnected_test.rb +0 -24
  255. data/test/validations_test.rb +0 -126
@@ -1,172 +1,926 @@
1
+ require 'erb'
1
2
  require 'yaml'
3
+ require 'zlib'
4
+ require 'active_support/dependencies'
5
+ require 'active_record/fixture_set/file'
6
+ require 'active_record/errors'
2
7
 
3
- # Fixtures are a way of organizing data that you want to test against. Each fixture file is created as a row
4
- # in the database and created as a hash with column names as keys and data as values. All of these fixture hashes
5
- # are kept in an overall hash where they can be accessed by their file name.
6
- #
7
- # Example:
8
- #
9
- # Directory with the fixture files
10
- #
11
- # developers/
12
- # david
13
- # luke
14
- # jamis
15
- #
16
- # The file +david+ then contains:
17
- #
18
- # id => 1
19
- # name => David Heinemeier Hansson
20
- # birthday => 1979-10-15
21
- # profession => Systems development
22
- #
23
- # Now when we call <tt>@developers = Fixtures.new(ActiveRecord::Base.connection, "developers", "developers/")</tt> all three
24
- # developers will get inserted into the "developers" table through the active Active Record connection (that must be setup
25
- # before-hand). And we can now query the fixture data through the <tt>@developers</tt> hash, so <tt>@developers["david"]["name"]</tt>
26
- # will return <tt>"David Heinemeier Hansson"</tt> and <tt>@developers["david"]["birthday"]</tt> will return <tt>Date.new(1979, 10, 15)</tt>.
27
- #
28
- # This can then be used for comparison in a unit test. Something like:
29
- #
30
- # def test_find
31
- # assert_equal @developers["david"]["name"], Developer.find(@developers["david"]["id"]).name
32
- # end
33
- #
34
- # == YAML fixtures
35
- #
36
- # Additionally, fixtures supports yaml files. Like fixture files, these yaml files have a pre-defined format. The document
37
- # must be formatted like this:
38
- #
39
- # name: david
40
- # data:
41
- # id: 1
42
- # name: David Heinemeier Hansson
43
- # birthday: 1979-10-15
44
- # profession: Systems development
45
- # ---
46
- # name: steve
47
- # data:
48
- # id: 2
49
- # name: Steve Ross Kellock
50
- # birthday: 1974-09-27
51
- # profession: guy with keyboard
52
- #
53
- # In that file, there's two records. Each record must have two parts: 'name' and 'data'. The data that you add
54
- # must be indented like you see above.
55
- #
56
- # Yaml fixtures file names must end with .yaml as in people.yaml or camel.yaml. The yaml fixtures are placed in the same
57
- # directory as the normal fixtures and can happy co-exist. :)
58
- class Fixtures
59
- def self.create_fixtures(fixtures_directory, *table_names)
60
- connection = block_given? ? yield : ActiveRecord::Base.connection
61
- ActiveRecord::Base.logger.level = Logger::ERROR
62
-
63
- fixtures = [ table_names ].flatten.collect do |table_name|
64
- Fixtures.new(connection, table_name, "#{fixtures_directory}/#{table_name}")
65
- end
66
-
67
- ActiveRecord::Base.logger.level = Logger::DEBUG
68
-
69
- return fixtures.size > 1 ? fixtures : fixtures.first
8
+ module ActiveRecord
9
+ class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
70
10
  end
71
11
 
72
- def initialize(connection, table_name, fixture_path, file_filter = /^\.|CVS|\.yaml/)
73
- @connection, @table_name, @fixture_path, @file_filter = connection, table_name, fixture_path, file_filter
74
- @fixtures = read_fixtures
12
+ # \Fixtures are a way of organizing data that you want to test against; in short, sample data.
13
+ #
14
+ # They are stored in YAML files, one file per model, which are placed in the directory
15
+ # appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically
16
+ # configured for Rails, so you can just put your files in <tt><your-rails-app>/test/fixtures/</tt>).
17
+ # The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
18
+ # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a fixture file looks
19
+ # like this:
20
+ #
21
+ # rubyonrails:
22
+ # id: 1
23
+ # name: Ruby on Rails
24
+ # url: http://www.rubyonrails.org
25
+ #
26
+ # google:
27
+ # id: 2
28
+ # name: Google
29
+ # url: http://www.google.com
30
+ #
31
+ # This fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and
32
+ # is followed by an indented list of key/value pairs in the "key: value" format. Records are
33
+ # separated by a blank line for your viewing pleasure.
34
+ #
35
+ # Note that fixtures are unordered. If you want ordered fixtures, use the omap YAML type.
36
+ # See http://yaml.org/type/omap.html
37
+ # for the specification. You will need ordered fixtures when you have foreign key constraints
38
+ # on keys in the same table. This is commonly needed for tree structures. Example:
39
+ #
40
+ # --- !omap
41
+ # - parent:
42
+ # id: 1
43
+ # parent_id: NULL
44
+ # title: Parent
45
+ # - child:
46
+ # id: 2
47
+ # parent_id: 1
48
+ # title: Child
49
+ #
50
+ # = Using Fixtures in Test Cases
51
+ #
52
+ # Since fixtures are a testing construct, we use them in our unit and functional tests. There
53
+ # are two ways to use the fixtures, but first let's take a look at a sample unit test:
54
+ #
55
+ # require 'test_helper'
56
+ #
57
+ # class WebSiteTest < ActiveSupport::TestCase
58
+ # test "web_site_count" do
59
+ # assert_equal 2, WebSite.count
60
+ # end
61
+ # end
62
+ #
63
+ # By default, <tt>test_helper.rb</tt> will load all of your fixtures into your test database,
64
+ # so this test will succeed.
65
+ #
66
+ # The testing environment will automatically load the all fixtures into the database before each
67
+ # test. To ensure consistent data, the environment deletes the fixtures before running the load.
68
+ #
69
+ # In addition to being available in the database, the fixture's data may also be accessed by
70
+ # using a special dynamic method, which has the same name as the model, and accepts the
71
+ # name of the fixture to instantiate:
72
+ #
73
+ # test "find" do
74
+ # assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
75
+ # end
76
+ #
77
+ # Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the
78
+ # following tests:
79
+ #
80
+ # test "find_alt_method_1" do
81
+ # assert_equal "Ruby on Rails", @web_sites['rubyonrails']['name']
82
+ # end
83
+ #
84
+ # test "find_alt_method_2" do
85
+ # assert_equal "Ruby on Rails", @rubyonrails.name
86
+ # end
87
+ #
88
+ # In order to use these methods to access fixtured data within your testcases, you must specify one of the
89
+ # following in your <tt>ActiveSupport::TestCase</tt>-derived class:
90
+ #
91
+ # - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
92
+ # self.use_instantiated_fixtures = true
93
+ #
94
+ # - create only the hash for the fixtures, do not 'find' each instance (enable alternate method #1 only)
95
+ # self.use_instantiated_fixtures = :no_instances
96
+ #
97
+ # Using either of these alternate methods incurs a performance hit, as the fixtured data must be fully
98
+ # traversed in the database to create the fixture hash and/or instance variables. This is expensive for
99
+ # large sets of fixtured data.
100
+ #
101
+ # = Dynamic fixtures with ERB
102
+ #
103
+ # Some times you don't care about the content of the fixtures as much as you care about the volume.
104
+ # In these cases, you can mix ERB in with your YAML fixtures to create a bunch of fixtures for load
105
+ # testing, like:
106
+ #
107
+ # <% 1.upto(1000) do |i| %>
108
+ # fix_<%= i %>:
109
+ # id: <%= i %>
110
+ # name: guy_<%= 1 %>
111
+ # <% end %>
112
+ #
113
+ # This will create 1000 very simple fixtures.
114
+ #
115
+ # Using ERB, you can also inject dynamic values into your fixtures with inserts like
116
+ # <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
117
+ # This is however a feature to be used with some caution. The point of fixtures are that they're
118
+ # stable units of predictable sample data. If you feel that you need to inject dynamic values, then
119
+ # perhaps you should reexamine whether your application is properly testable. Hence, dynamic values
120
+ # in fixtures are to be considered a code smell.
121
+ #
122
+ # = Transactional Fixtures
123
+ #
124
+ # Test cases can use begin+rollback to isolate their changes to the database instead of having to
125
+ # delete+insert for every test case.
126
+ #
127
+ # class FooTest < ActiveSupport::TestCase
128
+ # self.use_transactional_fixtures = true
129
+ #
130
+ # test "godzilla" do
131
+ # assert !Foo.all.empty?
132
+ # Foo.destroy_all
133
+ # assert Foo.all.empty?
134
+ # end
135
+ #
136
+ # test "godzilla aftermath" do
137
+ # assert !Foo.all.empty?
138
+ # end
139
+ # end
140
+ #
141
+ # If you preload your test database with all fixture data (probably in the rake task) and use
142
+ # transactional fixtures, then you may omit all fixtures declarations in your test cases since
143
+ # all the data's already there and every case rolls back its changes.
144
+ #
145
+ # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to
146
+ # true. This will provide access to fixture data for every table that has been loaded through
147
+ # fixtures (depending on the value of +use_instantiated_fixtures+).
148
+ #
149
+ # When *not* to use transactional fixtures:
150
+ #
151
+ # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until
152
+ # all parent transactions commit, particularly, the fixtures transaction which is begun in setup
153
+ # and rolled back in teardown. Thus, you won't be able to verify
154
+ # the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
155
+ # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
156
+ # Use InnoDB, MaxDB, or NDB instead.
157
+ #
158
+ # = Advanced Fixtures
159
+ #
160
+ # Fixtures that don't specify an ID get some extra features:
161
+ #
162
+ # * Stable, autogenerated IDs
163
+ # * Label references for associations (belongs_to, has_one, has_many)
164
+ # * HABTM associations as inline lists
165
+ # * Autofilled timestamp columns
166
+ # * Fixture label interpolation
167
+ # * Support for YAML defaults
168
+ #
169
+ # == Stable, Autogenerated IDs
170
+ #
171
+ # Here, have a monkey fixture:
172
+ #
173
+ # george:
174
+ # id: 1
175
+ # name: George the Monkey
176
+ #
177
+ # reginald:
178
+ # id: 2
179
+ # name: Reginald the Pirate
180
+ #
181
+ # Each of these fixtures has two unique identifiers: one for the database
182
+ # and one for the humans. Why don't we generate the primary key instead?
183
+ # Hashing each fixture's label yields a consistent ID:
184
+ #
185
+ # george: # generated id: 503576764
186
+ # name: George the Monkey
187
+ #
188
+ # reginald: # generated id: 324201669
189
+ # name: Reginald the Pirate
190
+ #
191
+ # Active Record looks at the fixture's model class, discovers the correct
192
+ # primary key, and generates it right before inserting the fixture
193
+ # into the database.
194
+ #
195
+ # The generated ID for a given label is constant, so we can discover
196
+ # any fixture's ID without loading anything, as long as we know the label.
197
+ #
198
+ # == Label references for associations (belongs_to, has_one, has_many)
199
+ #
200
+ # Specifying foreign keys in fixtures can be very fragile, not to
201
+ # mention difficult to read. Since Active Record can figure out the ID of
202
+ # any fixture from its label, you can specify FK's by label instead of ID.
203
+ #
204
+ # === belongs_to
205
+ #
206
+ # Let's break out some more monkeys and pirates.
207
+ #
208
+ # ### in pirates.yml
209
+ #
210
+ # reginald:
211
+ # id: 1
212
+ # name: Reginald the Pirate
213
+ # monkey_id: 1
214
+ #
215
+ # ### in monkeys.yml
216
+ #
217
+ # george:
218
+ # id: 1
219
+ # name: George the Monkey
220
+ # pirate_id: 1
221
+ #
222
+ # Add a few more monkeys and pirates and break this into multiple files,
223
+ # and it gets pretty hard to keep track of what's going on. Let's
224
+ # use labels instead of IDs:
225
+ #
226
+ # ### in pirates.yml
227
+ #
228
+ # reginald:
229
+ # name: Reginald the Pirate
230
+ # monkey: george
231
+ #
232
+ # ### in monkeys.yml
233
+ #
234
+ # george:
235
+ # name: George the Monkey
236
+ # pirate: reginald
237
+ #
238
+ # Pow! All is made clear. Active Record reflects on the fixture's model class,
239
+ # finds all the +belongs_to+ associations, and allows you to specify
240
+ # a target *label* for the *association* (monkey: george) rather than
241
+ # a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
242
+ #
243
+ # ==== Polymorphic belongs_to
244
+ #
245
+ # Supporting polymorphic relationships is a little bit more complicated, since
246
+ # Active Record needs to know what type your association is pointing at. Something
247
+ # like this should look familiar:
248
+ #
249
+ # ### in fruit.rb
250
+ #
251
+ # belongs_to :eater, polymorphic: true
252
+ #
253
+ # ### in fruits.yml
254
+ #
255
+ # apple:
256
+ # id: 1
257
+ # name: apple
258
+ # eater_id: 1
259
+ # eater_type: Monkey
260
+ #
261
+ # Can we do better? You bet!
262
+ #
263
+ # apple:
264
+ # eater: george (Monkey)
265
+ #
266
+ # Just provide the polymorphic target type and Active Record will take care of the rest.
267
+ #
268
+ # === has_and_belongs_to_many
269
+ #
270
+ # Time to give our monkey some fruit.
271
+ #
272
+ # ### in monkeys.yml
273
+ #
274
+ # george:
275
+ # id: 1
276
+ # name: George the Monkey
277
+ #
278
+ # ### in fruits.yml
279
+ #
280
+ # apple:
281
+ # id: 1
282
+ # name: apple
283
+ #
284
+ # orange:
285
+ # id: 2
286
+ # name: orange
287
+ #
288
+ # grape:
289
+ # id: 3
290
+ # name: grape
291
+ #
292
+ # ### in fruits_monkeys.yml
293
+ #
294
+ # apple_george:
295
+ # fruit_id: 1
296
+ # monkey_id: 1
297
+ #
298
+ # orange_george:
299
+ # fruit_id: 2
300
+ # monkey_id: 1
301
+ #
302
+ # grape_george:
303
+ # fruit_id: 3
304
+ # monkey_id: 1
305
+ #
306
+ # Let's make the HABTM fixture go away.
307
+ #
308
+ # ### in monkeys.yml
309
+ #
310
+ # george:
311
+ # id: 1
312
+ # name: George the Monkey
313
+ # fruits: apple, orange, grape
314
+ #
315
+ # ### in fruits.yml
316
+ #
317
+ # apple:
318
+ # name: apple
319
+ #
320
+ # orange:
321
+ # name: orange
322
+ #
323
+ # grape:
324
+ # name: grape
325
+ #
326
+ # Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
327
+ # on George's fixture, but we could've just as easily specified a list
328
+ # of monkeys on each fruit. As with +belongs_to+, Active Record reflects on
329
+ # the fixture's model class and discovers the +has_and_belongs_to_many+
330
+ # associations.
331
+ #
332
+ # == Autofilled Timestamp Columns
333
+ #
334
+ # If your table/model specifies any of Active Record's
335
+ # standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
336
+ # they will automatically be set to <tt>Time.now</tt>.
337
+ #
338
+ # If you've set specific values, they'll be left alone.
339
+ #
340
+ # == Fixture label interpolation
341
+ #
342
+ # The label of the current fixture is always available as a column value:
343
+ #
344
+ # geeksomnia:
345
+ # name: Geeksomnia's Account
346
+ # subdomain: $LABEL
347
+ #
348
+ # Also, sometimes (like when porting older join table fixtures) you'll need
349
+ # to be able to get a hold of the identifier for a given label. ERB
350
+ # to the rescue:
351
+ #
352
+ # george_reginald:
353
+ # monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
354
+ # pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>
355
+ #
356
+ # == Support for YAML defaults
357
+ #
358
+ # You probably already know how to use YAML to set and reuse defaults in
359
+ # your <tt>database.yml</tt> file. You can use the same technique in your fixtures:
360
+ #
361
+ # DEFAULTS: &DEFAULTS
362
+ # created_on: <%= 3.weeks.ago.to_s(:db) %>
363
+ #
364
+ # first:
365
+ # name: Smurf
366
+ # <<: *DEFAULTS
367
+ #
368
+ # second:
369
+ # name: Fraggle
370
+ # <<: *DEFAULTS
371
+ #
372
+ # Any fixture labeled "DEFAULTS" is safely ignored.
373
+ class FixtureSet
374
+ #--
375
+ # An instance of FixtureSet is normally stored in a single YAML file and possibly in a folder with the same name.
376
+ #++
75
377
 
76
- delete_existing_fixtures
77
- insert_fixtures
78
- end
378
+ MAX_ID = 2 ** 30 - 1
79
379
 
80
- # Access a fixture hash by using its file name as the key
81
- def [](key)
82
- @fixtures[key]
83
- end
380
+ @@all_cached_fixtures = Hash.new { |h,k| h[k] = {} }
84
381
 
85
- # Get the number of fixtures kept in this container
86
- def length
87
- @fixtures.length
88
- end
382
+ def self.find_table_name(fixture_set_name) # :nodoc:
383
+ ActiveSupport::Deprecation.warn(
384
+ "ActiveRecord::Fixtures.find_table_name is deprecated and shall be removed from future releases. Use ActiveRecord::Fixtures.default_fixture_model_name instead.")
385
+ default_fixture_model_name(fixture_set_name)
386
+ end
387
+
388
+ def self.default_fixture_model_name(fixture_set_name) # :nodoc:
389
+ ActiveRecord::Base.pluralize_table_names ?
390
+ fixture_set_name.singularize.camelize :
391
+ fixture_set_name.camelize
392
+ end
393
+
394
+ def self.default_fixture_table_name(fixture_set_name) # :nodoc:
395
+ "#{ ActiveRecord::Base.table_name_prefix }"\
396
+ "#{ fixture_set_name.tr('/', '_') }"\
397
+ "#{ ActiveRecord::Base.table_name_suffix }".to_sym
398
+ end
399
+
400
+ def self.reset_cache
401
+ @@all_cached_fixtures.clear
402
+ end
89
403
 
90
- private
91
- def read_fixtures
92
- Dir.entries(@fixture_path).inject({}) do |fixtures, file|
93
- # is this a regular fixture file?
94
- fixtures[file] = Fixture.new(@fixture_path, file) unless file =~ @file_filter
95
- # is this a *.yaml file?
96
- if file =~ /\.yaml/
97
- YamlFixture.produce( "#{@fixture_path}/#{file}" ).each { |fix| fixtures[fix.yaml_name] = fix }
404
+ def self.cache_for_connection(connection)
405
+ @@all_cached_fixtures[connection]
406
+ end
407
+
408
+ def self.fixture_is_cached?(connection, table_name)
409
+ cache_for_connection(connection)[table_name]
410
+ end
411
+
412
+ def self.cached_fixtures(connection, keys_to_fetch = nil)
413
+ if keys_to_fetch
414
+ cache_for_connection(connection).values_at(*keys_to_fetch)
415
+ else
416
+ cache_for_connection(connection).values
417
+ end
418
+ end
419
+
420
+ def self.cache_fixtures(connection, fixtures_map)
421
+ cache_for_connection(connection).update(fixtures_map)
422
+ end
423
+
424
+ def self.instantiate_fixtures(object, fixture_set, load_instances = true)
425
+ if load_instances
426
+ fixture_set.each do |fixture_name, fixture|
427
+ begin
428
+ object.instance_variable_set "@#{fixture_name}", fixture.find
429
+ rescue FixtureClassNotFound
430
+ nil
431
+ end
98
432
  end
99
- fixtures
100
433
  end
101
434
  end
102
435
 
103
- def delete_existing_fixtures
104
- @connection.delete "DELETE FROM #{@table_name}"
436
+ def self.instantiate_all_loaded_fixtures(object, load_instances = true)
437
+ all_loaded_fixtures.each_value do |fixture_set|
438
+ instantiate_fixtures(object, fixture_set, load_instances)
439
+ end
105
440
  end
106
441
 
107
- def insert_fixtures
108
- @fixtures.values.each do |fixture|
109
- @connection.execute "INSERT INTO #{@table_name} (#{fixture.key_list}) VALUES(#{fixture.value_list})"
442
+ cattr_accessor :all_loaded_fixtures
443
+ self.all_loaded_fixtures = {}
444
+
445
+ def self.create_fixtures(fixtures_directory, fixture_set_names, class_names = {})
446
+ fixture_set_names = Array(fixture_set_names).map(&:to_s)
447
+ class_names = class_names.stringify_keys
448
+
449
+ # FIXME: Apparently JK uses this.
450
+ connection = block_given? ? yield : ActiveRecord::Base.connection
451
+
452
+ files_to_read = fixture_set_names.reject { |fs_name|
453
+ fixture_is_cached?(connection, fs_name)
454
+ }
455
+
456
+ unless files_to_read.empty?
457
+ connection.disable_referential_integrity do
458
+ fixtures_map = {}
459
+
460
+ fixture_sets = files_to_read.map do |fs_name|
461
+ fixtures_map[fs_name] = new( # ActiveRecord::FixtureSet.new
462
+ connection,
463
+ fs_name,
464
+ class_names[fs_name] || default_fixture_model_name(fs_name),
465
+ ::File.join(fixtures_directory, fs_name))
466
+ end
467
+
468
+ all_loaded_fixtures.update(fixtures_map)
469
+
470
+ connection.transaction(:requires_new => true) do
471
+ fixture_sets.each do |fs|
472
+ conn = fs.model_class.respond_to?(:connection) ? fs.model_class.connection : connection
473
+ table_rows = fs.table_rows
474
+
475
+ table_rows.keys.each do |table|
476
+ conn.delete "DELETE FROM #{conn.quote_table_name(table)}", 'Fixture Delete'
477
+ end
478
+
479
+ table_rows.each do |fixture_set_name, rows|
480
+ rows.each do |row|
481
+ conn.insert_fixture(row, fixture_set_name)
482
+ end
483
+ end
484
+ end
485
+
486
+ # Cap primary key sequences to max(pk).
487
+ if connection.respond_to?(:reset_pk_sequence!)
488
+ fixture_sets.each do |fs|
489
+ connection.reset_pk_sequence!(fs.table_name)
490
+ end
491
+ end
492
+ end
493
+
494
+ cache_fixtures(connection, fixtures_map)
495
+ end
110
496
  end
497
+ cached_fixtures(connection, fixture_set_names)
111
498
  end
112
499
 
113
- def []=(key, value)
114
- @fixtures[key] = value
500
+ # Returns a consistent, platform-independent identifier for +label+.
501
+ # Identifiers are positive integers less than 2^32.
502
+ def self.identify(label)
503
+ Zlib.crc32(label.to_s) % MAX_ID
115
504
  end
116
- end
117
505
 
118
- class Fixture #:nodoc:
119
- def initialize(fixture_path, file)
120
- @fixture_path, @file = fixture_path, file
121
- @fixture = read_fixture
122
- end
506
+ attr_reader :table_name, :name, :fixtures, :model_class
123
507
 
124
- def [](key)
125
- @fixture[key]
126
- end
508
+ def initialize(connection, name, class_name, path)
509
+ @fixtures = {} # Ordered hash
510
+ @name = name
511
+ @path = path
127
512
 
128
- def to_hash
129
- @fixture
130
- end
513
+ if class_name.is_a?(Class) # TODO: Should be an AR::Base type class, or any?
514
+ @model_class = class_name
515
+ else
516
+ @model_class = class_name.constantize rescue nil
517
+ end
131
518
 
132
- def key_list
133
- @fixture.keys.join(", ")
134
- end
519
+ @connection = ( model_class.respond_to?(:connection) ?
520
+ model_class.connection : connection )
521
+
522
+ @table_name = ( model_class.respond_to?(:table_name) ?
523
+ model_class.table_name :
524
+ self.class.default_fixture_table_name(name) )
525
+
526
+ read_fixture_files
527
+ end
528
+
529
+ def [](x)
530
+ fixtures[x]
531
+ end
532
+
533
+ def []=(k,v)
534
+ fixtures[k] = v
535
+ end
536
+
537
+ def each(&block)
538
+ fixtures.each(&block)
539
+ end
540
+
541
+ def size
542
+ fixtures.size
543
+ end
544
+
545
+ # Return a hash of rows to be inserted. The key is the table, the value is
546
+ # a list of rows to insert to that table.
547
+ def table_rows
548
+ now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
549
+ now = now.to_s(:db)
550
+
551
+ # allow a standard key to be used for doing defaults in YAML
552
+ fixtures.delete('DEFAULTS')
553
+
554
+ # track any join tables we need to insert later
555
+ rows = Hash.new { |h,table| h[table] = [] }
556
+
557
+ rows[table_name] = fixtures.map do |label, fixture|
558
+ row = fixture.to_hash
559
+
560
+ if model_class && model_class < ActiveRecord::Base
561
+ # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
562
+ if model_class.record_timestamps
563
+ timestamp_column_names.each do |c_name|
564
+ row[c_name] = now unless row.key?(c_name)
565
+ end
566
+ end
567
+
568
+ # interpolate the fixture label
569
+ row.each do |key, value|
570
+ row[key] = label if value == "$LABEL"
571
+ end
572
+
573
+ # generate a primary key if necessary
574
+ if has_primary_key_column? && !row.include?(primary_key_name)
575
+ row[primary_key_name] = ActiveRecord::FixtureSet.identify(label)
576
+ end
577
+
578
+ # If STI is used, find the correct subclass for association reflection
579
+ reflection_class =
580
+ if row.include?(inheritance_column_name)
581
+ row[inheritance_column_name].constantize rescue model_class
582
+ else
583
+ model_class
584
+ end
585
+
586
+ reflection_class.reflect_on_all_associations.each do |association|
587
+ case association.macro
588
+ when :belongs_to
589
+ # Do not replace association name with association foreign key if they are named the same
590
+ fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
591
+
592
+ if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
593
+ if association.options[:polymorphic] && value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
594
+ # support polymorphic belongs_to as "label (Type)"
595
+ row[association.foreign_type] = $1
596
+ end
597
+
598
+ row[fk_name] = ActiveRecord::FixtureSet.identify(value)
599
+ end
600
+ when :has_and_belongs_to_many
601
+ if (targets = row.delete(association.name.to_s))
602
+ targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
603
+ table_name = association.join_table
604
+ rows[table_name].concat targets.map { |target|
605
+ { association.foreign_key => row[primary_key_name],
606
+ association.association_foreign_key => ActiveRecord::FixtureSet.identify(target) }
607
+ }
608
+ end
609
+ end
610
+ end
611
+ end
612
+
613
+ row
614
+ end
615
+ rows
616
+ end
617
+
618
+ private
619
+ def primary_key_name
620
+ @primary_key_name ||= model_class && model_class.primary_key
621
+ end
622
+
623
+ def has_primary_key_column?
624
+ @has_primary_key_column ||= primary_key_name &&
625
+ model_class.columns.any? { |c| c.name == primary_key_name }
626
+ end
627
+
628
+ def timestamp_column_names
629
+ @timestamp_column_names ||=
630
+ %w(created_at created_on updated_at updated_on) & column_names
631
+ end
632
+
633
+ def inheritance_column_name
634
+ @inheritance_column_name ||= model_class && model_class.inheritance_column
635
+ end
636
+
637
+ def column_names
638
+ @column_names ||= @connection.columns(@table_name).collect { |c| c.name }
639
+ end
640
+
641
+ def read_fixture_files
642
+ yaml_files = Dir["#{@path}/**/*.yml"].select { |f|
643
+ ::File.file?(f)
644
+ } + [yaml_file_path]
645
+
646
+ yaml_files.each do |file|
647
+ FixtureSet::File.open(file) do |fh|
648
+ fh.each do |fixture_name, row|
649
+ fixtures[fixture_name] = ActiveRecord::Fixture.new(row, model_class)
650
+ end
651
+ end
652
+ end
653
+ end
654
+
655
+ def yaml_file_path
656
+ "#{@path}.yml"
657
+ end
135
658
 
136
- def value_list
137
- @fixture.values.map { |v| "'#{v}'" }.join(", ")
138
659
  end
139
660
 
140
- private
141
- def read_fixture
142
- IO.readlines("#{@fixture_path}/#{@file}").inject({}) do |fixture, line|
143
- key, value = line.split(/ => /)
144
- fixture[key.strip] = value.strip
145
- fixture
661
+ #--
662
+ # Deprecate 'Fixtures' in favor of 'FixtureSet'.
663
+ #++
664
+ # :nodoc:
665
+ Fixtures = ActiveSupport::Deprecation::DeprecatedConstantProxy.new('ActiveRecord::Fixtures', 'ActiveRecord::FixtureSet')
666
+
667
+ class Fixture #:nodoc:
668
+ include Enumerable
669
+
670
+ class FixtureError < StandardError #:nodoc:
671
+ end
672
+
673
+ class FormatError < FixtureError #:nodoc:
674
+ end
675
+
676
+ attr_reader :model_class, :fixture
677
+
678
+ def initialize(fixture, model_class)
679
+ @fixture = fixture
680
+ @model_class = model_class
681
+ end
682
+
683
+ def class_name
684
+ model_class.name if model_class
685
+ end
686
+
687
+ def each
688
+ fixture.each { |item| yield item }
689
+ end
690
+
691
+ def [](key)
692
+ fixture[key]
693
+ end
694
+
695
+ alias :to_hash :fixture
696
+
697
+ def find
698
+ if model_class
699
+ model_class.find(fixture[model_class.primary_key])
700
+ else
701
+ raise FixtureClassNotFound, "No class attached to find."
146
702
  end
147
703
  end
704
+ end
148
705
  end
149
706
 
150
- # A YamlFixture is like a fixture, but instead of a name to use as
151
- # a key, it uses a yaml_name.
152
- class YamlFixture < Fixture #:nodoc:
153
- # yaml_name is equivalent to a normal fixture's filename
154
- attr_accessor :yaml_name
707
+ module ActiveRecord
708
+ module TestFixtures
709
+ extend ActiveSupport::Concern
155
710
 
156
- # constructor is passed the name & the actual instantiate fixture
157
- def initialize(yaml_name, fixture)
158
- @yaml_name, @fixture = yaml_name, fixture
159
- end
711
+ def before_setup
712
+ setup_fixtures
713
+ super
714
+ end
715
+
716
+ def after_teardown
717
+ super
718
+ teardown_fixtures
719
+ end
720
+
721
+ included do
722
+ class_attribute :fixture_path, :instance_writer => false
723
+ class_attribute :fixture_table_names
724
+ class_attribute :fixture_class_names
725
+ class_attribute :use_transactional_fixtures
726
+ class_attribute :use_instantiated_fixtures # true, false, or :no_instances
727
+ class_attribute :pre_loaded_fixtures
728
+
729
+ self.fixture_table_names = []
730
+ self.use_transactional_fixtures = true
731
+ self.use_instantiated_fixtures = false
732
+ self.pre_loaded_fixtures = false
160
733
 
161
- # given a valid yaml file name, create an array of YamlFixture objects
162
- def self.produce( yaml_file_name )
163
- results = []
164
- yaml_file = File.open( yaml_file_name )
165
- YAML::load_documents( yaml_file ) do |doc|
166
- f = YamlFixture.new( doc['name'], doc['data'] )
167
- results << f
734
+ self.fixture_class_names = Hash.new do |h, fixture_set_name|
735
+ h[fixture_set_name] = ActiveRecord::FixtureSet.default_fixture_model_name(fixture_set_name)
736
+ end
168
737
  end
169
- yaml_file.close
170
- results
738
+
739
+ module ClassMethods
740
+ # Sets the model class for a fixture when the class name cannot be inferred from the fixture name.
741
+ #
742
+ # Examples:
743
+ #
744
+ # set_fixture_class some_fixture: SomeModel,
745
+ # 'namespaced/fixture' => Another::Model
746
+ #
747
+ # The keys must be the fixture names, that coincide with the short paths to the fixture files.
748
+ #--
749
+ # It is also possible to pass the class name instead of the class:
750
+ # set_fixture_class 'some_fixture' => 'SomeModel'
751
+ # I think this option is redundant, i propose to deprecate it.
752
+ # Isn't it easier to always pass the class itself?
753
+ # (2011-12-20 alexeymuranov)
754
+ #++
755
+ def set_fixture_class(class_names = {})
756
+ self.fixture_class_names = self.fixture_class_names.merge(class_names.stringify_keys)
757
+ end
758
+
759
+ def fixtures(*fixture_set_names)
760
+ if fixture_set_names.first == :all
761
+ fixture_set_names = Dir["#{fixture_path}/**/*.{yml}"]
762
+ fixture_set_names.map! { |f| f[(fixture_path.to_s.size + 1)..-5] }
763
+ else
764
+ fixture_set_names = fixture_set_names.flatten.map { |n| n.to_s }
765
+ end
766
+
767
+ self.fixture_table_names |= fixture_set_names
768
+ require_fixture_classes(fixture_set_names)
769
+ setup_fixture_accessors(fixture_set_names)
770
+ end
771
+
772
+ def try_to_load_dependency(file_name)
773
+ require_dependency file_name
774
+ rescue LoadError => e
775
+ # Let's hope the developer has included it
776
+ # Let's warn in case this is a subdependency, otherwise
777
+ # subdependency error messages are totally cryptic
778
+ if ActiveRecord::Base.logger
779
+ ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
780
+ end
781
+ end
782
+
783
+ def require_fixture_classes(fixture_set_names = nil)
784
+ if fixture_set_names
785
+ fixture_set_names = fixture_set_names.map { |n| n.to_s }
786
+ else
787
+ fixture_set_names = fixture_table_names
788
+ end
789
+
790
+ fixture_set_names.each do |file_name|
791
+ file_name = file_name.singularize if ActiveRecord::Base.pluralize_table_names
792
+ try_to_load_dependency(file_name)
793
+ end
794
+ end
795
+
796
+ def setup_fixture_accessors(fixture_set_names = nil)
797
+ fixture_set_names = Array(fixture_set_names || fixture_table_names)
798
+ methods = Module.new do
799
+ fixture_set_names.each do |fs_name|
800
+ fs_name = fs_name.to_s
801
+ accessor_name = fs_name.tr('/', '_').to_sym
802
+
803
+ define_method(accessor_name) do |*fixture_names|
804
+ force_reload = fixture_names.pop if fixture_names.last == true || fixture_names.last == :reload
805
+
806
+ @fixture_cache[fs_name] ||= {}
807
+
808
+ instances = fixture_names.map do |f_name|
809
+ f_name = f_name.to_s
810
+ @fixture_cache[fs_name].delete(f_name) if force_reload
811
+
812
+ if @loaded_fixtures[fs_name][f_name]
813
+ @fixture_cache[fs_name][f_name] ||= @loaded_fixtures[fs_name][f_name].find
814
+ else
815
+ raise StandardError, "No fixture named '#{f_name}' found for fixture set '#{fs_name}'"
816
+ end
817
+ end
818
+
819
+ instances.size == 1 ? instances.first : instances
820
+ end
821
+ private accessor_name
822
+ end
823
+ end
824
+ include methods
825
+ end
826
+
827
+ def uses_transaction(*methods)
828
+ @uses_transaction = [] unless defined?(@uses_transaction)
829
+ @uses_transaction.concat methods.map { |m| m.to_s }
830
+ end
831
+
832
+ def uses_transaction?(method)
833
+ @uses_transaction = [] unless defined?(@uses_transaction)
834
+ @uses_transaction.include?(method.to_s)
835
+ end
836
+ end
837
+
838
+ def run_in_transaction?
839
+ use_transactional_fixtures &&
840
+ !self.class.uses_transaction?(method_name)
841
+ end
842
+
843
+ def setup_fixtures
844
+ return if ActiveRecord::Base.configurations.blank?
845
+
846
+ if pre_loaded_fixtures && !use_transactional_fixtures
847
+ raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
848
+ end
849
+
850
+ @fixture_cache = {}
851
+ @fixture_connections = []
852
+ @@already_loaded_fixtures ||= {}
853
+
854
+ # Load fixtures once and begin transaction.
855
+ if run_in_transaction?
856
+ if @@already_loaded_fixtures[self.class]
857
+ @loaded_fixtures = @@already_loaded_fixtures[self.class]
858
+ else
859
+ @loaded_fixtures = load_fixtures
860
+ @@already_loaded_fixtures[self.class] = @loaded_fixtures
861
+ end
862
+ @fixture_connections = enlist_fixture_connections
863
+ @fixture_connections.each do |connection|
864
+ connection.begin_transaction joinable: false
865
+ end
866
+ # Load fixtures for every test.
867
+ else
868
+ ActiveRecord::FixtureSet.reset_cache
869
+ @@already_loaded_fixtures[self.class] = nil
870
+ @loaded_fixtures = load_fixtures
871
+ end
872
+
873
+ # Instantiate fixtures for every test if requested.
874
+ instantiate_fixtures if use_instantiated_fixtures
875
+ end
876
+
877
+ def teardown_fixtures
878
+ return if ActiveRecord::Base.configurations.blank?
879
+
880
+ # Rollback changes if a transaction is active.
881
+ if run_in_transaction?
882
+ @fixture_connections.each do |connection|
883
+ connection.rollback_transaction if connection.transaction_open?
884
+ end
885
+ @fixture_connections.clear
886
+ else
887
+ ActiveRecord::FixtureSet.reset_cache
888
+ end
889
+
890
+ ActiveRecord::Base.clear_active_connections!
891
+ end
892
+
893
+ def enlist_fixture_connections
894
+ ActiveRecord::Base.connection_handler.connection_pool_list.map(&:connection)
895
+ end
896
+
897
+ private
898
+ def load_fixtures
899
+ fixtures = ActiveRecord::FixtureSet.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
900
+ Hash[fixtures.map { |f| [f.name, f] }]
901
+ end
902
+
903
+ # for pre_loaded_fixtures, only require the classes once. huge speed improvement
904
+ @@required_fixture_classes = false
905
+
906
+ def instantiate_fixtures
907
+ if pre_loaded_fixtures
908
+ raise RuntimeError, 'Load fixtures before instantiating them.' if ActiveRecord::FixtureSet.all_loaded_fixtures.empty?
909
+ unless @@required_fixture_classes
910
+ self.class.require_fixture_classes ActiveRecord::FixtureSet.all_loaded_fixtures.keys
911
+ @@required_fixture_classes = true
912
+ end
913
+ ActiveRecord::FixtureSet.instantiate_all_loaded_fixtures(self, load_instances?)
914
+ else
915
+ raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
916
+ @loaded_fixtures.each_value do |fixture_set|
917
+ ActiveRecord::FixtureSet.instantiate_fixtures(self, fixture_set, load_instances?)
918
+ end
919
+ end
920
+ end
921
+
922
+ def load_instances?
923
+ use_instantiated_fixtures != :no_instances
924
+ end
171
925
  end
172
926
  end