activerecord_csi 2.3.5.p6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (333) hide show
  1. data/CHANGELOG +5858 -0
  2. data/README +351 -0
  3. data/RUNNING_UNIT_TESTS +36 -0
  4. data/Rakefile +270 -0
  5. data/examples/associations.png +0 -0
  6. data/examples/performance.rb +162 -0
  7. data/install.rb +30 -0
  8. data/lib/active_record/aggregations.rb +261 -0
  9. data/lib/active_record/association_preload.rb +389 -0
  10. data/lib/active_record/associations/association_collection.rb +475 -0
  11. data/lib/active_record/associations/association_proxy.rb +278 -0
  12. data/lib/active_record/associations/belongs_to_association.rb +76 -0
  13. data/lib/active_record/associations/belongs_to_polymorphic_association.rb +53 -0
  14. data/lib/active_record/associations/has_and_belongs_to_many_association.rb +143 -0
  15. data/lib/active_record/associations/has_many_association.rb +122 -0
  16. data/lib/active_record/associations/has_many_through_association.rb +266 -0
  17. data/lib/active_record/associations/has_one_association.rb +133 -0
  18. data/lib/active_record/associations/has_one_through_association.rb +37 -0
  19. data/lib/active_record/associations.rb +2241 -0
  20. data/lib/active_record/attribute_methods.rb +388 -0
  21. data/lib/active_record/autosave_association.rb +364 -0
  22. data/lib/active_record/base.rb +3171 -0
  23. data/lib/active_record/batches.rb +81 -0
  24. data/lib/active_record/calculations.rb +311 -0
  25. data/lib/active_record/callbacks.rb +360 -0
  26. data/lib/active_record/connection_adapters/abstract/connection_pool.rb +371 -0
  27. data/lib/active_record/connection_adapters/abstract/connection_specification.rb +139 -0
  28. data/lib/active_record/connection_adapters/abstract/database_statements.rb +289 -0
  29. data/lib/active_record/connection_adapters/abstract/query_cache.rb +94 -0
  30. data/lib/active_record/connection_adapters/abstract/quoting.rb +69 -0
  31. data/lib/active_record/connection_adapters/abstract/schema_definitions.rb +722 -0
  32. data/lib/active_record/connection_adapters/abstract/schema_statements.rb +434 -0
  33. data/lib/active_record/connection_adapters/abstract_adapter.rb +241 -0
  34. data/lib/active_record/connection_adapters/mysql_adapter.rb +630 -0
  35. data/lib/active_record/connection_adapters/postgresql_adapter.rb +1113 -0
  36. data/lib/active_record/connection_adapters/sqlite3_adapter.rb +34 -0
  37. data/lib/active_record/connection_adapters/sqlite_adapter.rb +453 -0
  38. data/lib/active_record/dirty.rb +183 -0
  39. data/lib/active_record/dynamic_finder_match.rb +41 -0
  40. data/lib/active_record/dynamic_scope_match.rb +25 -0
  41. data/lib/active_record/fixtures.rb +996 -0
  42. data/lib/active_record/i18n_interpolation_deprecation.rb +26 -0
  43. data/lib/active_record/locale/en.yml +58 -0
  44. data/lib/active_record/locking/optimistic.rb +148 -0
  45. data/lib/active_record/locking/pessimistic.rb +55 -0
  46. data/lib/active_record/migration.rb +566 -0
  47. data/lib/active_record/named_scope.rb +192 -0
  48. data/lib/active_record/nested_attributes.rb +392 -0
  49. data/lib/active_record/observer.rb +197 -0
  50. data/lib/active_record/query_cache.rb +33 -0
  51. data/lib/active_record/reflection.rb +320 -0
  52. data/lib/active_record/schema.rb +51 -0
  53. data/lib/active_record/schema_dumper.rb +182 -0
  54. data/lib/active_record/serialization.rb +101 -0
  55. data/lib/active_record/serializers/json_serializer.rb +91 -0
  56. data/lib/active_record/serializers/xml_serializer.rb +357 -0
  57. data/lib/active_record/session_store.rb +326 -0
  58. data/lib/active_record/test_case.rb +66 -0
  59. data/lib/active_record/timestamp.rb +71 -0
  60. data/lib/active_record/transactions.rb +235 -0
  61. data/lib/active_record/validations.rb +1135 -0
  62. data/lib/active_record/version.rb +9 -0
  63. data/lib/active_record.rb +84 -0
  64. data/lib/activerecord.rb +2 -0
  65. data/test/assets/example.log +1 -0
  66. data/test/assets/flowers.jpg +0 -0
  67. data/test/cases/aaa_create_tables_test.rb +24 -0
  68. data/test/cases/active_schema_test_mysql.rb +100 -0
  69. data/test/cases/active_schema_test_postgresql.rb +24 -0
  70. data/test/cases/adapter_test.rb +145 -0
  71. data/test/cases/aggregations_test.rb +167 -0
  72. data/test/cases/ar_schema_test.rb +32 -0
  73. data/test/cases/associations/belongs_to_associations_test.rb +425 -0
  74. data/test/cases/associations/callbacks_test.rb +161 -0
  75. data/test/cases/associations/cascaded_eager_loading_test.rb +131 -0
  76. data/test/cases/associations/eager_load_includes_full_sti_class_test.rb +36 -0
  77. data/test/cases/associations/eager_load_nested_include_test.rb +130 -0
  78. data/test/cases/associations/eager_singularization_test.rb +145 -0
  79. data/test/cases/associations/eager_test.rb +834 -0
  80. data/test/cases/associations/extension_test.rb +62 -0
  81. data/test/cases/associations/habtm_join_table_test.rb +56 -0
  82. data/test/cases/associations/has_and_belongs_to_many_associations_test.rb +822 -0
  83. data/test/cases/associations/has_many_associations_test.rb +1134 -0
  84. data/test/cases/associations/has_many_through_associations_test.rb +346 -0
  85. data/test/cases/associations/has_one_associations_test.rb +330 -0
  86. data/test/cases/associations/has_one_through_associations_test.rb +209 -0
  87. data/test/cases/associations/inner_join_association_test.rb +93 -0
  88. data/test/cases/associations/join_model_test.rb +712 -0
  89. data/test/cases/associations_test.rb +262 -0
  90. data/test/cases/attribute_methods_test.rb +305 -0
  91. data/test/cases/autosave_association_test.rb +1142 -0
  92. data/test/cases/base_test.rb +2154 -0
  93. data/test/cases/batches_test.rb +61 -0
  94. data/test/cases/binary_test.rb +30 -0
  95. data/test/cases/calculations_test.rb +348 -0
  96. data/test/cases/callbacks_observers_test.rb +38 -0
  97. data/test/cases/callbacks_test.rb +438 -0
  98. data/test/cases/class_inheritable_attributes_test.rb +32 -0
  99. data/test/cases/column_alias_test.rb +17 -0
  100. data/test/cases/column_definition_test.rb +70 -0
  101. data/test/cases/connection_pool_test.rb +25 -0
  102. data/test/cases/connection_test_firebird.rb +8 -0
  103. data/test/cases/connection_test_mysql.rb +64 -0
  104. data/test/cases/copy_table_test_sqlite.rb +80 -0
  105. data/test/cases/database_statements_test.rb +12 -0
  106. data/test/cases/datatype_test_postgresql.rb +204 -0
  107. data/test/cases/date_time_test.rb +37 -0
  108. data/test/cases/default_test_firebird.rb +16 -0
  109. data/test/cases/defaults_test.rb +111 -0
  110. data/test/cases/deprecated_finder_test.rb +30 -0
  111. data/test/cases/dirty_test.rb +316 -0
  112. data/test/cases/finder_respond_to_test.rb +76 -0
  113. data/test/cases/finder_test.rb +1066 -0
  114. data/test/cases/fixtures_test.rb +656 -0
  115. data/test/cases/helper.rb +68 -0
  116. data/test/cases/i18n_test.rb +46 -0
  117. data/test/cases/inheritance_test.rb +262 -0
  118. data/test/cases/invalid_date_test.rb +24 -0
  119. data/test/cases/json_serialization_test.rb +205 -0
  120. data/test/cases/lifecycle_test.rb +193 -0
  121. data/test/cases/locking_test.rb +304 -0
  122. data/test/cases/method_scoping_test.rb +704 -0
  123. data/test/cases/migration_test.rb +1523 -0
  124. data/test/cases/migration_test_firebird.rb +124 -0
  125. data/test/cases/mixin_test.rb +96 -0
  126. data/test/cases/modules_test.rb +81 -0
  127. data/test/cases/multiple_db_test.rb +85 -0
  128. data/test/cases/named_scope_test.rb +361 -0
  129. data/test/cases/nested_attributes_test.rb +581 -0
  130. data/test/cases/pk_test.rb +119 -0
  131. data/test/cases/pooled_connections_test.rb +103 -0
  132. data/test/cases/query_cache_test.rb +123 -0
  133. data/test/cases/readonly_test.rb +107 -0
  134. data/test/cases/reflection_test.rb +194 -0
  135. data/test/cases/reload_models_test.rb +22 -0
  136. data/test/cases/repair_helper.rb +50 -0
  137. data/test/cases/reserved_word_test_mysql.rb +176 -0
  138. data/test/cases/sanitize_test.rb +25 -0
  139. data/test/cases/schema_authorization_test_postgresql.rb +75 -0
  140. data/test/cases/schema_dumper_test.rb +211 -0
  141. data/test/cases/schema_test_postgresql.rb +178 -0
  142. data/test/cases/serialization_test.rb +47 -0
  143. data/test/cases/synonym_test_oracle.rb +17 -0
  144. data/test/cases/timestamp_test.rb +75 -0
  145. data/test/cases/transactions_test.rb +522 -0
  146. data/test/cases/unconnected_test.rb +32 -0
  147. data/test/cases/validations_i18n_test.rb +955 -0
  148. data/test/cases/validations_test.rb +1640 -0
  149. data/test/cases/xml_serialization_test.rb +240 -0
  150. data/test/config.rb +5 -0
  151. data/test/connections/jdbc_jdbcderby/connection.rb +18 -0
  152. data/test/connections/jdbc_jdbch2/connection.rb +18 -0
  153. data/test/connections/jdbc_jdbchsqldb/connection.rb +18 -0
  154. data/test/connections/jdbc_jdbcmysql/connection.rb +26 -0
  155. data/test/connections/jdbc_jdbcpostgresql/connection.rb +26 -0
  156. data/test/connections/jdbc_jdbcsqlite3/connection.rb +25 -0
  157. data/test/connections/native_db2/connection.rb +25 -0
  158. data/test/connections/native_firebird/connection.rb +26 -0
  159. data/test/connections/native_frontbase/connection.rb +27 -0
  160. data/test/connections/native_mysql/connection.rb +25 -0
  161. data/test/connections/native_openbase/connection.rb +21 -0
  162. data/test/connections/native_oracle/connection.rb +27 -0
  163. data/test/connections/native_postgresql/connection.rb +25 -0
  164. data/test/connections/native_sqlite/connection.rb +25 -0
  165. data/test/connections/native_sqlite3/connection.rb +25 -0
  166. data/test/connections/native_sqlite3/in_memory_connection.rb +18 -0
  167. data/test/connections/native_sybase/connection.rb +23 -0
  168. data/test/fixtures/accounts.yml +29 -0
  169. data/test/fixtures/all/developers.yml +0 -0
  170. data/test/fixtures/all/people.csv +0 -0
  171. data/test/fixtures/all/tasks.yml +0 -0
  172. data/test/fixtures/author_addresses.yml +5 -0
  173. data/test/fixtures/author_favorites.yml +4 -0
  174. data/test/fixtures/authors.yml +9 -0
  175. data/test/fixtures/binaries.yml +132 -0
  176. data/test/fixtures/books.yml +7 -0
  177. data/test/fixtures/categories/special_categories.yml +9 -0
  178. data/test/fixtures/categories/subsubdir/arbitrary_filename.yml +4 -0
  179. data/test/fixtures/categories.yml +14 -0
  180. data/test/fixtures/categories_ordered.yml +7 -0
  181. data/test/fixtures/categories_posts.yml +23 -0
  182. data/test/fixtures/categorizations.yml +17 -0
  183. data/test/fixtures/clubs.yml +6 -0
  184. data/test/fixtures/comments.yml +59 -0
  185. data/test/fixtures/companies.yml +56 -0
  186. data/test/fixtures/computers.yml +4 -0
  187. data/test/fixtures/courses.yml +7 -0
  188. data/test/fixtures/customers.yml +26 -0
  189. data/test/fixtures/developers.yml +21 -0
  190. data/test/fixtures/developers_projects.yml +17 -0
  191. data/test/fixtures/edges.yml +6 -0
  192. data/test/fixtures/entrants.yml +14 -0
  193. data/test/fixtures/fixture_database.sqlite3 +0 -0
  194. data/test/fixtures/fixture_database_2.sqlite3 +0 -0
  195. data/test/fixtures/fk_test_has_fk.yml +3 -0
  196. data/test/fixtures/fk_test_has_pk.yml +2 -0
  197. data/test/fixtures/funny_jokes.yml +10 -0
  198. data/test/fixtures/items.yml +4 -0
  199. data/test/fixtures/jobs.yml +7 -0
  200. data/test/fixtures/legacy_things.yml +3 -0
  201. data/test/fixtures/mateys.yml +4 -0
  202. data/test/fixtures/member_types.yml +6 -0
  203. data/test/fixtures/members.yml +6 -0
  204. data/test/fixtures/memberships.yml +20 -0
  205. data/test/fixtures/minimalistics.yml +2 -0
  206. data/test/fixtures/mixed_case_monkeys.yml +6 -0
  207. data/test/fixtures/mixins.yml +29 -0
  208. data/test/fixtures/movies.yml +7 -0
  209. data/test/fixtures/naked/csv/accounts.csv +1 -0
  210. data/test/fixtures/naked/yml/accounts.yml +1 -0
  211. data/test/fixtures/naked/yml/companies.yml +1 -0
  212. data/test/fixtures/naked/yml/courses.yml +1 -0
  213. data/test/fixtures/organizations.yml +5 -0
  214. data/test/fixtures/owners.yml +7 -0
  215. data/test/fixtures/parrots.yml +27 -0
  216. data/test/fixtures/parrots_pirates.yml +7 -0
  217. data/test/fixtures/people.yml +15 -0
  218. data/test/fixtures/pets.yml +14 -0
  219. data/test/fixtures/pirates.yml +9 -0
  220. data/test/fixtures/posts.yml +52 -0
  221. data/test/fixtures/price_estimates.yml +7 -0
  222. data/test/fixtures/projects.yml +7 -0
  223. data/test/fixtures/readers.yml +9 -0
  224. data/test/fixtures/references.yml +17 -0
  225. data/test/fixtures/reserved_words/distinct.yml +5 -0
  226. data/test/fixtures/reserved_words/distincts_selects.yml +11 -0
  227. data/test/fixtures/reserved_words/group.yml +14 -0
  228. data/test/fixtures/reserved_words/select.yml +8 -0
  229. data/test/fixtures/reserved_words/values.yml +7 -0
  230. data/test/fixtures/ships.yml +5 -0
  231. data/test/fixtures/sponsors.yml +9 -0
  232. data/test/fixtures/subscribers.yml +7 -0
  233. data/test/fixtures/subscriptions.yml +12 -0
  234. data/test/fixtures/taggings.yml +28 -0
  235. data/test/fixtures/tags.yml +7 -0
  236. data/test/fixtures/tasks.yml +7 -0
  237. data/test/fixtures/topics.yml +42 -0
  238. data/test/fixtures/toys.yml +4 -0
  239. data/test/fixtures/treasures.yml +10 -0
  240. data/test/fixtures/vertices.yml +4 -0
  241. data/test/fixtures/warehouse-things.yml +3 -0
  242. data/test/migrations/broken/100_migration_that_raises_exception.rb +10 -0
  243. data/test/migrations/decimal/1_give_me_big_numbers.rb +15 -0
  244. data/test/migrations/duplicate/1_people_have_last_names.rb +9 -0
  245. data/test/migrations/duplicate/2_we_need_reminders.rb +12 -0
  246. data/test/migrations/duplicate/3_foo.rb +7 -0
  247. data/test/migrations/duplicate/3_innocent_jointable.rb +12 -0
  248. data/test/migrations/duplicate_names/20080507052938_chunky.rb +7 -0
  249. data/test/migrations/duplicate_names/20080507053028_chunky.rb +7 -0
  250. data/test/migrations/interleaved/pass_1/3_innocent_jointable.rb +12 -0
  251. data/test/migrations/interleaved/pass_2/1_people_have_last_names.rb +9 -0
  252. data/test/migrations/interleaved/pass_2/3_innocent_jointable.rb +12 -0
  253. data/test/migrations/interleaved/pass_3/1_people_have_last_names.rb +9 -0
  254. data/test/migrations/interleaved/pass_3/2_i_raise_on_down.rb +8 -0
  255. data/test/migrations/interleaved/pass_3/3_innocent_jointable.rb +12 -0
  256. data/test/migrations/missing/1000_people_have_middle_names.rb +9 -0
  257. data/test/migrations/missing/1_people_have_last_names.rb +9 -0
  258. data/test/migrations/missing/3_we_need_reminders.rb +12 -0
  259. data/test/migrations/missing/4_innocent_jointable.rb +12 -0
  260. data/test/migrations/valid/1_people_have_last_names.rb +9 -0
  261. data/test/migrations/valid/2_we_need_reminders.rb +12 -0
  262. data/test/migrations/valid/3_innocent_jointable.rb +12 -0
  263. data/test/models/author.rb +146 -0
  264. data/test/models/auto_id.rb +4 -0
  265. data/test/models/binary.rb +2 -0
  266. data/test/models/bird.rb +3 -0
  267. data/test/models/book.rb +4 -0
  268. data/test/models/categorization.rb +5 -0
  269. data/test/models/category.rb +34 -0
  270. data/test/models/citation.rb +6 -0
  271. data/test/models/club.rb +13 -0
  272. data/test/models/column_name.rb +3 -0
  273. data/test/models/comment.rb +29 -0
  274. data/test/models/company.rb +171 -0
  275. data/test/models/company_in_module.rb +61 -0
  276. data/test/models/computer.rb +3 -0
  277. data/test/models/contact.rb +16 -0
  278. data/test/models/contract.rb +5 -0
  279. data/test/models/course.rb +3 -0
  280. data/test/models/customer.rb +73 -0
  281. data/test/models/default.rb +2 -0
  282. data/test/models/developer.rb +101 -0
  283. data/test/models/edge.rb +5 -0
  284. data/test/models/entrant.rb +3 -0
  285. data/test/models/essay.rb +3 -0
  286. data/test/models/event.rb +3 -0
  287. data/test/models/guid.rb +2 -0
  288. data/test/models/item.rb +7 -0
  289. data/test/models/job.rb +5 -0
  290. data/test/models/joke.rb +3 -0
  291. data/test/models/keyboard.rb +3 -0
  292. data/test/models/legacy_thing.rb +3 -0
  293. data/test/models/matey.rb +4 -0
  294. data/test/models/member.rb +12 -0
  295. data/test/models/member_detail.rb +5 -0
  296. data/test/models/member_type.rb +3 -0
  297. data/test/models/membership.rb +9 -0
  298. data/test/models/minimalistic.rb +2 -0
  299. data/test/models/mixed_case_monkey.rb +3 -0
  300. data/test/models/movie.rb +5 -0
  301. data/test/models/order.rb +4 -0
  302. data/test/models/organization.rb +6 -0
  303. data/test/models/owner.rb +5 -0
  304. data/test/models/parrot.rb +16 -0
  305. data/test/models/person.rb +16 -0
  306. data/test/models/pet.rb +5 -0
  307. data/test/models/pirate.rb +70 -0
  308. data/test/models/post.rb +100 -0
  309. data/test/models/price_estimate.rb +3 -0
  310. data/test/models/project.rb +30 -0
  311. data/test/models/reader.rb +4 -0
  312. data/test/models/reference.rb +4 -0
  313. data/test/models/reply.rb +46 -0
  314. data/test/models/ship.rb +10 -0
  315. data/test/models/ship_part.rb +5 -0
  316. data/test/models/sponsor.rb +4 -0
  317. data/test/models/subject.rb +4 -0
  318. data/test/models/subscriber.rb +8 -0
  319. data/test/models/subscription.rb +4 -0
  320. data/test/models/tag.rb +7 -0
  321. data/test/models/tagging.rb +10 -0
  322. data/test/models/task.rb +3 -0
  323. data/test/models/topic.rb +80 -0
  324. data/test/models/toy.rb +6 -0
  325. data/test/models/treasure.rb +8 -0
  326. data/test/models/vertex.rb +9 -0
  327. data/test/models/warehouse_thing.rb +5 -0
  328. data/test/schema/mysql_specific_schema.rb +24 -0
  329. data/test/schema/postgresql_specific_schema.rb +114 -0
  330. data/test/schema/schema.rb +493 -0
  331. data/test/schema/schema2.rb +6 -0
  332. data/test/schema/sqlite_specific_schema.rb +25 -0
  333. metadata +420 -0
@@ -0,0 +1,996 @@
1
+ require 'erb'
2
+ require 'yaml'
3
+ require 'csv'
4
+ require 'zlib'
5
+ require 'active_support/dependencies'
6
+ require 'active_support/test_case'
7
+
8
+ if RUBY_VERSION < '1.9'
9
+ module YAML #:nodoc:
10
+ class Omap #:nodoc:
11
+ def keys; map { |k, v| k } end
12
+ def values; map { |k, v| v } end
13
+ end
14
+ end
15
+ end
16
+
17
+ if defined? ActiveRecord
18
+ class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
19
+ end
20
+ else
21
+ class FixtureClassNotFound < StandardError #:nodoc:
22
+ end
23
+ end
24
+
25
+ # Fixtures are a way of organizing data that you want to test against; in short, sample data.
26
+ #
27
+ # = Fixture formats
28
+ #
29
+ # Fixtures come in 3 flavors:
30
+ #
31
+ # 1. YAML fixtures
32
+ # 2. CSV fixtures
33
+ # 3. Single-file fixtures
34
+ #
35
+ # == YAML fixtures
36
+ #
37
+ # This type of fixture is in YAML format and the preferred default. YAML is a file format which describes data structures
38
+ # in a non-verbose, human-readable format. It ships with Ruby 1.8.1+.
39
+ #
40
+ # Unlike single-file fixtures, YAML fixtures are stored in a single file per model, which are placed in the directory appointed
41
+ # by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically configured for Rails, so you can just
42
+ # put your files in <tt><your-rails-app>/test/fixtures/</tt>). The fixture file ends with the <tt>.yml</tt> file extension (Rails example:
43
+ # <tt><your-rails-app>/test/fixtures/web_sites.yml</tt>). The format of a YAML fixture file looks like this:
44
+ #
45
+ # rubyonrails:
46
+ # id: 1
47
+ # name: Ruby on Rails
48
+ # url: http://www.rubyonrails.org
49
+ #
50
+ # google:
51
+ # id: 2
52
+ # name: Google
53
+ # url: http://www.google.com
54
+ #
55
+ # This YAML fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and is followed by an
56
+ # indented list of key/value pairs in the "key: value" format. Records are separated by a blank line for your viewing
57
+ # pleasure.
58
+ #
59
+ # Note that YAML fixtures are unordered. If you want ordered fixtures, use the omap YAML type. See http://yaml.org/type/omap.html
60
+ # for the specification. You will need ordered fixtures when you have foreign key constraints on keys in the same table.
61
+ # This is commonly needed for tree structures. Example:
62
+ #
63
+ # --- !omap
64
+ # - parent:
65
+ # id: 1
66
+ # parent_id: NULL
67
+ # title: Parent
68
+ # - child:
69
+ # id: 2
70
+ # parent_id: 1
71
+ # title: Child
72
+ #
73
+ # == CSV fixtures
74
+ #
75
+ # Fixtures can also be kept in the Comma Separated Value (CSV) format. Akin to YAML fixtures, CSV fixtures are stored
76
+ # in a single file, but instead end with the <tt>.csv</tt> file extension
77
+ # (Rails example: <tt><your-rails-app>/test/fixtures/web_sites.csv</tt>).
78
+ #
79
+ # The format of this type of fixture file is much more compact than the others, but also a little harder to read by us
80
+ # humans. The first line of the CSV file is a comma-separated list of field names. The rest of the file is then comprised
81
+ # of the actual data (1 per line). Here's an example:
82
+ #
83
+ # id, name, url
84
+ # 1, Ruby On Rails, http://www.rubyonrails.org
85
+ # 2, Google, http://www.google.com
86
+ #
87
+ # Should you have a piece of data with a comma character in it, you can place double quotes around that value. If you
88
+ # need to use a double quote character, you must escape it with another double quote.
89
+ #
90
+ # Another unique attribute of the CSV fixture is that it has *no* fixture name like the other two formats. Instead, the
91
+ # fixture names are automatically generated by deriving the class name of the fixture file and adding an incrementing
92
+ # number to the end. In our example, the 1st fixture would be called "web_site_1" and the 2nd one would be called
93
+ # "web_site_2".
94
+ #
95
+ # Most databases and spreadsheets support exporting to CSV format, so this is a great format for you to choose if you
96
+ # have existing data somewhere already.
97
+ #
98
+ # == Single-file fixtures
99
+ #
100
+ # This type of fixture was the original format for Active Record that has since been deprecated in favor of the YAML and CSV formats.
101
+ # Fixtures for this format are created by placing text files in a sub-directory (with the name of the model) to the directory
102
+ # appointed by <tt>ActiveSupport::TestCase.fixture_path=(path)</tt> (this is automatically configured for Rails, so you can just
103
+ # put your files in <tt><your-rails-app>/test/fixtures/<your-model-name>/</tt> --
104
+ # like <tt><your-rails-app>/test/fixtures/web_sites/</tt> for the WebSite model).
105
+ #
106
+ # Each text file placed in this directory represents a "record". Usually these types of fixtures are named without
107
+ # extensions, but if you are on a Windows machine, you might consider adding <tt>.txt</tt> as the extension. Here's what the
108
+ # above example might look like:
109
+ #
110
+ # web_sites/google
111
+ # web_sites/yahoo.txt
112
+ # web_sites/ruby-on-rails
113
+ #
114
+ # The file format of a standard fixture is simple. Each line is a property (or column in db speak) and has the syntax
115
+ # of "name => value". Here's an example of the ruby-on-rails fixture above:
116
+ #
117
+ # id => 1
118
+ # name => Ruby on Rails
119
+ # url => http://www.rubyonrails.org
120
+ #
121
+ # = Using fixtures in testcases
122
+ #
123
+ # Since fixtures are a testing construct, we use them in our unit and functional tests. There are two ways to use the
124
+ # fixtures, but first let's take a look at a sample unit test:
125
+ #
126
+ # require 'test_helper'
127
+ #
128
+ # class WebSiteTest < ActiveSupport::TestCase
129
+ # test "web_site_count" do
130
+ # assert_equal 2, WebSite.count
131
+ # end
132
+ # end
133
+ #
134
+ # By default, the <tt>test_helper module</tt> will load all of your fixtures into your test database, so this test will succeed.
135
+ # The testing environment will automatically load the all fixtures into the database before each test.
136
+ # To ensure consistent data, the environment deletes the fixtures before running the load.
137
+ #
138
+ # In addition to being available in the database, the fixture's data may also be accessed by
139
+ # using a special dynamic method, which has the same name as the model, and accepts the
140
+ # name of the fixture to instantiate:
141
+ #
142
+ # test "find" do
143
+ # assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
144
+ # end
145
+ #
146
+ # Alternatively, you may enable auto-instantiation of the fixture data. For instance, take the following tests:
147
+ #
148
+ # test "find_alt_method_1" do
149
+ # assert_equal "Ruby on Rails", @web_sites['rubyonrails']['name']
150
+ # end
151
+ #
152
+ # test "find_alt_method_2" do
153
+ # assert_equal "Ruby on Rails", @rubyonrails.news
154
+ # end
155
+ #
156
+ # In order to use these methods to access fixtured data within your testcases, you must specify one of the
157
+ # following in your <tt>ActiveSupport::TestCase</tt>-derived class:
158
+ #
159
+ # - to fully enable instantiated fixtures (enable alternate methods #1 and #2 above)
160
+ # self.use_instantiated_fixtures = true
161
+ #
162
+ # - create only the hash for the fixtures, do not 'find' each instance (enable alternate method #1 only)
163
+ # self.use_instantiated_fixtures = :no_instances
164
+ #
165
+ # Using either of these alternate methods incurs a performance hit, as the fixtured data must be fully
166
+ # traversed in the database to create the fixture hash and/or instance variables. This is expensive for
167
+ # large sets of fixtured data.
168
+ #
169
+ # = Dynamic fixtures with ERb
170
+ #
171
+ # Some times you don't care about the content of the fixtures as much as you care about the volume. In these cases, you can
172
+ # mix ERb in with your YAML or CSV fixtures to create a bunch of fixtures for load testing, like:
173
+ #
174
+ # <% for i in 1..1000 %>
175
+ # fix_<%= i %>:
176
+ # id: <%= i %>
177
+ # name: guy_<%= 1 %>
178
+ # <% end %>
179
+ #
180
+ # This will create 1000 very simple YAML fixtures.
181
+ #
182
+ # Using ERb, you can also inject dynamic values into your fixtures with inserts like <tt><%= Date.today.strftime("%Y-%m-%d") %></tt>.
183
+ # This is however a feature to be used with some caution. The point of fixtures are that they're stable units of predictable
184
+ # sample data. If you feel that you need to inject dynamic values, then perhaps you should reexamine whether your application
185
+ # is properly testable. Hence, dynamic values in fixtures are to be considered a code smell.
186
+ #
187
+ # = Transactional fixtures
188
+ #
189
+ # TestCases can use begin+rollback to isolate their changes to the database instead of having to delete+insert for every test case.
190
+ #
191
+ # class FooTest < ActiveSupport::TestCase
192
+ # self.use_transactional_fixtures = true
193
+ #
194
+ # test "godzilla" do
195
+ # assert !Foo.find(:all).empty?
196
+ # Foo.destroy_all
197
+ # assert Foo.find(:all).empty?
198
+ # end
199
+ #
200
+ # test "godzilla aftermath" do
201
+ # assert !Foo.find(:all).empty?
202
+ # end
203
+ # end
204
+ #
205
+ # If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures,
206
+ # then you may omit all fixtures declarations in your test cases since all the data's already there and every case rolls back its changes.
207
+ #
208
+ # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide
209
+ # access to fixture data for every table that has been loaded through fixtures (depending on the value of +use_instantiated_fixtures+)
210
+ #
211
+ # When *not* to use transactional fixtures:
212
+ #
213
+ # 1. You're testing whether a transaction works correctly. Nested transactions don't commit until all parent transactions commit,
214
+ # particularly, the fixtures transaction which is begun in setup and rolled back in teardown. Thus, you won't be able to verify
215
+ # the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
216
+ # 2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
217
+ # Use InnoDB, MaxDB, or NDB instead.
218
+ #
219
+ # = Advanced YAML Fixtures
220
+ #
221
+ # YAML fixtures that don't specify an ID get some extra features:
222
+ #
223
+ # * Stable, autogenerated IDs
224
+ # * Label references for associations (belongs_to, has_one, has_many)
225
+ # * HABTM associations as inline lists
226
+ # * Autofilled timestamp columns
227
+ # * Fixture label interpolation
228
+ # * Support for YAML defaults
229
+ #
230
+ # == Stable, autogenerated IDs
231
+ #
232
+ # Here, have a monkey fixture:
233
+ #
234
+ # george:
235
+ # id: 1
236
+ # name: George the Monkey
237
+ #
238
+ # reginald:
239
+ # id: 2
240
+ # name: Reginald the Pirate
241
+ #
242
+ # Each of these fixtures has two unique identifiers: one for the database
243
+ # and one for the humans. Why don't we generate the primary key instead?
244
+ # Hashing each fixture's label yields a consistent ID:
245
+ #
246
+ # george: # generated id: 503576764
247
+ # name: George the Monkey
248
+ #
249
+ # reginald: # generated id: 324201669
250
+ # name: Reginald the Pirate
251
+ #
252
+ # Active Record looks at the fixture's model class, discovers the correct
253
+ # primary key, and generates it right before inserting the fixture
254
+ # into the database.
255
+ #
256
+ # The generated ID for a given label is constant, so we can discover
257
+ # any fixture's ID without loading anything, as long as we know the label.
258
+ #
259
+ # == Label references for associations (belongs_to, has_one, has_many)
260
+ #
261
+ # Specifying foreign keys in fixtures can be very fragile, not to
262
+ # mention difficult to read. Since Active Record can figure out the ID of
263
+ # any fixture from its label, you can specify FK's by label instead of ID.
264
+ #
265
+ # === belongs_to
266
+ #
267
+ # Let's break out some more monkeys and pirates.
268
+ #
269
+ # ### in pirates.yml
270
+ #
271
+ # reginald:
272
+ # id: 1
273
+ # name: Reginald the Pirate
274
+ # monkey_id: 1
275
+ #
276
+ # ### in monkeys.yml
277
+ #
278
+ # george:
279
+ # id: 1
280
+ # name: George the Monkey
281
+ # pirate_id: 1
282
+ #
283
+ # Add a few more monkeys and pirates and break this into multiple files,
284
+ # and it gets pretty hard to keep track of what's going on. Let's
285
+ # use labels instead of IDs:
286
+ #
287
+ # ### in pirates.yml
288
+ #
289
+ # reginald:
290
+ # name: Reginald the Pirate
291
+ # monkey: george
292
+ #
293
+ # ### in monkeys.yml
294
+ #
295
+ # george:
296
+ # name: George the Monkey
297
+ # pirate: reginald
298
+ #
299
+ # Pow! All is made clear. Active Record reflects on the fixture's model class,
300
+ # finds all the +belongs_to+ associations, and allows you to specify
301
+ # a target *label* for the *association* (monkey: george) rather than
302
+ # a target *id* for the *FK* (<tt>monkey_id: 1</tt>).
303
+ #
304
+ # ==== Polymorphic belongs_to
305
+ #
306
+ # Supporting polymorphic relationships is a little bit more complicated, since
307
+ # Active Record needs to know what type your association is pointing at. Something
308
+ # like this should look familiar:
309
+ #
310
+ # ### in fruit.rb
311
+ #
312
+ # belongs_to :eater, :polymorphic => true
313
+ #
314
+ # ### in fruits.yml
315
+ #
316
+ # apple:
317
+ # id: 1
318
+ # name: apple
319
+ # eater_id: 1
320
+ # eater_type: Monkey
321
+ #
322
+ # Can we do better? You bet!
323
+ #
324
+ # apple:
325
+ # eater: george (Monkey)
326
+ #
327
+ # Just provide the polymorphic target type and Active Record will take care of the rest.
328
+ #
329
+ # === has_and_belongs_to_many
330
+ #
331
+ # Time to give our monkey some fruit.
332
+ #
333
+ # ### in monkeys.yml
334
+ #
335
+ # george:
336
+ # id: 1
337
+ # name: George the Monkey
338
+ # pirate_id: 1
339
+ #
340
+ # ### in fruits.yml
341
+ #
342
+ # apple:
343
+ # id: 1
344
+ # name: apple
345
+ #
346
+ # orange:
347
+ # id: 2
348
+ # name: orange
349
+ #
350
+ # grape:
351
+ # id: 3
352
+ # name: grape
353
+ #
354
+ # ### in fruits_monkeys.yml
355
+ #
356
+ # apple_george:
357
+ # fruit_id: 1
358
+ # monkey_id: 1
359
+ #
360
+ # orange_george:
361
+ # fruit_id: 2
362
+ # monkey_id: 1
363
+ #
364
+ # grape_george:
365
+ # fruit_id: 3
366
+ # monkey_id: 1
367
+ #
368
+ # Let's make the HABTM fixture go away.
369
+ #
370
+ # ### in monkeys.yml
371
+ #
372
+ # george:
373
+ # name: George the Monkey
374
+ # pirate: reginald
375
+ # fruits: apple, orange, grape
376
+ #
377
+ # ### in fruits.yml
378
+ #
379
+ # apple:
380
+ # name: apple
381
+ #
382
+ # orange:
383
+ # name: orange
384
+ #
385
+ # grape:
386
+ # name: grape
387
+ #
388
+ # Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
389
+ # on George's fixture, but we could've just as easily specified a list
390
+ # of monkeys on each fruit. As with +belongs_to+, Active Record reflects on
391
+ # the fixture's model class and discovers the +has_and_belongs_to_many+
392
+ # associations.
393
+ #
394
+ # == Autofilled timestamp columns
395
+ #
396
+ # If your table/model specifies any of Active Record's
397
+ # standard timestamp columns (+created_at+, +created_on+, +updated_at+, +updated_on+),
398
+ # they will automatically be set to <tt>Time.now</tt>.
399
+ #
400
+ # If you've set specific values, they'll be left alone.
401
+ #
402
+ # == Fixture label interpolation
403
+ #
404
+ # The label of the current fixture is always available as a column value:
405
+ #
406
+ # geeksomnia:
407
+ # name: Geeksomnia's Account
408
+ # subdomain: $LABEL
409
+ #
410
+ # Also, sometimes (like when porting older join table fixtures) you'll need
411
+ # to be able to get ahold of the identifier for a given label. ERB
412
+ # to the rescue:
413
+ #
414
+ # george_reginald:
415
+ # monkey_id: <%= Fixtures.identify(:reginald) %>
416
+ # pirate_id: <%= Fixtures.identify(:george) %>
417
+ #
418
+ # == Support for YAML defaults
419
+ #
420
+ # You probably already know how to use YAML to set and reuse defaults in
421
+ # your <tt>database.yml</tt> file. You can use the same technique in your fixtures:
422
+ #
423
+ # DEFAULTS: &DEFAULTS
424
+ # created_on: <%= 3.weeks.ago.to_s(:db) %>
425
+ #
426
+ # first:
427
+ # name: Smurf
428
+ # <<: *DEFAULTS
429
+ #
430
+ # second:
431
+ # name: Fraggle
432
+ # <<: *DEFAULTS
433
+ #
434
+ # Any fixture labeled "DEFAULTS" is safely ignored.
435
+
436
+ class Fixtures < (RUBY_VERSION < '1.9' ? YAML::Omap : Hash)
437
+ MAX_ID = 2 ** 30 - 1
438
+ DEFAULT_FILTER_RE = /\.ya?ml$/
439
+
440
+ @@all_cached_fixtures = {}
441
+
442
+ def self.reset_cache(connection = nil)
443
+ connection ||= ActiveRecord::Base.connection
444
+ @@all_cached_fixtures[connection.object_id] = {}
445
+ end
446
+
447
+ def self.cache_for_connection(connection)
448
+ @@all_cached_fixtures[connection.object_id] ||= {}
449
+ @@all_cached_fixtures[connection.object_id]
450
+ end
451
+
452
+ def self.fixture_is_cached?(connection, table_name)
453
+ cache_for_connection(connection)[table_name]
454
+ end
455
+
456
+ def self.cached_fixtures(connection, keys_to_fetch = nil)
457
+ if keys_to_fetch
458
+ fixtures = cache_for_connection(connection).values_at(*keys_to_fetch)
459
+ else
460
+ fixtures = cache_for_connection(connection).values
461
+ end
462
+ fixtures.size > 1 ? fixtures : fixtures.first
463
+ end
464
+
465
+ def self.cache_fixtures(connection, fixtures_map)
466
+ cache_for_connection(connection).update(fixtures_map)
467
+ end
468
+
469
+ def self.instantiate_fixtures(object, table_name, fixtures, load_instances = true)
470
+ object.instance_variable_set "@#{table_name.to_s.gsub('.','_')}", fixtures
471
+ if load_instances
472
+ ActiveRecord::Base.silence do
473
+ fixtures.each do |name, fixture|
474
+ begin
475
+ object.instance_variable_set "@#{name}", fixture.find
476
+ rescue FixtureClassNotFound
477
+ nil
478
+ end
479
+ end
480
+ end
481
+ end
482
+ end
483
+
484
+ def self.instantiate_all_loaded_fixtures(object, load_instances = true)
485
+ all_loaded_fixtures.each do |table_name, fixtures|
486
+ Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
487
+ end
488
+ end
489
+
490
+ cattr_accessor :all_loaded_fixtures
491
+ self.all_loaded_fixtures = {}
492
+
493
+ def self.create_fixtures(fixtures_directory, table_names, class_names = {})
494
+ table_names = [table_names].flatten.map { |n| n.to_s }
495
+ connection = block_given? ? yield : ActiveRecord::Base.connection
496
+
497
+ table_names_to_fetch = table_names.reject { |table_name| fixture_is_cached?(connection, table_name) }
498
+
499
+ unless table_names_to_fetch.empty?
500
+ ActiveRecord::Base.silence do
501
+ connection.disable_referential_integrity do
502
+ fixtures_map = {}
503
+
504
+ fixtures = table_names_to_fetch.map do |table_name|
505
+ fixtures_map[table_name] = Fixtures.new(connection, File.split(table_name.to_s).last, class_names[table_name.to_sym], File.join(fixtures_directory, table_name.to_s))
506
+ end
507
+
508
+ all_loaded_fixtures.update(fixtures_map)
509
+
510
+ connection.transaction(:requires_new => true) do
511
+ fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures }
512
+ fixtures.each { |fixture| fixture.insert_fixtures }
513
+
514
+ # Cap primary key sequences to max(pk).
515
+ if connection.respond_to?(:reset_pk_sequence!)
516
+ table_names.each do |table_name|
517
+ connection.reset_pk_sequence!(table_name)
518
+ end
519
+ end
520
+ end
521
+
522
+ cache_fixtures(connection, fixtures_map)
523
+ end
524
+ end
525
+ end
526
+ cached_fixtures(connection, table_names)
527
+ end
528
+
529
+ # Returns a consistent, platform-independent identifier for +label+.
530
+ # Identifiers are positive integers less than 2^32.
531
+ def self.identify(label)
532
+ Zlib.crc32(label.to_s) % MAX_ID
533
+ end
534
+
535
+ attr_reader :table_name, :name
536
+
537
+ def initialize(connection, table_name, class_name, fixture_path, file_filter = DEFAULT_FILTER_RE)
538
+ @connection, @table_name, @fixture_path, @file_filter = connection, table_name, fixture_path, file_filter
539
+ @name = table_name # preserve fixture base name
540
+ @class_name = class_name ||
541
+ (ActiveRecord::Base.pluralize_table_names ? @table_name.singularize.camelize : @table_name.camelize)
542
+ @table_name = "#{ActiveRecord::Base.table_name_prefix}#{@table_name}#{ActiveRecord::Base.table_name_suffix}"
543
+ @table_name = class_name.table_name if class_name.respond_to?(:table_name)
544
+ @connection = class_name.connection if class_name.respond_to?(:connection)
545
+ read_fixture_files
546
+ end
547
+
548
+ def delete_existing_fixtures
549
+ @connection.delete "DELETE FROM #{@connection.quote_table_name(table_name)}", 'Fixture Delete'
550
+ end
551
+
552
+ def insert_fixtures
553
+ now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
554
+ now = now.to_s(:db)
555
+
556
+ # allow a standard key to be used for doing defaults in YAML
557
+ if is_a?(Hash)
558
+ delete('DEFAULTS')
559
+ else
560
+ delete(assoc('DEFAULTS'))
561
+ end
562
+
563
+ # track any join tables we need to insert later
564
+ habtm_fixtures = Hash.new do |h, habtm|
565
+ h[habtm] = HabtmFixtures.new(@connection, habtm.options[:join_table], nil, nil)
566
+ end
567
+
568
+ each do |label, fixture|
569
+ row = fixture.to_hash
570
+
571
+ if model_class && model_class < ActiveRecord::Base
572
+ # fill in timestamp columns if they aren't specified and the model is set to record_timestamps
573
+ if model_class.record_timestamps
574
+ timestamp_column_names.each do |name|
575
+ row[name] = now unless row.key?(name)
576
+ end
577
+ end
578
+
579
+ # interpolate the fixture label
580
+ row.each do |key, value|
581
+ row[key] = label if value == "$LABEL"
582
+ end
583
+
584
+ # generate a primary key if necessary
585
+ if has_primary_key_column? && !row.include?(primary_key_name)
586
+ row[primary_key_name] = Fixtures.identify(label)
587
+ end
588
+
589
+ # If STI is used, find the correct subclass for association reflection
590
+ reflection_class =
591
+ if row.include?(inheritance_column_name)
592
+ row[inheritance_column_name].constantize rescue model_class
593
+ else
594
+ model_class
595
+ end
596
+
597
+ reflection_class.reflect_on_all_associations.each do |association|
598
+ case association.macro
599
+ when :belongs_to
600
+ # Do not replace association name with association foreign key if they are named the same
601
+ fk_name = (association.options[:foreign_key] || "#{association.name}_id").to_s
602
+
603
+ if association.name.to_s != fk_name && value = row.delete(association.name.to_s)
604
+ if association.options[:polymorphic]
605
+ if value.sub!(/\s*\(([^\)]*)\)\s*$/, "")
606
+ target_type = $1
607
+ target_type_name = (association.options[:foreign_type] || "#{association.name}_type").to_s
608
+
609
+ # support polymorphic belongs_to as "label (Type)"
610
+ row[target_type_name] = target_type
611
+ end
612
+ end
613
+
614
+ row[fk_name] = Fixtures.identify(value)
615
+ end
616
+ when :has_and_belongs_to_many
617
+ if (targets = row.delete(association.name.to_s))
618
+ targets = targets.is_a?(Array) ? targets : targets.split(/\s*,\s*/)
619
+ join_fixtures = habtm_fixtures[association]
620
+
621
+ targets.each do |target|
622
+ join_fixtures["#{label}_#{target}"] = Fixture.new(
623
+ { association.primary_key_name => row[primary_key_name],
624
+ association.association_foreign_key => Fixtures.identify(target) },
625
+ nil, @connection)
626
+ end
627
+ end
628
+ end
629
+ end
630
+ end
631
+
632
+ @connection.insert_fixture(fixture, @table_name)
633
+ end
634
+
635
+ # insert any HABTM join tables we discovered
636
+ habtm_fixtures.values.each do |fixture|
637
+ fixture.delete_existing_fixtures
638
+ fixture.insert_fixtures
639
+ end
640
+ end
641
+
642
+ private
643
+ class HabtmFixtures < ::Fixtures #:nodoc:
644
+ def read_fixture_files; end
645
+ end
646
+
647
+ def model_class
648
+ unless defined?(@model_class)
649
+ @model_class =
650
+ if @class_name.nil? || @class_name.is_a?(Class)
651
+ @class_name
652
+ else
653
+ @class_name.constantize rescue nil
654
+ end
655
+ end
656
+
657
+ @model_class
658
+ end
659
+
660
+ def primary_key_name
661
+ @primary_key_name ||= model_class && model_class.primary_key
662
+ end
663
+
664
+ def has_primary_key_column?
665
+ @has_primary_key_column ||= model_class && primary_key_name &&
666
+ model_class.columns.find { |c| c.name == primary_key_name }
667
+ end
668
+
669
+ def timestamp_column_names
670
+ @timestamp_column_names ||= %w(created_at created_on updated_at updated_on).select do |name|
671
+ column_names.include?(name)
672
+ end
673
+ end
674
+
675
+ def inheritance_column_name
676
+ @inheritance_column_name ||= model_class && model_class.inheritance_column
677
+ end
678
+
679
+ def column_names
680
+ @column_names ||= @connection.columns(@table_name).collect(&:name)
681
+ end
682
+
683
+ def read_fixture_files
684
+ if File.file?(yaml_file_path)
685
+ read_yaml_fixture_files
686
+ elsif File.file?(csv_file_path)
687
+ read_csv_fixture_files
688
+ end
689
+ end
690
+
691
+ def read_yaml_fixture_files
692
+ yaml_string = ""
693
+ Dir["#{@fixture_path}/**/*.yml"].select { |f| test(?f, f) }.each do |subfixture_path|
694
+ yaml_string << IO.read(subfixture_path)
695
+ end
696
+ yaml_string << IO.read(yaml_file_path)
697
+
698
+ if yaml = parse_yaml_string(yaml_string)
699
+ # If the file is an ordered map, extract its children.
700
+ yaml_value =
701
+ if yaml.respond_to?(:type_id) && yaml.respond_to?(:value)
702
+ yaml.value
703
+ else
704
+ [yaml]
705
+ end
706
+
707
+ yaml_value.each do |fixture|
708
+ raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{fixture}" unless fixture.respond_to?(:each)
709
+ fixture.each do |name, data|
710
+ unless data
711
+ raise Fixture::FormatError, "Bad data for #{@class_name} fixture named #{name} (nil)"
712
+ end
713
+
714
+ self[name] = Fixture.new(data, model_class, @connection)
715
+ end
716
+ end
717
+ end
718
+ end
719
+
720
+ def read_csv_fixture_files
721
+ reader = CSV.parse(erb_render(IO.read(csv_file_path)))
722
+ header = reader.shift
723
+ i = 0
724
+ reader.each do |row|
725
+ data = {}
726
+ row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
727
+ self["#{@class_name.to_s.underscore}_#{i+=1}"] = Fixture.new(data, model_class, @connection)
728
+ end
729
+ end
730
+
731
+ def yaml_file_path
732
+ "#{@fixture_path}.yml"
733
+ end
734
+
735
+ def csv_file_path
736
+ @fixture_path + ".csv"
737
+ end
738
+
739
+ def yaml_fixtures_key(path)
740
+ File.basename(@fixture_path).split(".").first
741
+ end
742
+
743
+ def parse_yaml_string(fixture_content)
744
+ YAML::load(erb_render(fixture_content))
745
+ rescue => error
746
+ raise Fixture::FormatError, "a YAML error occurred parsing #{yaml_file_path}. Please note that YAML must be consistently indented using spaces. Tabs are not allowed. Please have a look at http://www.yaml.org/faq.html\nThe exact error was:\n #{error.class}: #{error}"
747
+ end
748
+
749
+ def erb_render(fixture_content)
750
+ ERB.new(fixture_content).result
751
+ end
752
+ end
753
+
754
+ class Fixture #:nodoc:
755
+ include Enumerable
756
+
757
+ class FixtureError < StandardError #:nodoc:
758
+ end
759
+
760
+ class FormatError < FixtureError #:nodoc:
761
+ end
762
+
763
+ attr_reader :model_class
764
+
765
+ def initialize(fixture, model_class, connection = ActiveRecord::Base.connection)
766
+ @connection = connection
767
+ @fixture = fixture
768
+ @model_class = model_class.is_a?(Class) ? model_class : model_class.constantize rescue nil
769
+ end
770
+
771
+ def class_name
772
+ @model_class.name if @model_class
773
+ end
774
+
775
+ def each
776
+ @fixture.each { |item| yield item }
777
+ end
778
+
779
+ def [](key)
780
+ @fixture[key]
781
+ end
782
+
783
+ def to_hash
784
+ @fixture
785
+ end
786
+
787
+ def key_list
788
+ columns = @fixture.keys.collect{ |column_name| @connection.quote_column_name(column_name) }
789
+ columns.join(", ")
790
+ end
791
+
792
+ def value_list
793
+ list = @fixture.inject([]) do |fixtures, (key, value)|
794
+ col = model_class.columns_hash[key] if model_class.respond_to?(:ancestors) && model_class.ancestors.include?(ActiveRecord::Base)
795
+ fixtures << @connection.quote(value, col).gsub('[^\]\\n', "\n").gsub('[^\]\\r', "\r")
796
+ end
797
+ list * ', '
798
+ end
799
+
800
+ def find
801
+ if model_class
802
+ model_class.find(self[model_class.primary_key])
803
+ else
804
+ raise FixtureClassNotFound, "No class attached to find."
805
+ end
806
+ end
807
+ end
808
+
809
+ module ActiveRecord
810
+ module TestFixtures
811
+ def self.included(base)
812
+ base.class_eval do
813
+ setup :setup_fixtures
814
+ teardown :teardown_fixtures
815
+
816
+ superclass_delegating_accessor :fixture_path
817
+ superclass_delegating_accessor :fixture_table_names
818
+ superclass_delegating_accessor :fixture_class_names
819
+ superclass_delegating_accessor :use_transactional_fixtures
820
+ superclass_delegating_accessor :use_instantiated_fixtures # true, false, or :no_instances
821
+ superclass_delegating_accessor :pre_loaded_fixtures
822
+
823
+ self.fixture_table_names = []
824
+ self.use_transactional_fixtures = false
825
+ self.use_instantiated_fixtures = true
826
+ self.pre_loaded_fixtures = false
827
+
828
+ self.fixture_class_names = {}
829
+ end
830
+
831
+ base.extend ClassMethods
832
+ end
833
+
834
+ module ClassMethods
835
+ def set_fixture_class(class_names = {})
836
+ self.fixture_class_names = self.fixture_class_names.merge(class_names)
837
+ end
838
+
839
+ def fixtures(*table_names)
840
+ if table_names.first == :all
841
+ table_names = Dir["#{fixture_path}/*.yml"] + Dir["#{fixture_path}/*.csv"]
842
+ table_names.map! { |f| File.basename(f).split('.')[0..-2].join('.') }
843
+ else
844
+ table_names = table_names.flatten.map { |n| n.to_s }
845
+ end
846
+
847
+ self.fixture_table_names |= table_names
848
+ require_fixture_classes(table_names)
849
+ setup_fixture_accessors(table_names)
850
+ end
851
+
852
+ def try_to_load_dependency(file_name)
853
+ require_dependency file_name
854
+ rescue LoadError => e
855
+ # Let's hope the developer has included it himself
856
+
857
+ # Let's warn in case this is a subdependency, otherwise
858
+ # subdependency error messages are totally cryptic
859
+ if ActiveRecord::Base.logger
860
+ ActiveRecord::Base.logger.warn("Unable to load #{file_name}, underlying cause #{e.message} \n\n #{e.backtrace.join("\n")}")
861
+ end
862
+ end
863
+
864
+ def require_fixture_classes(table_names = nil)
865
+ (table_names || fixture_table_names).each do |table_name|
866
+ file_name = table_name.to_s
867
+ file_name = file_name.singularize if ActiveRecord::Base.pluralize_table_names
868
+ try_to_load_dependency(file_name)
869
+ end
870
+ end
871
+
872
+ def setup_fixture_accessors(table_names = nil)
873
+ table_names = [table_names] if table_names && !table_names.respond_to?(:each)
874
+ (table_names || fixture_table_names).each do |table_name|
875
+ table_name = table_name.to_s.tr('.', '_')
876
+
877
+ define_method(table_name) do |*fixtures|
878
+ force_reload = fixtures.pop if fixtures.last == true || fixtures.last == :reload
879
+
880
+ @fixture_cache[table_name] ||= {}
881
+
882
+ instances = fixtures.map do |fixture|
883
+ @fixture_cache[table_name].delete(fixture) if force_reload
884
+
885
+ if @loaded_fixtures[table_name][fixture.to_s]
886
+ @fixture_cache[table_name][fixture] ||= @loaded_fixtures[table_name][fixture.to_s].find
887
+ else
888
+ raise StandardError, "No fixture with name '#{fixture}' found for table '#{table_name}'"
889
+ end
890
+ end
891
+
892
+ instances.size == 1 ? instances.first : instances
893
+ end
894
+ end
895
+ end
896
+
897
+ def uses_transaction(*methods)
898
+ @uses_transaction = [] unless defined?(@uses_transaction)
899
+ @uses_transaction.concat methods.map(&:to_s)
900
+ end
901
+
902
+ def uses_transaction?(method)
903
+ @uses_transaction = [] unless defined?(@uses_transaction)
904
+ @uses_transaction.include?(method.to_s)
905
+ end
906
+ end
907
+
908
+ def run_in_transaction?
909
+ use_transactional_fixtures &&
910
+ !self.class.uses_transaction?(method_name)
911
+ end
912
+
913
+ def setup_fixtures
914
+ return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
915
+
916
+ if pre_loaded_fixtures && !use_transactional_fixtures
917
+ raise RuntimeError, 'pre_loaded_fixtures requires use_transactional_fixtures'
918
+ end
919
+
920
+ @fixture_cache = {}
921
+ @@already_loaded_fixtures ||= {}
922
+
923
+ # Load fixtures once and begin transaction.
924
+ if run_in_transaction?
925
+ if @@already_loaded_fixtures[self.class]
926
+ @loaded_fixtures = @@already_loaded_fixtures[self.class]
927
+ else
928
+ load_fixtures
929
+ @@already_loaded_fixtures[self.class] = @loaded_fixtures
930
+ end
931
+ ActiveRecord::Base.connection.increment_open_transactions
932
+ ActiveRecord::Base.connection.transaction_joinable = false
933
+ ActiveRecord::Base.connection.begin_db_transaction
934
+ # Load fixtures for every test.
935
+ else
936
+ Fixtures.reset_cache
937
+ @@already_loaded_fixtures[self.class] = nil
938
+ load_fixtures
939
+ end
940
+
941
+ # Instantiate fixtures for every test if requested.
942
+ instantiate_fixtures if use_instantiated_fixtures
943
+ end
944
+
945
+ def teardown_fixtures
946
+ return unless defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank?
947
+
948
+ unless run_in_transaction?
949
+ Fixtures.reset_cache
950
+ end
951
+
952
+ # Rollback changes if a transaction is active.
953
+ if run_in_transaction? && ActiveRecord::Base.connection.open_transactions != 0
954
+ ActiveRecord::Base.connection.rollback_db_transaction
955
+ ActiveRecord::Base.connection.decrement_open_transactions
956
+ end
957
+ ActiveRecord::Base.clear_active_connections!
958
+ end
959
+
960
+ private
961
+ def load_fixtures
962
+ @loaded_fixtures = {}
963
+ fixtures = Fixtures.create_fixtures(fixture_path, fixture_table_names, fixture_class_names)
964
+ unless fixtures.nil?
965
+ if fixtures.instance_of?(Fixtures)
966
+ @loaded_fixtures[fixtures.name] = fixtures
967
+ else
968
+ fixtures.each { |f| @loaded_fixtures[f.name] = f }
969
+ end
970
+ end
971
+ end
972
+
973
+ # for pre_loaded_fixtures, only require the classes once. huge speed improvement
974
+ @@required_fixture_classes = false
975
+
976
+ def instantiate_fixtures
977
+ if pre_loaded_fixtures
978
+ raise RuntimeError, 'Load fixtures before instantiating them.' if Fixtures.all_loaded_fixtures.empty?
979
+ unless @@required_fixture_classes
980
+ self.class.require_fixture_classes Fixtures.all_loaded_fixtures.keys
981
+ @@required_fixture_classes = true
982
+ end
983
+ Fixtures.instantiate_all_loaded_fixtures(self, load_instances?)
984
+ else
985
+ raise RuntimeError, 'Load fixtures before instantiating them.' if @loaded_fixtures.nil?
986
+ @loaded_fixtures.each do |table_name, fixtures|
987
+ Fixtures.instantiate_fixtures(self, table_name, fixtures, load_instances?)
988
+ end
989
+ end
990
+ end
991
+
992
+ def load_instances?
993
+ use_instantiated_fixtures != :no_instances
994
+ end
995
+ end
996
+ end