Skip to content

Instantly share code, notes, and snippets.

@riethmayer
Created October 1, 2010 17:44
Show Gist options
  • Save riethmayer/606556 to your computer and use it in GitHub Desktop.
Save riethmayer/606556 to your computer and use it in GitHub Desktop.
Create Factory unless model exists
# spec/support/factory_helper.rb
module FactoryHelper
def self.included(base)
base.send(:include, FactoryHelper::InstanceMethods)
end
module InstanceMethods
# Extracts a klass name and the attribute name out of a string
# like this: find_or_create_factory_KLASS_NAME_by_ATTRIBUTE
# It returns the Class of KLASS_NAME if defined and the ATTRIBUTE
# In case KLASS_NAME is no valid constant, the String "KLASS_NAME"
# is returned.
# ==== Parameters
# name of a method
def extract_class_and_attributes_from name
regexp = /^find_or_create_factory_(.+?)_by_(.+?)$/
attribute = name.to_s
return name unless attribute =~ regexp
klass_name = $1.capitalize
attribute_name = $2
if Kernel.const_defined?(klass_name)
[Kernel.const_get(klass_name), attribute_name]
else
[klass_name, attribute_name]
end
end
# intercepts methods which look like
# /^find_or_create_factory_(.+?)_by_(.+?)$/ as where the first
# braces match the factory name and the second braces match the
# primary attribute you're searching for.
# ==== Parameters
# name of the method
# args for the method
def method_missing(name, *args)
klass, attribute = extract_class_and_attributes_from name
if klass.class == String || attribute.blank?
super(name, *args)
else
find_or_create_factory(klass, attribute, *args)
end
end
private
# finds an object or creates one, where the first attribute must
# be the field you're searching for (so this must be unique).
#
# ==== Parameters
# klass_name: the name of your model
# attributes: e.g. :title => "foo", :bar => "bar"
def find_or_create_factory klass, attribute, *attributes
attribute = attribute.to_sym
obj = klass.send(:where, attribute => attributes[0][attribute]).first
obj || Factory(klass.to_s.downcase.to_sym, *attributes)
end
end
end
# spec/models/factory_spec_helper.rb
require 'spec_helper'
describe FactoryHelper do
include FactoryHelper
describe "#find_or_create_factory_KLASS_NAME_by_ATTRIBUTE" do
describe "name extraction" do
it "should return the name itself if it doesn't match" do
name = "foo"
extract_class_and_attributes_from(name).should == name
end
it "should return the matching klass_name and attribute if the regex matches" do
method_name = "find_or_create_factory_klass_by_ATTRIBUTE"
name, attribute = extract_class_and_attributes_from method_name
name.should == "Klass"
attribute.should == "ATTRIBUTE"
end
it "should convert into a constant if the class has been found" do
method_name = "find_or_create_factory_organization_by_ATTRIBUTE"
name, attribute = extract_class_and_attributes_from method_name
name.should == Organization
attribute.should == "ATTRIBUTE"
end
end
describe "finding part" do
it "should find an existing organization" do
organization = Factory(:organization, :title => "foo")
found = find_or_create_factory_organization_by_title(:title => "foo")
found.should == organization
end
end
describe "creating part" do
it "should create a nonexisting organization" do
organization = Organization.find_by_title "foo"
organization.destroy! if organization
Organization.find_by_title("foo").should be_nil
find_or_create_factory_organization_by_title(:title => "foo").should_not be_nil
Organization.find_by_title("foo").should_not be_nil
end
it "should raise a method missing if you try to create nonsense" do
lambda {find_or_create_something_nonexistant_by_foo(:foo => "foo")}.
should raise_error
end
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment