Skip to content

Commit

Permalink
Initial work on DeclarationList and cleaning up AttributeList
Browse files Browse the repository at this point in the history
  • Loading branch information
joshuaclayton committed Oct 31, 2011
1 parent cddfa92 commit f721bc6
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 100 deletions.
3 changes: 3 additions & 0 deletions lib/factory_girl.rb
@@ -1,3 +1,5 @@
require "active_support/core_ext/module/delegation"

require 'factory_girl/proxy'
require 'factory_girl/proxy/build'
require 'factory_girl/proxy/create'
Expand All @@ -11,6 +13,7 @@
require 'factory_girl/attribute/association'
require 'factory_girl/attribute/sequence'
require 'factory_girl/callback'
require 'factory_girl/declaration_list'
require 'factory_girl/declaration'
require 'factory_girl/declaration/static'
require 'factory_girl/declaration/dynamic'
Expand Down
51 changes: 26 additions & 25 deletions lib/factory_girl/attribute_list.rb
Expand Up @@ -2,14 +2,12 @@ module FactoryGirl
class AttributeList
include Enumerable

attr_reader :callbacks, :declarations

def initialize(name = nil)
@name = name
@attributes = {}
@declarations = []
@callbacks = []
@declarations = DeclarationList.new
@overridable = false
@compiled = false
end

def declare_attribute(declaration)
Expand All @@ -18,24 +16,21 @@ def declare_attribute(declaration)
end

def define_attribute(attribute)
if attribute.respond_to?(:factory) && attribute.factory == @name
raise AssociationDefinitionError, "Self-referencing association '#{attribute.name}' in '#{attribute.factory}'"
end

ensure_attribute_not_self_referencing! attribute
ensure_attribute_not_defined! attribute
add_attribute attribute
end

def add_callback(callback)
@callbacks << callback
add_attribute attribute
end

def each(&block)
flattened_attributes.each(&block)
end

def apply_attributes(attributes_to_apply)
attributes_to_apply.callbacks.reverse.each { |callback| prepend_callback(callback) }
def ensure_compiled
compile unless @compiled
end

def apply_attribute_list(attributes_to_apply)
new_attributes = []

attributes_to_apply.each do |attribute|
Expand All @@ -53,19 +48,19 @@ def apply_attributes(attributes_to_apply)
end

def overridable
@compiled = false
@overridable = true
end

def overridable?
@overridable
end
private

def size
to_a.size
def compile
@declarations.to_attributes.each do |attribute|
define_attribute(attribute)
end
@compiled = true
end

private

def add_attribute(attribute)
delete_attribute(attribute.name) if overridable?

Expand All @@ -74,10 +69,6 @@ def add_attribute(attribute)
attribute
end

def prepend_callback(callback)
@callbacks.unshift(callback)
end

def prepend_attributes(new_attributes)
new_attributes.group_by {|attr| attr.priority }.each do |priority, attributes|
@attributes[priority] ||= []
Expand All @@ -98,6 +89,12 @@ def ensure_attribute_not_defined!(attribute)
end
end

def ensure_attribute_not_self_referencing!(attribute)
if attribute.respond_to?(:factory) && attribute.factory == @name
raise AssociationDefinitionError, "Self-referencing association '#{attribute.name}' in '#{attribute.factory}'"
end
end

def attribute_defined?(attribute_name)
!!find_attribute(attribute_name)
end
Expand All @@ -115,5 +112,9 @@ def delete_attribute(attribute_name)
end
end
end

def overridable?
@overridable
end
end
end
15 changes: 15 additions & 0 deletions lib/factory_girl/declaration_list.rb
@@ -0,0 +1,15 @@
module FactoryGirl
class DeclarationList
def initialize
@definitions = []
end

def to_attributes
@definitions.inject([]) {|result, definition| result += definition.to_attributes }
end

def method_missing(name, *args, &block)
@definitions.send(name, *args, &block)
end
end
end
54 changes: 22 additions & 32 deletions lib/factory_girl/factory.rb
@@ -1,5 +1,4 @@
require "active_support/core_ext/hash/keys"
require "active_support/core_ext/module/delegation"
require "active_support/inflector"

module FactoryGirl
Expand All @@ -16,16 +15,20 @@ def initialize(name, options = {}) #:nodoc:
@default_strategy = options[:default_strategy]
@defined_traits = []
@attribute_list = build_attribute_list
@compiled = false
@callbacks = []
end

delegate :overridable?, :declarations, :declare_attribute, :define_attribute, :add_callback, :to => :@attribute_list
delegate :declare_attribute, :to => :@attribute_list

def factory_name
$stderr.puts "DEPRECATION WARNING: factory.factory_name is deprecated; use factory.name instead."
name
end

def add_callback(callback)
@callbacks << callback
end

def build_class #:nodoc:
@build_class ||= class_name.to_s.camelize.constantize
end
Expand All @@ -35,7 +38,6 @@ def default_strategy #:nodoc:
end

def allow_overrides
@compiled = false
@attribute_list.overridable
self
end
Expand All @@ -45,8 +47,6 @@ def define_trait(trait)
end

def run(proxy_class, overrides, &block) #:nodoc:
ensure_compiled

runner_options = {
:attributes => attributes,
:callbacks => callbacks,
Expand Down Expand Up @@ -111,8 +111,9 @@ def to_create(&block)
@to_create_block = block
end

def ensure_compiled
compile unless @compiled
def compile
parent.compile if parent
@attribute_list.ensure_compiled
end

protected
Expand All @@ -122,39 +123,24 @@ def class_name #:nodoc:
end

def attributes
ensure_compiled
compile
build_attribute_list.tap do |list|
@traits.reverse.map { |name| trait_by_name(name) }.each do |trait|
list.apply_attributes(trait.attributes)
traits.each do |trait|
list.apply_attribute_list(trait.attributes)
end

list.apply_attributes(@attribute_list)
list.apply_attributes(parent.attributes) if parent
list.apply_attribute_list(@attribute_list)
list.apply_attribute_list(parent.attributes) if parent
end
end

private

def callbacks
attributes.callbacks
[@callbacks].tap do |result|
result.unshift(*parent.callbacks) if parent
end.flatten
end

def compile
inherit_factory(parent) if parent

declarations.each do |declaration|
declaration.to_attributes.each do |attribute|
define_attribute(attribute)
end
end

@compiled = true
end

def inherit_factory(parent) #:nodoc:
parent.ensure_compiled
allow_overrides if parent.overridable?
end
private

def assert_valid_options(options)
options.assert_valid_keys(:class, :parent, :default_strategy, :aliases, :traits)
Expand All @@ -166,6 +152,10 @@ def assert_valid_options(options)
end
end

def traits
@traits.reverse.map { |name| trait_by_name(name) }
end

def trait_for(name)
@defined_traits.detect {|trait| trait.name == name }
end
Expand Down
2 changes: 1 addition & 1 deletion lib/factory_girl/step_definitions.rb
Expand Up @@ -95,7 +95,7 @@ def initialize(human_hash_to_attributes_hash, key, value)
World(FactoryGirlStepHelpers)

FactoryGirl.factories.each do |factory|
factory.ensure_compiled
factory.compile
factory.human_names.each do |human_name|
Given /^the following (?:#{human_name}|#{human_name.pluralize}) exists?:$/i do |table|
table.hashes.each do |human_hash|
Expand Down
2 changes: 1 addition & 1 deletion lib/factory_girl/syntax/default.rb
Expand Up @@ -46,7 +46,7 @@ def factory(name, options = {}, &block)
factory = FactoryGirl.factory_by_name(name).allow_overrides
proxy = FactoryGirl::DefinitionProxy.new(factory)
proxy.instance_eval(&block)
factory.ensure_compiled
factory.compile
end
end
end
Expand Down
19 changes: 3 additions & 16 deletions lib/factory_girl/trait.rb
Expand Up @@ -11,24 +11,11 @@ def initialize(name, &block) #:nodoc:
proxy.instance_eval(&@block) if block_given?
end

def declare_attribute(declaration)
@attribute_list.declare_attribute(declaration)
declaration
end

def add_callback(name, &block)
@attribute_list.add_callback(Callback.new(name, block))
end
delegate :declare_attribute, :to => :@attribute_list

def attributes
AttributeList.new.tap do |list|
@attribute_list.declarations.each do |declaration|
declaration.to_attributes.each do |attribute|
list.define_attribute(attribute)
end
end
list.apply_attributes @attribute_list
end
@attribute_list.ensure_compiled
@attribute_list
end

def names
Expand Down
30 changes: 9 additions & 21 deletions spec/factory_girl/attribute_list_spec.rb
Expand Up @@ -60,19 +60,7 @@
end
end

describe FactoryGirl::AttributeList, "#add_callback" do
let(:proxy_class) { mock("klass") }
let(:proxy) { FactoryGirl::Proxy.new(proxy_class) }

it "allows for defining adding a callback" do
subject.add_callback(FactoryGirl::Callback.new(:after_create, lambda { "Called after_create" }))

subject.callbacks.first.name.should == :after_create
subject.callbacks.first.run(nil, nil).should == "Called after_create"
end
end

describe FactoryGirl::AttributeList, "#apply_attributes" do
describe FactoryGirl::AttributeList, "#apply_attribute_list" do
let(:full_name_attribute) { FactoryGirl::Attribute::Static.new(:full_name, "John Adams", false) }
let(:city_attribute) { FactoryGirl::Attribute::Static.new(:city, "Boston", false) }
let(:email_attribute) { FactoryGirl::Attribute::Dynamic.new(:email, false, lambda {|model| "#{model.full_name}@example.com" }) }
Expand All @@ -86,28 +74,28 @@ def list(*attributes)

it "prepends applied attributes" do
subject.define_attribute(full_name_attribute)
subject.apply_attributes(list(city_attribute))
subject.apply_attribute_list(list(city_attribute))
subject.to_a.should == [city_attribute, full_name_attribute]
end

it "moves non-static attributes to the end of the list" do
subject.define_attribute(full_name_attribute)
subject.apply_attributes(list(city_attribute, email_attribute))
subject.apply_attribute_list(list(city_attribute, email_attribute))
subject.to_a.should == [city_attribute, full_name_attribute, email_attribute]
end

it "maintains order of non-static attributes" do
subject.define_attribute(full_name_attribute)
subject.define_attribute(login_attribute)
subject.apply_attributes(list(city_attribute, email_attribute))
subject.apply_attribute_list(list(city_attribute, email_attribute))
subject.to_a.should == [city_attribute, full_name_attribute, email_attribute, login_attribute]
end

it "doesn't overwrite attributes that are already defined" do
subject.define_attribute(full_name_attribute)
attribute_with_same_name = FactoryGirl::Attribute::Static.new(:full_name, "Benjamin Franklin", false)

subject.apply_attributes(list(attribute_with_same_name))
subject.apply_attribute_list(list(attribute_with_same_name))
subject.to_a.should == [full_name_attribute]
end

Expand All @@ -116,28 +104,28 @@ def list(*attributes)

it "prepends applied attributes" do
subject.define_attribute(full_name_attribute)
subject.apply_attributes(list(city_attribute))
subject.apply_attribute_list(list(city_attribute))
subject.to_a.should == [city_attribute, full_name_attribute]
end

it "moves non-static attributes to the end of the list" do
subject.define_attribute(full_name_attribute)
subject.apply_attributes(list(city_attribute, email_attribute))
subject.apply_attribute_list(list(city_attribute, email_attribute))
subject.to_a.should == [city_attribute, full_name_attribute, email_attribute]
end

it "maintains order of non-static attributes" do
subject.define_attribute(full_name_attribute)
subject.define_attribute(login_attribute)
subject.apply_attributes(list(city_attribute, email_attribute))
subject.apply_attribute_list(list(city_attribute, email_attribute))
subject.to_a.should == [city_attribute, full_name_attribute, email_attribute, login_attribute]
end

it "overwrites attributes that are already defined" do
subject.define_attribute(full_name_attribute)
attribute_with_same_name = FactoryGirl::Attribute::Static.new(:full_name, "Benjamin Franklin", false)

subject.apply_attributes(list(attribute_with_same_name))
subject.apply_attribute_list(list(attribute_with_same_name))
subject.to_a.should == [attribute_with_same_name]
end
end
Expand Down

0 comments on commit f721bc6

Please sign in to comment.