Implicitly call FactoryGirl's syntax methods from dynamic attributes

This commit is contained in:
Joshua Clayton 2012-04-19 21:34:47 -04:00
parent 4682ab0701
commit 29a5ab1a89
8 changed files with 96 additions and 10 deletions

View File

@ -28,6 +28,7 @@ require 'factory_girl/aliases'
require 'factory_girl/definition'
require 'factory_girl/definition_proxy'
require 'factory_girl/syntax'
require 'factory_girl/syntax_runner'
require 'factory_girl/find_definitions'
require 'factory_girl/reload'
require 'factory_girl/version'

View File

@ -1,6 +1,7 @@
module FactoryGirl
class AttributeAssigner
def initialize(evaluator, &instance_builder)
def initialize(evaluator, build_class, &instance_builder)
@build_class = build_class
@instance_builder = instance_builder
@evaluator = evaluator
@attribute_list = evaluator.class.attribute_list
@ -18,7 +19,7 @@ module FactoryGirl
end
def hash
@evaluator.instance = NullObject.new
@evaluator.instance = build_hash
attributes_to_set_on_hash.inject({}) do |result, attribute|
result[attribute] = get(attribute)
@ -32,6 +33,10 @@ module FactoryGirl
@build_class_instance ||= @evaluator.instance_exec(&@instance_builder)
end
def build_hash
@build_hash ||= NullObject.new(hash_instance_methods_to_respond_to)
end
def get(attribute_name)
@evaluator.send(attribute_name)
end
@ -64,6 +69,10 @@ module FactoryGirl
@evaluator.__overrides.keys
end
def hash_instance_methods_to_respond_to
@attribute_list.map(&:name) + override_names + @build_class.instance_methods
end
def alias_names_to_ignore
@attribute_list.reject(&:ignored).map do |attribute|
override_names.map {|override| attribute.name if attribute.alias_for?(override) && attribute.name != override && !ignored_attribute_names.include?(override) }

View File

@ -47,7 +47,11 @@ module FactoryGirl
if @cached_attributes.key?(method_name)
@cached_attributes[method_name]
else
@instance.send(method_name, *args, &block)
if @instance.respond_to?(method_name)
@instance.send(method_name, *args, &block)
else
SyntaxRunner.new.send(method_name, *args, &block)
end
end
end

View File

@ -33,7 +33,7 @@ module FactoryGirl
strategy = strategy_class.new
evaluator = evaluator_class.new(strategy, overrides.symbolize_keys)
attribute_assigner = AttributeAssigner.new(evaluator, &instance_builder)
attribute_assigner = AttributeAssigner.new(evaluator, build_class, &instance_builder)
evaluation = Evaluation.new(attribute_assigner, to_create)
evaluation.add_observer(CallbackRunner.new(callbacks, evaluator))

View File

@ -1,7 +1,19 @@
module FactoryGirl
class NullObject < ::BasicObject
def method_missing(*args)
nil
def initialize(methods_to_respond_to)
@methods_to_respond_to = methods_to_respond_to.map(&:to_s)
end
def method_missing(name, *args, &block)
if respond_to?(name)
nil
else
super
end
end
def respond_to?(method, include_private=false)
@methods_to_respond_to.include? method.to_s
end
end
end

View File

@ -0,0 +1,5 @@
module FactoryGirl
class SyntaxRunner
include Syntax::Methods
end
end

View File

@ -0,0 +1,41 @@
require "spec_helper"
describe "syntax methods within dynamic attributes" do
before do
define_model("Post", title: :string, user_id: :integer) do
belongs_to :user
def generate
"generate result"
end
end
define_model("User", email: :string)
FactoryGirl.define do
sequence(:email_address) {|n| "person-#{n}@example.com" }
factory :user do
email { generate(:email_address) }
end
factory :post do
title { generate }
user { build(:user) }
end
end
end
it "can access syntax methods from dynamic attributes" do
FactoryGirl.build(:user).email.should == "person-1@example.com"
FactoryGirl.attributes_for(:user)[:email].should == "person-2@example.com"
end
it "can access syntax methods from dynamic attributes" do
FactoryGirl.build(:post).user.should be_instance_of(User)
end
it "can access methods already existing on the class" do
FactoryGirl.build(:post).title.should == "generate result"
FactoryGirl.attributes_for(:post)[:title].should be_nil
end
end

View File

@ -1,8 +1,22 @@
require "spec_helper"
describe FactoryGirl::NullObject do
its(:id) { should be_nil }
its(:age) { should be_nil }
its(:name) { should be_nil }
its(:admin?) { should be_nil }
let(:methods_to_respond_to) { %w[id age name admin?] }
let(:methods_to_not_respond_to) { %w[email date_of_birth title] }
subject { FactoryGirl::NullObject.new(methods_to_respond_to) }
it "responds to the given methods" do
methods_to_respond_to.each do |method_name|
subject.__send__(method_name).should be_nil
subject.respond_to?(method_name).should be_true
end
end
it "does not respond to other methods" do
methods_to_not_respond_to.each do |method_name|
expect { subject.__send__(method_name) }.to raise_error(NoMethodError)
subject.respond_to?(method_name).should be_false
end
end
end