Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

root/tags/rel_2-0-2/activerecord/lib/active_record/fixtures.rb

Revision 8392, 34.8 kB (checked in by rick, 1 year ago)

Make the Fixtures Test::Unit enhancements more supporting for double-loaded test cases. Closes #10379 [brynary]

  • Property svn:executable set to *
Line 
1 require 'erb'
2 require 'yaml'
3 require 'csv'
4
5 module YAML #:nodoc:
6   class Omap #:nodoc:
7     def keys;   map { |k, v| k } end
8     def values; map { |k, v| v } end
9   end
10 end
11
12 if defined? ActiveRecord
13   class FixtureClassNotFound < ActiveRecord::ActiveRecordError #:nodoc:
14   end
15 else
16   class FixtureClassNotFound < StandardError #:nodoc:
17   end
18 end
19
20 # Fixtures are a way of organizing data that you want to test against; in short, sample data. They come in 3 flavors:
21 #
22 #   1.  YAML fixtures
23 #   2.  CSV fixtures
24 #   3.  Single-file fixtures
25 #
26 # = YAML fixtures
27 #
28 # This type of fixture is in YAML format and the preferred default. YAML is a file format which describes data structures
29 # in a non-verbose, human-readable format. It ships with Ruby 1.8.1+.
30 #
31 # Unlike single-file fixtures, YAML fixtures are stored in a single file per model, which are placed in the directory appointed
32 # by <tt>Test::Unit::TestCase.fixture_path=(path)</tt> (this is automatically configured for Rails, so you can just
33 # put your files in <your-rails-app>/test/fixtures/). The fixture file ends with the .yml file extension (Rails example:
34 # "<your-rails-app>/test/fixtures/web_sites.yml"). The format of a YAML fixture file looks like this:
35 #
36 #   rubyonrails:
37 #     id: 1
38 #     name: Ruby on Rails
39 #     url: http://www.rubyonrails.org
40 #
41 #   google:
42 #     id: 2
43 #     name: Google
44 #     url: http://www.google.com
45 #
46 # This YAML fixture file includes two fixtures.  Each YAML fixture (ie. record) is given a name and is followed by an
47 # indented list of key/value pairs in the "key: value" format.  Records are separated by a blank line for your viewing
48 # pleasure.
49 #
50 # Note that YAML fixtures are unordered. If you want ordered fixtures, use the omap YAML type.  See http://yaml.org/type/omap.html
51 # for the specification.  You will need ordered fixtures when you have foreign key constraints on keys in the same table.
52 # This is commonly needed for tree structures.  Example:
53 #
54 #    --- !omap
55 #    - parent:
56 #        id:         1
57 #        parent_id:  NULL
58 #        title:      Parent
59 #    - child:
60 #        id:         2
61 #        parent_id:  1
62 #        title:      Child
63 #
64 # = CSV fixtures
65 #
66 # Fixtures can also be kept in the Comma Separated Value format. Akin to YAML fixtures, CSV fixtures are stored
67 # in a single file, but instead end with the .csv file extension (Rails example: "<your-rails-app>/test/fixtures/web_sites.csv")
68 #
69 # The format of this type of fixture file is much more compact than the others, but also a little harder to read by us
70 # humans.  The first line of the CSV file is a comma-separated list of field names.  The rest of the file is then comprised
71 # of the actual data (1 per line).  Here's an example:
72 #
73 #   id, name, url
74 #   1, Ruby On Rails, http://www.rubyonrails.org
75 #   2, Google, http://www.google.com
76 #
77 # Should you have a piece of data with a comma character in it, you can place double quotes around that value.  If you
78 # need to use a double quote character, you must escape it with another double quote.
79 #
80 # Another unique attribute of the CSV fixture is that it has *no* fixture name like the other two formats.  Instead, the
81 # fixture names are automatically generated by deriving the class name of the fixture file and adding an incrementing
82 # number to the end.  In our example, the 1st fixture would be called "web_site_1" and the 2nd one would be called
83 # "web_site_2".
84 #
85 # Most databases and spreadsheets support exporting to CSV format, so this is a great format for you to choose if you
86 # have existing data somewhere already.
87 #
88 # = Single-file fixtures
89 #
90 # This type of fixture was the original format for Active Record that has since been deprecated in favor of the YAML and CSV formats.
91 # Fixtures for this format are created by placing text files in a sub-directory (with the name of the model) to the directory
92 # appointed by <tt>Test::Unit::TestCase.fixture_path=(path)</tt> (this is automatically configured for Rails, so you can just
93 # put your files in <your-rails-app>/test/fixtures/<your-model-name>/ -- like <your-rails-app>/test/fixtures/web_sites/ for the WebSite
94 # model).
95 #
96 # Each text file placed in this directory represents a "record".  Usually these types of fixtures are named without
97 # extensions, but if you are on a Windows machine, you might consider adding .txt as the extension.  Here's what the
98 # above example might look like:
99 #
100 #   web_sites/google
101 #   web_sites/yahoo.txt
102 #   web_sites/ruby-on-rails
103 #
104 # The file format of a standard fixture is simple.  Each line is a property (or column in db speak) and has the syntax
105 # of "name => value".  Here's an example of the ruby-on-rails fixture above:
106 #
107 #   id => 1
108 #   name => Ruby on Rails
109 #   url => http://www.rubyonrails.org
110 #
111 # = Using Fixtures
112 #
113 # Since fixtures are a testing construct, we use them in our unit and functional tests.  There are two ways to use the
114 # fixtures, but first let's take a look at a sample unit test:
115 #
116 #   require 'web_site'
117 #
118 #   class WebSiteTest < Test::Unit::TestCase
119 #     def test_web_site_count
120 #       assert_equal 2, WebSite.count
121 #     end
122 #   end
123 #
124 # As it stands, unless we pre-load the web_site table in our database with two records, this test will fail.  Here's the
125 # easiest way to add fixtures to the database:
126 #
127 #   ...
128 #   class WebSiteTest < Test::Unit::TestCase
129 #     fixtures :web_sites # add more by separating the symbols with commas
130 #   ...
131 #
132 # By adding a "fixtures" method to the test case and passing it a list of symbols (only one is shown here though), we trigger
133 # the testing environment to automatically load the appropriate fixtures into the database before each test.
134 # To ensure consistent data, the environment deletes the fixtures before running the load.
135 #
136 # In addition to being available in the database, the fixtures are also loaded into a hash stored in an instance variable
137 # of the test case.  It is named after the symbol... so, in our example, there would be a hash available called
138 # @web_sites.  This is where the "fixture name" comes into play.
139 #
140 # On top of that, each record is automatically "found" (using Model.find(id)) and placed in the instance variable of its name.
141 # So for the YAML fixtures, we'd get @rubyonrails and @google, which could be interrogated using regular Active Record semantics:
142 #
143 #   # test if the object created from the fixture data has the same attributes as the data itself
144 #   def test_find
145 #     assert_equal @web_sites["rubyonrails"]["name"], @rubyonrails.name
146 #   end
147 #
148 # As seen above, the data hash created from the YAML fixtures would have @web_sites["rubyonrails"]["url"] return
149 # "http://www.rubyonrails.org" and @web_sites["google"]["name"] would return "Google". The same fixtures, but loaded
150 # from a CSV fixture file, would be accessible via @web_sites["web_site_1"]["name"] == "Ruby on Rails" and have the individual
151 # fixtures available as instance variables @web_site_1 and @web_site_2.
152 #
153 # If you do not wish to use instantiated fixtures (usually for performance reasons) there are two options.
154 #
155 #   - to completely disable instantiated fixtures:
156 #       self.use_instantiated_fixtures = false
157 #
158 #   - to keep the fixture instance (@web_sites) available, but do not automatically 'find' each instance:
159 #       self.use_instantiated_fixtures = :no_instances
160 #
161 # Even if auto-instantiated fixtures are disabled, you can still access them
162 # by name via special dynamic methods. Each method has the same name as the
163 # model, and accepts the name of the fixture to instantiate:
164 #
165 #   fixtures :web_sites
166 #
167 #   def test_find
168 #     assert_equal "Ruby on Rails", web_sites(:rubyonrails).name
169 #   end
170 #
171 # = Dynamic fixtures with ERb
172 #
173 # 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
174 # mix ERb in with your YAML or CSV fixtures to create a bunch of fixtures for load testing, like:
175 #
176 # <% for i in 1..1000 %>
177 # fix_<%= i %>:
178 #   id: <%= i %>
179 #   name: guy_<%= 1 %>
180 # <% end %>
181 #
182 # This will create 1000 very simple YAML fixtures.
183 #
184 # Using ERb, you can also inject dynamic values into your fixtures with inserts like <%= Date.today.strftime("%Y-%m-%d") %>.
185 # This is however a feature to be used with some caution. The point of fixtures are that they're stable units of predictable
186 # sample data. If you feel that you need to inject dynamic values, then perhaps you should reexamine whether your application
187 # is properly testable. Hence, dynamic values in fixtures are to be considered a code smell.
188 #
189 # = Transactional fixtures
190 #
191 # TestCases can use begin+rollback to isolate their changes to the database instead of having to delete+insert for every test case.
192 # They can also turn off auto-instantiation of fixture data since the feature is costly and often unused.
193 #
194 #   class FooTest < Test::Unit::TestCase
195 #     self.use_transactional_fixtures = true
196 #     self.use_instantiated_fixtures = false
197 #   
198 #     fixtures :foos
199 #   
200 #     def test_godzilla
201 #       assert !Foo.find(:all).empty?
202 #       Foo.destroy_all
203 #       assert Foo.find(:all).empty?
204 #     end
205 #   
206 #     def test_godzilla_aftermath
207 #       assert !Foo.find(:all).empty?
208 #     end
209 #   end
210 #   
211 # If you preload your test database with all fixture data (probably in the Rakefile task) and use transactional fixtures,
212 # 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.
213 #
214 # In order to use instantiated fixtures with preloaded data, set +self.pre_loaded_fixtures+ to true. This will provide
215 # access to fixture data for every table that has been loaded through fixtures (depending on the value of +use_instantiated_fixtures+)
216 #
217 # When *not* to use transactional fixtures:
218 #   1. You're testing whether a transaction works correctly. Nested transactions don't commit until all parent transactions commit,
219 #      particularly, the fixtures transaction which is begun in setup and rolled back in teardown. Thus, you won't be able to verify
220 #      the results of your transaction until Active Record supports nested transactions or savepoints (in progress).
221 #   2. Your database does not support transactions. Every Active Record database supports transactions except MySQL MyISAM.
222 #      Use InnoDB, MaxDB, or NDB instead.
223 #
224 # = Advanced YAML Fixtures
225 #
226 # YAML fixtures that don't specify an ID get some extra features:
227 #
228 # * Stable, autogenerated ID's
229 # * Label references for associations (belongs_to, has_one, has_many)
230 # * HABTM associations as inline lists
231 # * Autofilled timestamp columns
232 # * Fixture label interpolation
233 # * Support for YAML defaults
234 #
235 # == Stable, autogenerated ID's
236 #
237 # Here, have a monkey fixture:
238 #
239 #   george:
240 #     id: 1
241 #     name: George the Monkey
242 #
243 #   reginald:
244 #     id: 2
245 #     name: Reginald the Pirate
246 #
247 # Each of these fixtures has two unique identifiers: one for the database
248 # and one for the humans. Why don't we generate the primary key instead?
249 # Hashing each fixture's label yields a consistent ID:
250 #
251 #   george: # generated id: 503576764
252 #     name: George the Monkey
253 #
254 #   reginald: # generated id: 324201669
255 #     name: Reginald the Pirate
256 #
257 # ActiveRecord looks at the fixture's model class, discovers the correct
258 # primary key, and generates it right before inserting the fixture
259 # into the database.
260 #
261 # The generated ID for a given label is constant, so we can discover
262 # any fixture's ID without loading anything, as long as we know the label.
263 #
264 # == Label references for associations (belongs_to, has_one, has_many)
265 #
266 # Specifying foreign keys in fixtures can be very fragile, not to
267 # mention difficult to read. Since ActiveRecord can figure out the ID of
268 # any fixture from its label, you can specify FK's by label instead of ID.
269 #
270 # === belongs_to
271 #
272 # Let's break out some more monkeys and pirates.
273 #
274 #   ### in pirates.yml
275 #
276 #   reginald:
277 #     id: 1
278 #     name: Reginald the Pirate
279 #     monkey_id: 1
280 #
281 #   ### in monkeys.yml
282 #
283 #   george:
284 #     id: 1
285 #     name: George the Monkey
286 #     pirate_id: 1
287 #
288 # Add a few more monkeys and pirates and break this into multiple files,
289 # and it gets pretty hard to keep track of what's going on. Let's
290 # use labels instead of ID's:
291 #
292 #   ### in pirates.yml
293 #
294 #   reginald:
295 #     name: Reginald the Pirate
296 #     monkey: george
297 #
298 #   ### in monkeys.yml
299 #
300 #   george:
301 #     name: George the Monkey
302 #     pirate: reginald
303 #
304 # Pow! All is made clear. ActiveRecord reflects on the fixture's model class,
305 # finds all the +belongs_to+ associations, and allows you to specify
306 # a target *label* for the *association* (monkey: george) rather than
307 # a target *id* for the *FK* (monkey_id: 1).
308 #
309 # ==== Polymorphic belongs_to
310 #
311 # Supporting polymorphic relationships is a little bit more complicated, since
312 # ActiveRecord needs to know what type your association is pointing at. Something
313 # like this should look familiar:
314 #
315 #   ### in fruit.rb
316 #
317 #   belongs_to :eater, :polymorphic => true
318 #
319 #   ### in fruits.yml
320 #
321 #   apple:
322 #     id: 1
323 #     name: apple
324 #     eater_id: 1
325 #     eater_type: Monkey
326 #
327 # Can we do better? You bet!
328 #
329 #   apple:
330 #     eater: george (Monkey)
331 #
332 # Just provide the polymorphic target type and ActiveRecord will take care of the rest.
333 #
334 # === has_and_belongs_to_many
335 #
336 # Time to give our monkey some fruit.
337 #
338 #   ### in monkeys.yml
339 #
340 #   george:
341 #     id: 1
342 #     name: George the Monkey
343 #     pirate_id: 1
344 #
345 #   ### in fruits.yml
346 #
347 #   apple:
348 #     id: 1
349 #     name: apple
350 #
351 #   orange:
352 #     id: 2
353 #     name: orange
354 #
355 #   grape:
356 #     id: 3
357 #     name: grape
358 #
359 #   ### in fruits_monkeys.yml
360 #
361 #   apple_george:
362 #     fruit_id: 1
363 #     monkey_id: 1
364 #
365 #   orange_george:
366 #     fruit_id: 2
367 #     monkey_id: 1
368 #
369 #   grape_george:
370 #     fruit_id: 3
371 #     monkey_id: 1
372 #
373 # Let's make the HABTM fixture go away.
374 #
375 #   ### in monkeys.yml
376 #
377 #   george:
378 #     name: George the Monkey
379 #     pirate: reginald
380 #     fruits: apple, orange, grape
381 #
382 #   ### in fruits.yml
383 #
384 #   apple:
385 #     name: apple
386 #
387 #   orange:
388 #     name: orange
389 #
390 #   grape:
391 #     name: grape
392 #
393 # Zap! No more fruits_monkeys.yml file. We've specified the list of fruits
394 # on George's fixture, but we could've just as easily specified a list
395 # of monkeys on each fruit. As with +belongs_to+, ActiveRecord reflects on
396 # the fixture's model class and discovers the +has_and_belongs_to_many+
397 # associations.
398 #
399 # == Autofilled timestamp columns
400 #
401 # If your table/model specifies any of ActiveRecord's
402 # standard timestamp columns (created_at, created_on, updated_at, updated_on),
403 # they will automatically be set to Time.now.
404 #
405 # If you've set specific values, they'll be left alone.
406 #
407 # == Fixture label interpolation
408 #
409 # The label of the current fixture is always available as a column value:
410 #
411 #   geeksomnia:
412 #     name: Geeksomnia's Account
413 #     subdomain: $LABEL
414 #
415 # Also, sometimes (like when porting older join table fixtures) you'll need
416 # to be able to get ahold of the identifier for a given label. ERB
417 # to the rescue:
418 #
419 #   george_reginald:
420 #     monkey_id: <%= Fixtures.identify(:reginald) %>
421 #     pirate_id: <%= Fixtures.identify(:george) %>
422 #
423 # == Support for YAML defaults
424 #
425 # You probably already know how to use YAML to set and reuse defaults in
426 # your +database.yml+ file,. You can use the same technique in your fixtures:
427 #
428 #   DEFAULTS: &DEFAULTS
429 #     created_on: <%= 3.weeks.ago.to_s(:db) %>
430 #
431 #   first:
432 #     name: Smurf
433 #     <<: *DEFAULTS
434 #
435 #   second:
436 #     name: Fraggle
437 #     <<: *DEFAULTS
438 #
439 # Any fixture labeled "DEFAULTS" is safely ignored.
440
441 class Fixtures < YAML::Omap
442   DEFAULT_FILTER_RE = /\.ya?ml$/
443
444   @@all_cached_fixtures = {}
445
446   def self.reset_cache(connection = nil)
447     connection ||= ActiveRecord::Base.connection
448     @@all_cached_fixtures[connection.object_id] = {}
449   end
450
451   def self.cache_for_connection(connection)
452     @@all_cached_fixtures[connection.object_id] ||= {}
453     @@all_cached_fixtures[connection.object_id]
454   end
455
456   def self.fixture_is_cached?(connection, table_name)
457     cache_for_connection(connection)[table_name]
458   end
459
460   def self.cached_fixtures(connection, keys_to_fetch = nil)
461     if keys_to_fetch
462       fixtures = cache_for_connection(connection).values_at(*keys_to_fetch)
463     else
464       fixtures = cache_for_connection(connection).values
465     end
466     fixtures.size > 1 ? fixtures : fixtures.first
467   end
468
469   def self.cache_fixtures(connection, fixtures)
470     cache_for_connection(connection).update(fixtures.index_by(&:table_name))
471   end
472
473   def self.instantiate_fixtures(object, table_name, fixtures, load_instances = true)
474     object.instance_variable_set "@#{table_name.to_s.gsub('.','_')}", fixtures
475     if load_instances
476       ActiveRecord::Base.silence do
477         fixtures.each do |name, fixture|
478           begin
479             object.instance_variable_set "@#{name}", fixture.find
480           rescue FixtureClassNotFound
481             nil
482           end
483         end
484       end
485     end
486   end
487
488   def self.instantiate_all_loaded_fixtures(object, load_instances = true)
489     all_loaded_fixtures.each do |table_name, fixtures|
490       Fixtures.instantiate_fixtures(object, table_name, fixtures, load_instances)
491     end
492   end
493
494   cattr_accessor :all_loaded_fixtures
495   self.all_loaded_fixtures = {}
496
497   def self.create_fixtures(fixtures_directory, table_names, class_names = {})
498     table_names = [table_names].flatten.map { |n| n.to_s }
499     connection  = block_given? ? yield : ActiveRecord::Base.connection
500
501     table_names_to_fetch = table_names.reject { |table_name| fixture_is_cached?(connection, table_name) }
502
503     unless table_names_to_fetch.empty?
504       ActiveRecord::Base.silence do
505         connection.disable_referential_integrity do
506           fixtures_map = {}
507
508           fixtures = table_names_to_fetch.map do |table_name|
509             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))
510           end
511
512           all_loaded_fixtures.update(fixtures_map)
513
514           connection.transaction(Thread.current['open_transactions'].to_i == 0) do
515             fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures }
516             fixtures.each { |fixture| fixture.insert_fixtures }
517
518             # Cap primary key sequences to max(pk).
519             if connection.respond_to?(:reset_pk_sequence!)
520               table_names.each do |table_name|
521                 connection.reset_pk_sequence!(table_name)
522               end
523             end
524           end
525
526           cache_fixtures(connection, fixtures)
527         end
528       end
529     end
530     cached_fixtures(connection, table_names)
531   end
532
533   # Returns a consistent identifier for +label+. This will always
534   # be a positive integer, and will always be the same for a given
535   # label, assuming the same OS, platform, and version of Ruby.
536   def self.identify(label)
537     label.to_s.hash.abs
538   end
539
540   attr_reader :table_name
541
542   def initialize(connection, table_name, class_name, fixture_path, file_filter = DEFAULT_FILTER_RE)
543     @connection, @table_name, @fixture_path, @file_filter = connection, table_name, fixture_path, file_filter
544     @class_name = class_name ||
545                   (ActiveRecord::Base.pluralize_table_names ? @table_name.singularize.camelize : @table_name.camelize)
546     @table_name = ActiveRecord::Base.table_name_prefix + @table_name + ActiveRecord::Base.table_name_suffix
547     @table_name = class_name.table_name if class_name.respond_to?(:table_name)
548     @connection = class_name.connection if class_name.respond_to?(:connection)
549     read_fixture_files
550   end
551
552   def delete_existing_fixtures
553     @connection.delete "DELETE FROM #{@connection.quote_table_name(table_name)}", 'Fixture Delete'
554   end
555
556   def insert_fixtures
557     now = ActiveRecord::Base.default_timezone == :utc ? Time.now.utc : Time.now
558     now = now.to_s(:db)
559
560     # allow a standard key to be used for doing defaults in YAML
561     delete(assoc("DEFAULTS"))
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) }, nil)
625               end
626             end
627           end
628         end
629       end
630
631       @connection.insert_fixture(fixture, @table_name)
632     end
633
634     # insert any HABTM join tables we discovered
635     habtm_fixtures.values.each do |fixture|
636       fixture.delete_existing_fixtures
637       fixture.insert_fixtures
638     end
639   end
640
641   private
642     class HabtmFixtures < ::Fixtures #:nodoc:
643       def read_fixture_files; end
644     end
645
646     def model_class
647       @model_class ||= @class_name.is_a?(Class) ?
648         @class_name : @class_name.constantize rescue nil
649     end
650
651     def primary_key_name
652       @primary_key_name ||= model_class && model_class.primary_key
653     end
654
655     def has_primary_key_column?
656       @has_primary_key_column ||= model_class && primary_key_name &&
657         model_class.columns.find { |c| c.name == primary_key_name }
658     end
659
660     def timestamp_column_names
661       @timestamp_column_names ||= %w(created_at created_on updated_at updated_on).select do |name|
662         column_names.include?(name)
663       end
664     end
665
666     def inheritance_column_name
667       @inheritance_column_name ||= model_class && model_class.inheritance_column
668     end
669
670     def column_names
671       @column_names ||= @connection.columns(@table_name).collect(&:name)
672     end
673
674     def read_fixture_files
675       if File.file?(yaml_file_path)
676         read_yaml_fixture_files
677       elsif File.file?(csv_file_path)
678         read_csv_fixture_files
679       else
680         # Standard fixtures
681         Dir.entries(@fixture_path).each do |file|
682           path = File.join(@fixture_path, file)
683           if File.file?(path) and file !~ @file_filter
684             self[file] = Fixture.new(path, @class_name)
685           end
686         end
687       end
688