Skip to content

Commit

Permalink
Implicitly call FactoryGirl's syntax methods from dynamic attributes
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuaclayton committed Apr 20, 2012
1 parent 4682ab0 commit 29a5ab1
Show file tree
Hide file tree
Showing 8 changed files with 96 additions and 10 deletions.
1 change: 1 addition & 0 deletions lib/factory_girl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
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'
Expand Down
13 changes: 11 additions & 2 deletions lib/factory_girl/attribute_assigner.rb
Original file line number Diff line number Diff line change
@@ -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
Expand All @@ -18,7 +19,7 @@ def object
end

def hash
@evaluator.instance = NullObject.new
@evaluator.instance = build_hash

attributes_to_set_on_hash.inject({}) do |result, attribute|
result[attribute] = get(attribute)
Expand All @@ -32,6 +33,10 @@ def build_class_instance
@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
Expand Down Expand Up @@ -64,6 +69,10 @@ def override_names
@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) }
Expand Down
6 changes: 5 additions & 1 deletion lib/factory_girl/evaluator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,11 @@ def method_missing(method_name, *args, &block)
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

Expand Down
2 changes: 1 addition & 1 deletion lib/factory_girl/factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ def run(strategy_class, overrides, &block) #:nodoc:
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))
Expand Down
16 changes: 14 additions & 2 deletions lib/factory_girl/null_object.rb
Original file line number Diff line number Diff line change
@@ -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
5 changes: 5 additions & 0 deletions lib/factory_girl/syntax_runner.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module FactoryGirl
class SyntaxRunner
include Syntax::Methods
end
end
41 changes: 41 additions & 0 deletions spec/acceptance/syntax_methods_within_dynamic_attributes_spec.rb
Original file line number Diff line number Diff line change
@@ -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
22 changes: 18 additions & 4 deletions spec/factory_girl/null_object_spec.rb
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit 29a5ab1

Please sign in to comment.