diff --git a/lib/factory_girl.rb b/lib/factory_girl.rb index 90ed7aa..13eeae5 100644 --- a/lib/factory_girl.rb +++ b/lib/factory_girl.rb @@ -52,7 +52,7 @@ module FactoryGirl def self.sequence_by_name(name) sequences.find(name) end - + def self.attribute_groups @attribute_groups ||= Registry.new end @@ -64,5 +64,4 @@ module FactoryGirl def self.attribute_group_by_name(name) attribute_groups.find(name) end - end diff --git a/lib/factory_girl/attribute/attribute_group.rb b/lib/factory_girl/attribute/attribute_group.rb index e80dafd..3de0f7c 100644 --- a/lib/factory_girl/attribute/attribute_group.rb +++ b/lib/factory_girl/attribute/attribute_group.rb @@ -4,17 +4,18 @@ module FactoryGirl class AttributeGroup < Attribute def initialize(name, factory) super(name) - @factory=factory + @factory = factory end def add_to(proxy) - if @factory - @factory.attribute_group_by_name(name).attributes.each { |attr| attr.add_to(proxy) } - else - FactoryGirl.attribute_group_by_name(name).attributes.each { |attr| attr.add_to(proxy) } - end + attribute_group.attributes.each { |attr| attr.add_to(proxy) } + end + + private + + def attribute_group + (@factory || FactoryGirl).attribute_group_by_name(name) end end - end end diff --git a/lib/factory_girl/attribute/implicit.rb b/lib/factory_girl/attribute/implicit.rb index eae1e42..5b6baba 100644 --- a/lib/factory_girl/attribute/implicit.rb +++ b/lib/factory_girl/attribute/implicit.rb @@ -2,9 +2,9 @@ module FactoryGirl class Attribute class Implicit < Attribute - def initialize(name, factory=nil) + def initialize(name, factory = nil) super(name) - @factory=factory + @factory = factory end def add_to(proxy) diff --git a/lib/factory_girl/factory.rb b/lib/factory_girl/factory.rb index 3515480..33ccd17 100644 --- a/lib/factory_girl/factory.rb +++ b/lib/factory_girl/factory.rb @@ -14,7 +14,7 @@ module FactoryGirl class Factory attr_reader :name #:nodoc: attr_reader :attributes #:nodoc: - + def factory_name puts "WARNING: factory.factory_name is deprecated. Use factory.name instead." name @@ -44,36 +44,15 @@ module FactoryGirl @options[:class] ||= parent.class_name @options[:default_strategy] ||= parent.default_strategy - new_attributes = [] - parent.attributes.each do |attribute| - if attribute_defined?(attribute.name) - @attributes.delete_if do |attrib| - new_attributes << attrib.clone if attrib.name == attribute.name - end - else - new_attributes << attribute.clone - end - end - - @attributes.unshift *new_attributes - @attributes = @attributes.partition{|attr| attr.priority.zero? }.flatten + apply_attributes(parent.attributes) + sort_attributes! end - + def apply_attribute_groups(groups) - groups.reverse.map{ |name| attribute_group_by_name(name) }.each do |group| - new_attributes=[] - group.attributes.each do |attribute| - if attribute_defined?(attribute.name) - @attributes.delete_if do |attrib| - new_attributes << attrib.clone if attrib.name == attribute.name - end - else - new_attributes << attribute.clone - end - end - @attributes.unshift *new_attributes + groups.reverse.map { |name| attribute_group_by_name(name) }.each do |group| + apply_attributes(group.attributes) end - @attributes = @attributes.partition{|attr| attr.priority.zero?}.flatten + sort_attributes! end def define_attribute(attribute) @@ -87,7 +66,7 @@ module FactoryGirl end @attributes << attribute end - + def define_attribute_group(group) attribute_groups.add group end @@ -124,14 +103,14 @@ module FactoryGirl def attribute_group_by_name(name) return attribute_groups.find(name) if attribute_groups.registered?(name) - - if @parent.nil? - FactoryGirl::attribute_group_by_name(name) - else + + if @parent FactoryGirl.factory_by_name(@parent).attribute_group_by_name(name) + else + FactoryGirl.attribute_group_by_name(name) end end - + # Names for this factory, including aliases. # # Example: @@ -231,7 +210,27 @@ module FactoryGirl options end end - + + def apply_attributes(attributes_to_apply) + new_attributes=[] + + attributes_to_apply.each do |attribute| + if attribute_defined?(attribute.name) + @attributes.delete_if do |attrib| + new_attributes << attrib.clone if attrib.name == attribute.name + end + else + new_attributes << attribute.clone + end + end + + @attributes.unshift *new_attributes + end + + def sort_attributes! + @attributes = @attributes.partition {|attr| attr.priority.zero? }.flatten + end + def attribute_groups @attribute_groups ||= Registry.new end diff --git a/lib/factory_girl/syntax/default.rb b/lib/factory_girl/syntax/default.rb index 894945e..b341d53 100644 --- a/lib/factory_girl/syntax/default.rb +++ b/lib/factory_girl/syntax/default.rb @@ -30,11 +30,11 @@ module FactoryGirl factory(child_name, child_options.merge(:parent => name), &child_block) end end - + def sequence(name, start_value = 1, &block) FactoryGirl.register_sequence(Sequence.new(name, start_value, &block)) end - + def attribute_group(name, &block) FactoryGirl.register_attribute_group(AttributeGroup.new(name, &block)) end diff --git a/spec/acceptance/attribute_groups_spec.rb b/spec/acceptance/attribute_groups_spec.rb index 6923700..ebefc1d 100644 --- a/spec/acceptance/attribute_groups_spec.rb +++ b/spec/acceptance/attribute_groups_spec.rb @@ -3,10 +3,17 @@ require "acceptance/acceptance_helper" describe "an instance generated by a factory with multiple attribute groups" do before do - define_model("User", :name => :string, :admin => :boolean, :gender => :string, :email => :string) + define_model("User", + :name => :string, + :admin => :boolean, + :gender => :string, + :email => :string) + + FactoryGirl.define do + factory :user_without_admin_scoping, :class => User do + admin_attribute_group + end - FactoryGirl.define do - factory :user do name "John" @@ -14,6 +21,10 @@ describe "an instance generated by a factory with multiple attribute groups" do admin true end + attribute_group :admin_attribute_group do + admin true + end + attribute_group :male do name "Joe" gender "Male" @@ -24,30 +35,33 @@ describe "an instance generated by a factory with multiple attribute groups" do gender "Female" end - factory :admin, :attribute_groups=>[:admin] + factory :admin, :attribute_groups => [:admin] + factory :male_user do male end + factory :female, :attribute_groups => [:female] do attribute_group :admin do admin true name "Judy" end - factory :female_admin_judy, :attribute_groups=>[:admin] + + factory :female_admin_judy, :attribute_groups => [:admin] end - factory :female_admin, :attribute_groups => [:female, :admin] + + factory :female_admin, :attribute_groups => [:female, :admin] factory :female_after_male_admin, :attribute_groups => [:male, :female, :admin] factory :male_after_female_admin, :attribute_groups => [:female, :male, :admin] end - + attribute_group :email do email { "#{name}@example.com" } end - - factory :user_with_email, :class=>User, :attribute_groups=>[:email] do + + factory :user_with_email, :class => User, :attribute_groups => [:email] do name "Bill" end - end end @@ -55,71 +69,73 @@ describe "an instance generated by a factory with multiple attribute groups" do subject { FactoryGirl.create(:user) } its(:name) { should == "John" } its(:gender) { should be_nil } - it { should_not be_admin } + it { should_not be_admin } end - + context "the child class with one attribute group" do subject { FactoryGirl.create(:admin) } its(:name) { should == "John" } its(:gender) { should be_nil } - it { should be_admin } + it { should be_admin } end - + context "the other child class with one attribute group" do subject { FactoryGirl.create(:female) } its(:name) { should == "Jane" } its(:gender) { should == "Female" } - it { should_not be_admin } + it { should_not be_admin } end - + context "the child with multiple attribute groups" do subject { FactoryGirl.create(:female_admin) } its(:name) { should == "Jane" } its(:gender) { should == "Female" } - it { should be_admin } + it { should be_admin } end - + context "the child with multiple attribute groups and overridden attributes" do subject { FactoryGirl.create(:female_admin, :name => "Jill", :gender => nil) } its(:name) { should == "Jill" } its(:gender) { should be_nil } - it { should be_admin } + it { should be_admin } end - + context "the child with multiple attribute groups who override the same attribute" do context "when the male assigns name after female" do subject { FactoryGirl.create(:male_after_female_admin) } - its(:name) { should == "Joe" } its(:gender) { should == "Male" } - it { should be_admin } + it { should be_admin } end - + context "when the female assigns name after male" do subject { FactoryGirl.create(:female_after_male_admin) } - its(:name) { should == "Jane" } its(:gender) { should == "Female" } - it { should be_admin } + it { should be_admin } end end - + context "child class with scoped attribute group and inherited attribute group" do - subject { FactoryGirl.create(:female_admin_judy) } - its(:name) { should == "Judy" } + subject { FactoryGirl.create(:female_admin_judy) } + its(:name) { should == "Judy" } its(:gender) { should == "Female" } - it { should be_admin } + it { should be_admin } end - + context "factory using global attribute group" do - subject { FactoryGirl.create(:user_with_email) } - its(:name) { should == "Bill" } + subject { FactoryGirl.create(:user_with_email) } + its(:name) { should == "Bill" } its(:email) { should == "Bill@example.com"} end - + context "factory created with alternate syntax for specifying attribute group" do - subject { FactoryGirl.create(:male_user) } + subject { FactoryGirl.create(:male_user) } its(:gender) { should == "Male" } end + context "factory outside of scope" do + subject { FactoryGirl.create(:user_without_admin_scoping) } + it { expect { subject }.to raise_error(ArgumentError, "Not registered: admin_attribute_group") } + end end diff --git a/spec/factory_girl/definition_proxy_spec.rb b/spec/factory_girl/definition_proxy_spec.rb index 5213598..7de59a8 100644 --- a/spec/factory_girl/definition_proxy_spec.rb +++ b/spec/factory_girl/definition_proxy_spec.rb @@ -130,7 +130,7 @@ describe FactoryGirl::DefinitionProxy do name = :user attr = 'attribute' stub(attr).name { name } - mock(FactoryGirl::Attribute::Implicit).new(name,factory) { attr } + mock(FactoryGirl::Attribute::Implicit).new(name, factory) { attr } subject.send(name) factory.attributes.should include(attr) end