1
0
Fork 0
mirror of https://github.com/rails/rails.git synced 2022-11-09 12:12:34 -05:00

Tidied up Fixtures for better readability and some error checking [bitsweat]

git-svn-id: http://svn-commit.rubyonrails.org/rails/trunk@100 5ecf4fe2-1ee6-0310-87b1-e25e094e27de
This commit is contained in:
David Heinemeier Hansson 2004-12-09 15:52:54 +00:00
parent 8bf9ec6171
commit c7589559de
2 changed files with 61 additions and 42 deletions

View file

@ -30,9 +30,9 @@ require 'active_record/support/inflector'
# 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 @@ require 'active_record/support/inflector'
# 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 @@ require 'active_record/support/inflector'
# 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,38 +137,42 @@ require 'active_record/support/inflector'
# 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)
@ -176,23 +180,23 @@ class Fixtures < Hash
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 @@ class Fixtures < Hash
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 @@ class Fixture #:nodoc:
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,10 +296,10 @@ end
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
@ -294,7 +307,7 @@ class Test::Unit::TestCase #:nodoc:
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 @@ class Test::Unit::TestCase #:nodoc:
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

View file

@ -5,7 +5,7 @@ require 'fixtures/company'
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 @@ class FixturesTest < Test::Unit::TestCase
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