Created
November 20, 2008 23:55
-
-
Save mjwillson/27283 to your computer and use it in GitHub Desktop.
Some general-purpose fixture-loading code which supports references and respects dependencies. Quite short as it hooks into yaml.rb to do most of the work
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# This is a mixin for Test::Unit::TestCase, for providing tests with a simple facility to load fixtures from | |
# YAML files. It aims to do as much as possible in a generic way with the YAML library rather than couple | |
# tightly to a database library. | |
# | |
# Fixtures can use arbitrary YAML to represent ruby objects, using !ruby/object:Some::ClassName where needed. | |
# you will typically need to implement yaml_initialize on the relevant class, in order for this to | |
# work in the way you desire (we do so here for Sequel::Model; others including ActiveRecord shouldn't be hard) | |
# | |
# Data within one fixture can refer to other fixtures by file and name, using the syntax: !fixture file/name. | |
# this is achieved by adding a special YAML domain type. | |
# | |
# By performing the 'parse' and 'transform' phases of the YAML parser separately, we can do so in a way that | |
# ensures that ruby objects are instantiated in a correct depth-first order respecting dependencies. | |
# | |
# Circular dependencies it won't cope with, though. Addressing these in a suitably general way may be hard; we | |
# suggest you "join up the dots" yourself post-loading for now, although it may be possible to add some kind | |
# of generic support for calling a "yaml_reinitialize_after_circular_dependencies_have_been_resolved" method. | |
# | |
# As an example: artists.yml | |
# | |
# non_phixion: !ruby/object:Artist | |
# genre: !fixture genres/rap | |
# similar_to: !fixture artists/arsonists | |
# rating: 7 | |
# | |
# arsonists: !ruby/object:Artist | |
# genre: !fixture genres/rap | |
# rating: 6 | |
require 'yaml' | |
module YAMLFixtures | |
@dir = File.dirname(__FILE__)+'/fixtures' | |
@fixture_files = {} | |
@loaded = {} | |
YAML.add_domain_type('yaml.org,2002', 'fixture') do |type, val| | |
file, name = val.split('/',2) | |
load(file, name) | |
end | |
def self.fixture_file_yaml_node(file) | |
data = File.read(File.join(@dir, "#{file}.yml")) | |
node = YAML.parse(data) | |
@fixture_files[file] ||= node | |
end | |
def self.load(file, name) | |
@loaded[file] ||= {} | |
result = @loaded[file][name] and return result | |
raise "Cyclic dependency" if result == false | |
puts "* Loading fixture #{file}/#{name}" | |
@loaded[file][name] = false | |
# we get the YAML::Syck::Node for only this fixture within the parse tree, and we invoke the transform phase on just this one node at a time, | |
# This ensures that objects are loaded in the right order when there are fixture references between them. | |
node = fixture_file_yaml_node(file)[name] | |
# YAML won't auto-load classes, so we give it a helping hand: | |
$1.constantize if node.type_id =~ /^tag:ruby.yaml.org,2002:object:(.*)$/ | |
@loaded[file][name] = node.transform | |
end | |
def self.load_all(file) | |
names = fixture_file_yaml_node.value.keys.map {|key| key.value} | |
result = {} | |
names.each {|name| result[name] = load(file, name)} | |
result | |
end | |
def self.purge_fixtures | |
@loaded = {} | |
end | |
def load(file, name) | |
YAMLFixtures.load(file, name) | |
end | |
def load_all(file) | |
YAMLFixtures.load_all(file) | |
end | |
def run(*args, &block) | |
begin | |
super | |
ensure | |
YAMLFixtures.purge_fixtures | |
end | |
end | |
end | |
class Sequel::Model | |
def yaml_initialize(type, value_hash) | |
initialize(value_hash) | |
save | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment