Skip to content

Commit

Permalink
first attribute groups specs passing
Browse files Browse the repository at this point in the history
  • Loading branch information
twalpole authored and joshuaclayton committed Aug 12, 2011
1 parent e39d6e6 commit 36fb3fb
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 14 deletions.
1 change: 1 addition & 0 deletions lib/factory_girl.rb
Expand Up @@ -13,6 +13,7 @@
require 'factory_girl/attribute/sequence'
require 'factory_girl/attribute/implicit'
require 'factory_girl/sequence'
require 'factory_girl/attribute_group'
require 'factory_girl/aliases'
require 'factory_girl/definition_proxy'
require 'factory_girl/syntax/methods'
Expand Down
29 changes: 29 additions & 0 deletions lib/factory_girl/attribute_group.rb
@@ -0,0 +1,29 @@
module FactoryGirl

class AttributeGroup
attr_reader :name
attr_reader :attributes

def initialize(name, &block) #:nodoc:
@name = name
@attributes = []
proxy = FactoryGirl::DefinitionProxy.new(self)
proxy.instance_eval(&block) if block_given?
end

def define_attribute(attribute)
name = attribute.name
if attribute_defined?(name)
raise AttributeDefinitionError, "Attribute already defined: #{name}"
end
@attributes << attribute
end

private

def attribute_defined? (name)
!@attributes.detect {|attr| attr.name == name && !attr.is_a?(Attribute::Callback) }.nil?
end

end
end
5 changes: 4 additions & 1 deletion lib/factory_girl/definition_proxy.rb
Expand Up @@ -7,7 +7,6 @@ class DefinitionProxy
end

attr_reader :child_factories

def initialize(factory)
@factory = factory
@child_factories = []
Expand Down Expand Up @@ -154,5 +153,9 @@ def to_create(&block)
def factory(name, options = {}, &block)
@child_factories << [name, options, block]
end

def attr_group(name, &block)
@factory.define_attribute_group(AttributeGroup.new(name, &block))
end
end
end
37 changes: 34 additions & 3 deletions lib/factory_girl/factory.rb
Expand Up @@ -14,7 +14,8 @@ class DuplicateDefinitionError < RuntimeError
class Factory
attr_reader :name #:nodoc:
attr_reader :attributes #:nodoc:

attr_reader :attribute_groups #:nodoc:

def factory_name
puts "WARNING: factory.factory_name is deprecated. Use factory.name instead."
name
Expand All @@ -37,6 +38,7 @@ def initialize(name, options = {}) #:nodoc:
@name = factory_name_for(name)
@options = options
@attributes = []
@attribute_groups = {}
end

def inherit_from(parent) #:nodoc:
Expand All @@ -55,7 +57,24 @@ def inherit_from(parent) #:nodoc:
end

@attributes.unshift *new_attributes
@attributes = @attributes.partition {|attr| attr.priority.zero? }.flatten
@attributes = @attributes.partition{|attr| attr.priority.zero? }.flatten
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
@attributes = @attributes.partition{|attr| attr.priority.zero?}.flatten
end
end

def define_attribute(attribute)
Expand All @@ -69,6 +88,10 @@ def define_attribute(attribute)
end
@attributes << attribute
end

def define_attribute_group(group)
@attribute_groups[group.name.to_sym] = group
end

def add_callback(name, &block)
unless [:after_build, :after_create, :after_stub].include?(name.to_sym)
Expand Down Expand Up @@ -100,6 +123,14 @@ def associations
attributes.select {|attribute| attribute.association? }
end

def attribute_group_by_name(name)
name=name.to_sym
group = @attribute_groups[name]
unless @options[:parent].nil?
group ||= FactoryGirl.factory_by_name(@options[:parent]).attribute_group_by_name(name)
end
group
end
# Names for this factory, including aliases.
#
# Example:
Expand Down Expand Up @@ -159,7 +190,7 @@ def attribute_defined? (name)
end

def assert_valid_options(options)
invalid_keys = options.keys - [:class, :parent, :default_strategy, :aliases]
invalid_keys = options.keys - [:class, :parent, :default_strategy, :aliases, :attr_groups]
unless invalid_keys == []
raise ArgumentError, "Unknown arguments: #{invalid_keys.inspect}"
end
Expand Down
7 changes: 6 additions & 1 deletion lib/factory_girl/syntax/default.rb
Expand Up @@ -16,6 +16,11 @@ def factory(name, options = {}, &block)
factory = Factory.new(name, options)
proxy = FactoryGirl::DefinitionProxy.new(factory)
proxy.instance_eval(&block) if block_given?

if groups = options.delete(:attr_groups)
factory.apply_attribute_groups(groups)
end

if parent = options.delete(:parent)
factory.inherit_from(FactoryGirl.factory_by_name(parent))
end
Expand All @@ -25,7 +30,7 @@ def factory(name, options = {}, &block)
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
Expand Down
19 changes: 10 additions & 9 deletions spec/acceptance/attribute_groups_spec.rb
Expand Up @@ -14,6 +14,7 @@
end

attr_group :male do
name "Joe"
gender "Male"
end

Expand All @@ -38,47 +39,47 @@
its(:gender) { should be_nil }
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 }
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 }
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 }
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 }
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 == "John" }
its(:name) { should == "Joe" }
its(:gender) { should == "Male" }
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 }
Expand Down

0 comments on commit 36fb3fb

Please sign in to comment.