Permalink
Browse files

Tidied up Fixtures for better readability and some error checking [bi…

…tsweat]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@100 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
  • Loading branch information...
1 parent 8bf9ec6 commit c7589559dea75a735ccb74364b06d57f26f1db3d @dhh dhh committed Dec 9, 2004
Showing with 61 additions and 42 deletions.
  1. +49 −36 activerecord/lib/active_record/fixtures.rb
  2. +12 −6 activerecord/test/fixtures_test.rb
@@ -30,9 +30,9 @@
# name: Google
# url: http://www.google.com
#
-# This YAML fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and is followed by an
+# This YAML fixture file includes two fixtures. Each YAML fixture (ie. record) is given a name and is followed by an
# indented list of key/value pairs in the "key: value" format. Records are separated by a blank line for your viewing
-# pleasure.
+# pleasure.
#
# = CSV fixtures
#
@@ -51,17 +51,17 @@
# need to use a double quote character, you must escape it with another double quote.
#
# Another unique attribute of the CSV fixture is that it has *no* fixture name like the other two formats. Instead, the
-# fixture names are automatically generated by deriving the class name of the fixture file and adding an incrementing
+# fixture names are automatically generated by deriving the class name of the fixture file and adding an incrementing
# number to the end. In our example, the 1st fixture would be called "web_site_1" and the 2nd one would be called
# "web_site_2".
#
-# Most databases and spreadsheets support exporting to CSV format, so this is a great format for you to choose if you
+# Most databases and spreadsheets support exporting to CSV format, so this is a great format for you to choose if you
# have existing data somewhere already.
-#
-# = Single-file fixtures
+#
+# = Single-file fixtures
#
# This type of fixtures was the original format for Active Record that has since been deprecated in favor of the YAML and CSV formats.
-# Fixtures for this format are created by placing text files in a sub-directory (with the name of the model) to the directory
+# Fixtures for this format are created by placing text files in a sub-directory (with the name of the model) to the directory
# appointed by <tt>Test::Unit::TestCase.fixture_path=(path)</tt> (this is automatically configured for Rails, so you can just
# put your files in <your-rails-app>/test/fixtures/<your-model-name>/ -- like <your-rails-app>/test/fixtures/web_sites/ for the WebSite
# model).
@@ -91,19 +91,19 @@
# class WebSiteTest < Test::Unit::TestCase
# def test_web_site_count
# assert_equal 2, WebSite.count
-# end
+# end
# end
-#
-# As it stands, unless we pre-load the web_site table in our database with two records, this test will fail. Here's the
+#
+# As it stands, unless we pre-load the web_site table in our database with two records, this test will fail. Here's the
# easiest way to add fixtures to the database:
#
# ...
# class WebSiteTest < Test::Unit::TestCase
# fixtures :web_sites # add more by separating the symbols with commas
# ...
-#
+#
# By adding a "fixtures" method to the test case and passing it a list of symbols (only one is shown here tho), we trigger
-# the testing environment to automatically load the appropriate fixtures into the database before each test, and
+# the testing environment to automatically load the appropriate fixtures into the database before each test, and
# automatically delete them after each test.
#
# In addition to being available in the database, the fixtures are also loaded into a hash stored in an instance variable
@@ -137,62 +137,66 @@
# This will create 1000 very simple YAML fixtures.
#
# Using ERb, you can also inject dynamic values into your fixtures with inserts like <%= Date.today.strftime("%Y-%m-%d") %>.
-# This is however a feature to be used with some caution. The point of fixtures are that they're stable units of predictable
+# This is however a feature to be used with some caution. The point of fixtures are that they're stable units of predictable
# sample data. If you feel that you need to inject dynamic values, then perhaps you should reexamine whether your application
# is properly testable. Hence, dynamic values in fixtures are to be considered a code smell.
class Fixtures < Hash
+ DEFAULT_FILTER_RE = /\.ya?ml$/
+
def self.instantiate_fixtures(object, fixtures_directory, *table_names)
[ create_fixtures(fixtures_directory, *table_names) ].flatten.each_with_index do |fixtures, idx|
object.instance_variable_set "@#{table_names[idx]}", fixtures
fixtures.each { |name, fixture| object.instance_variable_set "@#{name}", fixture.find }
end
end
-
+
def self.create_fixtures(fixtures_directory, *table_names)
connection = block_given? ? yield : ActiveRecord::Base.connection
old_logger_level = ActiveRecord::Base.logger.level
begin
ActiveRecord::Base.logger.level = Logger::ERROR
- fixtures = []
+
+ fixtures = table_names.flatten.map do |table_name|
+ Fixtures.new(connection, table_name.to_s, File.join(fixtures_directory, table_name.to_s))
+ end
+
connection.transaction do
- fixtures = table_names.flatten.map do |table_name|
- Fixtures.new(connection, table_name.to_s, File.join(fixtures_directory, table_name.to_s))
- end
fixtures.reverse.each { |fixture| fixture.delete_existing_fixtures }
fixtures.each { |fixture| fixture.insert_fixtures }
end
+
return fixtures.size > 1 ? fixtures : fixtures.first
ensure
ActiveRecord::Base.logger.level = old_logger_level
end
end
- def initialize(connection, table_name, fixture_path, file_filter = /^\.|CVS|\.yml|\.csv/)
+ def initialize(connection, table_name, fixture_path, file_filter = DEFAULT_FILTER_RE)
@connection, @table_name, @fixture_path, @file_filter = connection, table_name, fixture_path, file_filter
@class_name = Inflector.classify(@table_name)
read_fixture_files
end
def delete_existing_fixtures
- @connection.delete "DELETE FROM #{@table_name}"
+ @connection.delete "DELETE FROM #{@table_name}", 'Fixture Delete'
end
def insert_fixtures
values.each do |fixture|
- @connection.execute "INSERT INTO #{@table_name} (#{fixture.key_list}) VALUES(#{fixture.value_list})"
+ @connection.execute "INSERT INTO #{@table_name} (#{fixture.key_list}) VALUES (#{fixture.value_list})", 'Fixture Insert'
end
end
private
def read_fixture_files
- if File.exists?(yaml_file_path)
+ if File.file?(yaml_file_path)
# YAML fixtures
YAML::load(erb_render(IO.read(yaml_file_path))).each do |name, data|
self[name] = Fixture.new(data, @class_name)
end
- elsif File.exists?(csv_file_path)
+ elsif File.file?(csv_file_path)
# CSV fixtures
reader = CSV::Reader.create(erb_render(IO.read(csv_file_path)))
header = reader.shift
@@ -202,22 +206,31 @@ def read_fixture_files
row.each_with_index { |cell, j| data[header[j].to_s.strip] = cell.to_s.strip }
self["#{Inflector::underscore(@class_name)}_#{i+=1}"]= Fixture.new(data, @class_name)
end
+ elsif File.file?(deprecated_yaml_file_path)
+ raise Fixture::FormatError, ".yml extension required: rename #{deprecated_yaml_file_path} to #{yaml_file_path}"
else
# Standard fixtures
- Dir.entries(@fixture_path).each do |file|
- self[file] = Fixture.new(File.join(@fixture_path, file), @class_name) unless file =~ @file_filter
+ Dir.entries(@fixture_path).each do |file|
+ path = File.join(@fixture_path, file)
+ if File.file?(path) and file !~ @file_filter
+ self[file] = Fixture.new(path, @class_name)
+ end
end
end
end
def yaml_file_path
- @fixture_path + ".yml"
+ "#{@fixture_path}.yml"
+ end
+
+ def deprecated_yaml_file_path
+ "#{@fixture_path}.yaml"
end
def csv_file_path
@fixture_path + ".csv"
end
-
+
def yaml_fixtures_key(path)
File.basename(@fixture_path).split(".").first
end
@@ -256,25 +269,25 @@ def key_list
def value_list
@fixture.values.map { |v| ActiveRecord::Base.connection.quote(v).gsub('\\n', "\n").gsub('\\r', "\r") }.join(", ")
end
-
+
def find
Object.const_get(@class_name).find(self[Object.const_get(@class_name).primary_key])
end
-
+
private
def read_fixture_file(fixture_file_path)
IO.readlines(fixture_file_path).inject({}) do |fixture, line|
# Mercifully skip empty lines.
- next if line.empty?
+ next if line =~ /^\s*$/
# Use the same regular expression for attributes as Active Record.
unless md = /^\s*([a-zA-Z][-_\w]*)\s*=>\s*(.+)\s*$/.match(line)
- raise FormatError, "#{path}: fixture format error at '#{line}'. Expecting 'key => value'."
+ raise FormatError, "#{fixture_file_path}: fixture format error at '#{line}'. Expecting 'key => value'."
end
key, value = md.captures
# Disallow duplicate keys to catch typos.
- raise FormatError, "#{path}: duplicate '#{key}' in fixture." if fixture[key]
+ raise FormatError, "#{fixture_file_path}: duplicate '#{key}' in fixture." if fixture[key]
fixture[key] = value.strip
fixture
end
@@ -283,18 +296,18 @@ def read_fixture_file(fixture_file_path)
class Test::Unit::TestCase #:nodoc:
include ClassInheritableAttributes
-
+
cattr_accessor :fixture_path
cattr_accessor :fixture_table_names
-
+
def self.fixtures(*table_names)
write_inheritable_attribute("fixture_table_names", table_names)
end
def setup
instantiate_fixtures(*fixture_table_names) if fixture_table_names
end
-
+
def self.method_added(method_symbol)
if method_symbol == :setup && !method_defined?(:setup_without_fixtures)
alias_method :setup_without_fixtures, :setup
@@ -309,7 +322,7 @@ def self.method_added(method_symbol)
def instantiate_fixtures(*table_names)
Fixtures.instantiate_fixtures(self, fixture_path, *table_names)
end
-
+
def fixture_table_names
self.class.read_inheritable_attribute("fixture_table_names")
end
@@ -5,7 +5,7 @@
class FixturesTest < Test::Unit::TestCase
fixtures :topics, :developers, :accounts
-
+
FIXTURES = %w( accounts companies customers
developers developers_projects entrants
movies projects subscribers topics )
@@ -49,34 +49,40 @@ def test_inserts
def test_bad_format
path = File.join(File.dirname(__FILE__), 'fixtures', 'bad_fixtures')
Dir.entries(path).each do |file|
- next unless File.file?(file) and file !~ %r(^.|.yaml$)
+ next unless File.file?(file) and file !~ Fixtures::DEFAULT_FILTER_RE
assert_raise(Fixture::FormatError) {
Fixture.new(bad_fixtures_path, file)
}
end
end
+ def test_deprecated_yaml_extension
+ assert_raise(Fixture::FormatError) {
+ Fixtures.new(nil, 'bad_extension', File.join(File.dirname(__FILE__), 'fixtures'))
+ }
+ end
+
def test_logger_level_invariant
level = ActiveRecord::Base.logger.level
create_fixtures('topics')
assert_equal level, ActiveRecord::Base.logger.level
end
-
+
def test_instantiation
topics = create_fixtures("topics")
assert_kind_of Topic, topics["first"].find
end
-
+
def test_complete_instantiation
assert_equal 2, @topics.size
assert_equal "The First Topic", @first.title
end
-
+
def test_fixtures_from_root_yml_with_instantiation
# assert_equal 2, @accounts.size
assert_equal 50, @unknown.credit_limit
end
-
+
def test_erb_in_fixtures
assert_equal 10, @developers.size
assert_equal "fixture_5", @dev_5.name

0 comments on commit c758955

Please sign in to comment.