Merb: fixtures
Ostatnio szukałem jakiegoś dobrego narzędzia do fixtures (nie mam pojęcia jak to można po polsku nazwa) w Merbie i znalazłem projekt na githubie merb-fixtures. Niestety okazało się, że sprawia pewne problemy w szczególności z dm-is-nested_set z paczki dm-more DataMappera. Jednak postanowiłem się nie poddawać i korzystając z kodu merb-fixtures napisać coś własnego.
Cała zawartość pliku fixtures.rb
prezentuje się tak:
module Fixtures
class Manager
class << self
def init
@@fixtures = {}
end
def load
Merb.logger.debug("Loading fixtures ...")
unless Merb.env?("production")
directory = Merb.root / "app" / "fixtures"
if File.exist?(directory)
Dir[directory / "*.rb"].each do |file|
Kernel.load(file)
end
else
raise 'FixturesDirectoryNotFound'
end
end
end
def create(klass, name, &block)
unless object = klass.fixture(name)
object = klass.new
@@fixtures[klass] << {:name => name, :object => object}
end
object.instance_eval(&block) if block_given?
object.save! if object.dirty?
object
end
def fixture(klass, name)
if fix = (@@fixtures[klass] ||= []).find {|e| e[:name] == name}
fix[:object]
end
end
def fixtures(klass)
@@fixtures[klass]
end
def delete_fixtures(klass)
@@fixtures[klass] = []
end
def save_all
@@fixtures.each_pair do |_, fixtures|
fixtures.each {|f| f[:object].save}
end
end
end
end
module Extensions
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def fixture(name)
Manager.fixture(self, name)
end
def fixtures
Manager.fixtures(self)
end
def delete_fixtures
Manager.delete_fixtures(self)
end
end
end
end
models = []
ObjectSpace.each_object(Class) do |klass|
if klass.included_modules.include?(DataMapper::Resource)
klass.send(:include, Fixtures::Extensions)
end
end
module Kernel
def fixture_for(klass, name, &block)
Fixtures::Manager.create(klass, name, &block)
end
end
Teraz krótko o tym jak się tego używa.
Po pierwsze wrzucamy plik fixtures.rb do katalogu lib
i dodajemy w pliku config/init.rb
Merb::BootLoader.after_app_loads do
require 'lib/fixtures.rb'
end
Jest to bardzo istotne aby umieścić require
w tym bloku ponieważ biblioteka wymaga dostępu do klas modeli.
Następnie tworzymy katalog app/fixtures
i w nim dla przykładu plik category.rb
o zawartości:
fixture_for(Category, :root) do
self.name = 'root'
end
fixture_for(Category, :space_ships) do
self.name = 'Space ships'
self.parent = Category.fixture(:root)
end
fixture_for(Category, :fast_space_ships) do
self.name = 'Fast ships'
self.parent = Category.fixture(:space_ships)
end
fixture_for(Category, :big_space_ships) do
self.name = 'Big ships'
self.parent = Category.fixture(:space_ships)
end
fixture_for(Category, :robots) do
self.name = 'Robots'
self.parent = Category.fixture(:root)
end
fixture_for(Category, :human_like_robots) do
self.name = 'Human like'
self.parent = Category.fixture(:robots)
end
fixture_for(Category, :other_robots) do
self.name = 'Other'
self.parent = Category.fixture(:robots)
end
Teraz możemy załadować (poprzez merb -i
) dane do bazy za pomocą Fixtures::Manager.load
. Jednak najbardziej przydanym wykorzystaniem fixtures są testy…
W pliku spec/spec_helper.rb
dodajemy:
DataMapper.auto_migrate!
Fixtures::Manager.init
def fixtures(*classes)
Fixtures::Manager.load
classes.each {|klass|
klass.fixtures.each { |e|
instance_variable_set("@#{e[:name]}", e[:object].clone)
}
}
end
Teraz w pliku category_spec.rb
:
before(:each) do
fixtures(Category)
# można załadować kilka na raz np.
# fixtures(Category, Product, User)
end
Da nam to nie tylko dostęp do obiektów za pomocą Klasa.fixture(:nazwa)
, ale też @nazwa
(w tym przypadku np.@space_ships
)
Nie pozostaje nic innego tylko zabrać się do wygodnego pisanie testów ;)
P.S. Znowu wyszła tona kodu + dwa zdania…
Looking for comments section?
Send me an email instead to [email protected]