Created
December 5, 2008 14:14
-
-
Save oleganza/32343 to your computer and use it in GitHub Desktop.
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
# SimpleFixtures is an alternative to dm-sweatshop | |
# | |
# Oleg Andreev <[email protected]> | |
# November 24, 2008 | |
# License: WTFPL (http://sam.zoy.org/wtfpl/) | |
# | |
module SimpleFixtures | |
# Define a named fixture. Named fixtures other than :default are merged to :default one | |
# | |
# A.define_fixture(:a => 1) | |
# A.define_fixture {|overrides| {:a => 1}.merge(overrides) } | |
# A.define_fixture(:some_name, :a => 1) | |
# A.define_fixture(:some_name, proc {|overrides| {:a => 1} }) | |
# | |
def define_fixture(name = nil, attrs = nil, &blk) | |
if !name.is_a?(Symbol) and !name.is_a?(String) # name and attrs could be nil, but blk != nil | |
attrs = name | |
name = :__default__ | |
end | |
fixtures[name] = attrs || blk || {} | |
end | |
# DSSV = double space separated values. | |
# Example: | |
# name email account | |
# oleg Oleg [email protected] proc {Account.fixture!(:idbns)} | |
# antares Michael [email protected] proc {Account.fixture!(:nr)} | |
# | |
def dssv_fixtures(dssv) | |
splitted_lines = dssv.to_a. | |
map{|l| l.strip }. # strip spaces and CR/LF | |
select{|l| !l.empty? }. # remove blank lines | |
map{|l| l.split(/\s\s+/) } # split by two+ spaces | |
fields = splitted_lines.shift.map { |f| f.strip.to_sym } | |
splitted_lines.each do |arr| | |
arr.size != (fields.size + 1) and raise "Malformed DSSV fixture format (check double spaces!)" | |
# extract tag as Symbol | |
tag = arr.shift.to_sym | |
# add field names to values and convert to hash {:name=>"Name", :email=>"email", ...} | |
attrs = Hash[*(fields.zip(arr).flatten)] | |
# define named fixture | |
define_fixture(tag, attrs.inject({}) { |h, (k, v)| | |
h[k] = (v =~ %r{^proc\s?\{.*\}$}i ? eval(v) : P.autotype(v)) | |
h | |
}) | |
end | |
end | |
def fixture!(*args, &blk) | |
# special case: only name is given - return an unique fixture | |
return unique_fixture!(args.first) if args.size == 1 and args.first.is_a?(Symbol) || args.first.is_a?(String) | |
instance = fixture(*args, &blk) | |
instance.save or raise "Could not save #{instance}! Errors: #{instance.errors.to_a.inspect}" | |
instance | |
end | |
# A.fixture # default fixture | |
# A.fixture(:a => 1) # default fixture with overrides | |
# A.fixture(:black) # :black fixture | |
# A.fixture(:black, :a => 1) # :black fixture with overrides | |
# | |
def fixture(*args, &blk) | |
new(fixture_attributes(*args), &blk) | |
end | |
def fixture_attributes(*args) | |
P.fixture_attributes(self, *args) | |
end | |
def fast_clean! | |
@unique_fixtures = nil | |
super rescue nil | |
end | |
def unique_fixture!(name, overrides = {}) | |
@unique_fixtures ||= Hash.new # this should be cleared in DM.fast_clean! | |
@unique_fixtures[name] ||= fixture!(name, overrides) | |
end | |
def fixtures | |
@fixtures ||= Hash.new({}) | |
end | |
module P extend self | |
def autotype(str) # for DSSV | |
case str | |
when /^\:([\w\d_]+)$/: $1.to_sym | |
when /^\d[\d\_]+$/: str.to_i | |
when /^\d[\d\_\.]+$/: str.to_f | |
else str | |
end | |
end | |
def fixture_attributes(model, *args) | |
name = args.shift if args.first.is_a?(Symbol) || args.first.is_a?(String) | |
sc = model.superclass | |
base_fixtures = sc.respond_to?(:fixture_attributes) ? sc.fixture_attributes(name) : {} | |
execute_merge(model, base_fixtures, # first, use superclass fixture | |
execute_merge(model, model.fixtures[:__default__], # then override with local class default fixture | |
execute_merge(model, model.fixtures[name], # then override with named fixture | |
execute_merge(model, args.shift || {}, args.shift || {})))). # then override with local attributes | |
inject({}) do |h, (k,v)| | |
h[k] = (v.respond_to?(:call)) ? v.call : v if v | |
h | |
end | |
end | |
def execute_merge(model, proc_or_hash, overrides = {}) | |
case proc_or_hash | |
when Proc: proc_or_hash.call(overrides) | |
when Hash: proc_or_hash.merge(overrides) | |
else | |
raise "Attributes must be Proc, DuckType(:call) or Hash (was #{proc_or_hash.inspect})" | |
end | |
end | |
end # P | |
module RandomFixtures | |
def random_sha1 | |
require 'digest/sha1' | |
Digest::SHA1.hexdigest("#{rand(2**160)}") | |
end | |
def valid_email_fixture(prefix = "user", domain = "example.com") | |
"#{prefix}#{rand(2**64)}@#{domain}" | |
end | |
# Generates exponentially distributed time values | |
def random_fixture_datetime(range = 55, base = 1.5) | |
offset = (base**(rand * range)) | |
offset > 2**30 && offset = 2**30 | |
Time.now - offset | |
end | |
end | |
include RandomFixtures | |
module RSpec | |
# fixtures_spec.rb: | |
# extend SimpleFixtures::RSpec | |
# verify_default_fixtures!( | |
# :models => [ Person, Project ], | |
# :except => [ Merb::DataMapperSessionStore ] | |
# ) | |
def verify_default_fixtures!(opts = {}) | |
fixtured_models = opts[:models] || [ ] | |
exceptions = opts[:except] || opts [:exceptions] || [ ] | |
fixtured_models.each do |model| | |
describe model, "default fixture" do | |
before(:all) do | |
@fixture = model.fixture! | |
end | |
it { @fixture.should be_valid } | |
end | |
end | |
(DataMapper::Resource.descendants.to_a - fixtured_models - exceptions).each do |model| | |
describe model, "default fixture" do | |
it "should have valid default fixture" do | |
pending "add this class to a list of models with fixtures" | |
end | |
end | |
end | |
end # verify_default_fixtures | |
end # RSpec | |
end # SimpleFixtures | |
DataMapper::Model.send(:include, SimpleFixtures) if defined?(DataMapper) |
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
require File.join(File.dirname(__FILE__), "spec_helper") | |
describe SimpleFixtures do | |
before(:each) do | |
@model = Class.new do | |
attr_accessor :attrs, :blk | |
def initialize(attrs, &blk) | |
@attrs = attrs | |
@blk = blk | |
end | |
def save | |
@saved = true | |
end | |
def saved? | |
@saved | |
end | |
end | |
@model.extend(SimpleFixtures) | |
end | |
describe "regular named fixtures" do | |
before(:each) do | |
model = @model | |
@model.define_fixture( :name => "Person", :friend => proc { model.fixture!(:with_block) } ) | |
@model.define_fixture(:oleg, :name => "Oleg", :friend => proc { model.fixture!(:antares) } ) | |
@model.define_fixture(:antares, proc {{:name => "MK"}}) | |
@model.define_fixture(:with_block) do |overrides| # with block, but without a friend | |
overrides.merge(:with_block => :i_am_over_it, :friend => nil) | |
end | |
end | |
it "should have named fixtures" do | |
@model.fixtures[:__default__].should_not be_empty | |
@model.fixtures[:oleg].should_not be_empty | |
@model.fixtures[:antares].should respond_to(:call) | |
@model.fixtures[:with_block].should respond_to(:call) | |
end | |
it "should generate a named and default fixtures using Model#fixture and #fixture!" do | |
f = @model.fixture(&@some_block) | |
f.should_not be_saved | |
f.attrs[:friend].should be_saved | |
f.attrs[:name].should == "Person" | |
f.attrs[:friend].should be_kind_of(@model) | |
f.attrs[:friend].attrs[:name].should == "Person" | |
f.attrs[:friend].attrs[:with_block].should == :i_am_over_it | |
f.attrs[:friend].attrs[:friend].should == nil | |
end | |
describe "unique_fixture!(:name, overrides = {})" do | |
before(:each) do | |
@uf1 = @model.unique_fixture!(:oleg, {:xyz => 123}) | |
@uf2 = @model.unique_fixture!(:oleg, {:qwe => 345}) | |
end | |
it "should return equal unique fixtures" do | |
@uf1.object_id.should == @uf2.object_id | |
end | |
it "should set attributes provided first time" do | |
@uf2.attrs[:xyz].should == 123 | |
@uf2.attrs[:qwe].should be_nil | |
end | |
end # unique_fixture!(:name, overrides) | |
describe "fixture!(:name)" do | |
before(:each) do | |
@uf1 = @model.fixture!(:oleg) | |
@uf2 = @model.fixture!(:oleg) | |
@f1 = @model.fixture!(:oleg, {:xyz => 123}) | |
@f2 = @model.fixture!(:oleg, {:qwe => 345}) | |
end | |
it "should return an unique fixture using fixture!(:name) with not overrides given" do | |
@uf1.object_id.should == @uf2.object_id | |
end | |
it "should return a not unique fixture using fixture!(:name) with overrides given" do | |
@f1.object_id.should_not == @f2.object_id | |
end | |
end # fixture!(:name) | |
end # regular | |
describe "with DSSV" do | |
before(:each) do | |
@model.dssv_fixtures(%{ | |
name autotype country | |
spb St. Petersburg 5_000_000 proc { ("Rus" + "sia").to_sym } | |
paris Paris, Île-de-France :symbol proc {%{France}} | |
}) | |
end | |
it "should generate named fixtures" do | |
spb = @model.fixture(:spb) | |
spb.attrs.should == {:name => "St. Petersburg", :autotype => 5_000_000, :country => :Russia } | |
paris = @model.fixture(:paris) | |
paris.attrs.should == {:name => "Paris, Île-de-France", :autotype => :symbol, :country => "France" } | |
end | |
describe "(malformed DSSV)" do | |
it do | |
lambda{ @model.dssv_fixtures(%{ | |
a b c | |
tag1 a b c | |
tag2 a b b2 c | |
}) }.should raise_error | |
end | |
end | |
end # DSSV | |
describe "inherited fixtures" do | |
before(:each) do | |
@model2 = Class.new(@model){} | |
@model3 = Class.new(@model2){} | |
@model.define_fixture(:base => true, :prop => 1) | |
@model2.define_fixture(:child => true, :prop => 2) | |
@model3.define_fixture(:grandchild => true, :prop => 3) | |
@model.define_fixture(:named, :named => :base, :named_prop => 1) | |
@model2.define_fixture(:named, :named_prop => 2) | |
@model3.define_fixture(:named, :named_prop => 3) | |
end | |
it "should inherit default attributes" do | |
@model2.fixture_attributes.should == { | |
:base => true, | |
:child => true, | |
:prop => 2 | |
} | |
@model3.fixture_attributes.should == { | |
:base => true, | |
:child => true, | |
:grandchild => true, | |
:prop => 3 | |
} | |
end | |
it "should inherit named attributes" do | |
@model2.fixture_attributes(:named).should == { | |
:base => true, | |
:child => true, | |
:prop => 2, | |
:named => :base, | |
:named_prop => 2 | |
} | |
@model3.fixture_attributes(:named).should == { | |
:base => true, | |
:child => true, | |
:grandchild => true, | |
:prop => 3, | |
:named => :base, | |
:named_prop => 3 | |
} | |
end | |
end # inherited fixtures | |
end # SimpleFixtures |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment