factory_girl_kibiz0r 2.0.0.beta2
Sign up to get free protection for your applications and to get access to all the features.
- data/Appraisals +12 -0
- data/CONTRIBUTION_GUIDELINES.md +9 -0
- data/Changelog +29 -0
- data/GETTING_STARTED.md +246 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +62 -0
- data/LICENSE +19 -0
- data/README.md +64 -0
- data/Rakefile +54 -0
- data/features/factory_girl_steps.feature +151 -0
- data/features/step_definitions/database_steps.rb +20 -0
- data/features/support/env.rb +6 -0
- data/features/support/factories.rb +84 -0
- data/lib/factory_girl/aliases.rb +21 -0
- data/lib/factory_girl/attribute/association.rb +24 -0
- data/lib/factory_girl/attribute/callback.rb +16 -0
- data/lib/factory_girl/attribute/dynamic.rb +21 -0
- data/lib/factory_girl/attribute/implicit.rb +36 -0
- data/lib/factory_girl/attribute/list.rb +19 -0
- data/lib/factory_girl/attribute/sequence.rb +16 -0
- data/lib/factory_girl/attribute/static.rb +18 -0
- data/lib/factory_girl/attribute.rb +42 -0
- data/lib/factory_girl/definition_proxy.rb +147 -0
- data/lib/factory_girl/deprecated.rb +18 -0
- data/lib/factory_girl/factory.rb +196 -0
- data/lib/factory_girl/find_definitions.rb +23 -0
- data/lib/factory_girl/proxy/attributes_for.rb +21 -0
- data/lib/factory_girl/proxy/build.rb +36 -0
- data/lib/factory_girl/proxy/create.rb +16 -0
- data/lib/factory_girl/proxy/stub.rb +64 -0
- data/lib/factory_girl/proxy.rb +87 -0
- data/lib/factory_girl/rails2.rb +1 -0
- data/lib/factory_girl/registry.rb +45 -0
- data/lib/factory_girl/sequence.rb +31 -0
- data/lib/factory_girl/step_definitions.rb +60 -0
- data/lib/factory_girl/syntax/blueprint.rb +42 -0
- data/lib/factory_girl/syntax/default.rb +33 -0
- data/lib/factory_girl/syntax/generate.rb +73 -0
- data/lib/factory_girl/syntax/make.rb +41 -0
- data/lib/factory_girl/syntax/methods.rb +86 -0
- data/lib/factory_girl/syntax/sham.rb +45 -0
- data/lib/factory_girl/syntax/vintage.rb +152 -0
- data/lib/factory_girl/syntax.rb +12 -0
- data/lib/factory_girl/version.rb +4 -0
- data/lib/factory_girl.rb +54 -0
- data/spec/acceptance/acceptance_helper.rb +11 -0
- data/spec/acceptance/attribute_aliases_spec.rb +26 -0
- data/spec/acceptance/attributes_for_spec.rb +48 -0
- data/spec/acceptance/build_spec.rb +35 -0
- data/spec/acceptance/build_stubbed_spec.rb +79 -0
- data/spec/acceptance/callbacks_spec.rb +53 -0
- data/spec/acceptance/create_spec.rb +67 -0
- data/spec/acceptance/default_strategy_spec.rb +27 -0
- data/spec/acceptance/definition_spec.rb +28 -0
- data/spec/acceptance/parent_spec.rb +39 -0
- data/spec/acceptance/sequence_spec.rb +34 -0
- data/spec/acceptance/syntax/blueprint_spec.rb +32 -0
- data/spec/acceptance/syntax/generate_spec.rb +60 -0
- data/spec/acceptance/syntax/make_spec.rb +35 -0
- data/spec/acceptance/syntax/sham_spec.rb +44 -0
- data/spec/acceptance/syntax/vintage_spec.rb +224 -0
- data/spec/factory_girl/aliases_spec.rb +33 -0
- data/spec/factory_girl/attribute/association_spec.rb +33 -0
- data/spec/factory_girl/attribute/callback_spec.rb +23 -0
- data/spec/factory_girl/attribute/dynamic_spec.rb +60 -0
- data/spec/factory_girl/attribute/implicit_spec.rb +50 -0
- data/spec/factory_girl/attribute/sequence_spec.rb +21 -0
- data/spec/factory_girl/attribute/static_spec.rb +29 -0
- data/spec/factory_girl/attribute_spec.rb +39 -0
- data/spec/factory_girl/definition_proxy_spec.rb +129 -0
- data/spec/factory_girl/deprecated_spec.rb +66 -0
- data/spec/factory_girl/factory_spec.rb +374 -0
- data/spec/factory_girl/find_definitions_spec.rb +100 -0
- data/spec/factory_girl/proxy/attributes_for_spec.rb +52 -0
- data/spec/factory_girl/proxy/build_spec.rb +86 -0
- data/spec/factory_girl/proxy/create_spec.rb +107 -0
- data/spec/factory_girl/proxy/stub_spec.rb +80 -0
- data/spec/factory_girl/proxy_spec.rb +102 -0
- data/spec/factory_girl/registry_spec.rb +83 -0
- data/spec/factory_girl/sequence_spec.rb +96 -0
- data/spec/factory_girl_spec.rb +17 -0
- data/spec/spec_helper.rb +93 -0
- metadata +294 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
ActiveRecord::Base.establish_connection(
|
2
|
+
:adapter => 'sqlite3',
|
3
|
+
:database => File.join(File.dirname(__FILE__), 'test.db')
|
4
|
+
)
|
5
|
+
|
6
|
+
class CreateSchema < ActiveRecord::Migration
|
7
|
+
def self.up
|
8
|
+
create_table :posts, :force => true do |t|
|
9
|
+
t.integer :author_id
|
10
|
+
t.integer :category_id
|
11
|
+
t.string :title
|
12
|
+
t.string :body
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table :category_groups, :force => true do |t|
|
16
|
+
t.string :name
|
17
|
+
end
|
18
|
+
|
19
|
+
create_table :categories, :force => true do |t|
|
20
|
+
t.integer :category_group_id
|
21
|
+
t.string :name
|
22
|
+
end
|
23
|
+
|
24
|
+
create_table :users, :force => true do |t|
|
25
|
+
t.string :name
|
26
|
+
t.boolean :admin, :default => false, :null => false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
CreateSchema.suppress_messages { CreateSchema.migrate(:up) }
|
32
|
+
|
33
|
+
class User < ActiveRecord::Base
|
34
|
+
end
|
35
|
+
|
36
|
+
class CategoryGroup < ActiveRecord::Base
|
37
|
+
end
|
38
|
+
|
39
|
+
class Category < ActiveRecord::Base
|
40
|
+
belongs_to :category_group
|
41
|
+
end
|
42
|
+
|
43
|
+
class Post < ActiveRecord::Base
|
44
|
+
belongs_to :author, :class_name => 'User'
|
45
|
+
belongs_to :category
|
46
|
+
end
|
47
|
+
|
48
|
+
class NonActiveRecord
|
49
|
+
end
|
50
|
+
|
51
|
+
FactoryGirl.define do
|
52
|
+
# To make sure the step defs work with an email
|
53
|
+
sequence :email do |n|
|
54
|
+
"email#{n}@example.com"
|
55
|
+
end
|
56
|
+
|
57
|
+
factory :user do
|
58
|
+
end
|
59
|
+
|
60
|
+
factory :admin_user, :parent => :user do
|
61
|
+
admin true
|
62
|
+
end
|
63
|
+
|
64
|
+
factory :category do
|
65
|
+
name "programming"
|
66
|
+
category_group
|
67
|
+
end
|
68
|
+
|
69
|
+
factory :category_group do
|
70
|
+
name "tecnhology"
|
71
|
+
end
|
72
|
+
|
73
|
+
factory :post do
|
74
|
+
association :author, :factory => :user
|
75
|
+
category
|
76
|
+
end
|
77
|
+
|
78
|
+
# This is here to ensure that factory step definitions don't raise for a non-AR factory
|
79
|
+
factory :non_active_record do
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
require 'factory_girl/step_definitions'
|
84
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
|
3
|
+
class << self
|
4
|
+
attr_accessor :aliases #:nodoc:
|
5
|
+
end
|
6
|
+
self.aliases = [
|
7
|
+
[/(.+)_id/, '\1'],
|
8
|
+
[/(.*)/, '\1_id']
|
9
|
+
]
|
10
|
+
|
11
|
+
def self.aliases_for(attribute) #:nodoc:
|
12
|
+
aliases.collect do |params|
|
13
|
+
pattern, replace = *params
|
14
|
+
if pattern.match(attribute.to_s)
|
15
|
+
attribute.to_s.sub(pattern, replace).to_sym
|
16
|
+
else
|
17
|
+
nil
|
18
|
+
end
|
19
|
+
end.compact << attribute
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
class Attribute #:nodoc:
|
3
|
+
|
4
|
+
class Association < Attribute #:nodoc:
|
5
|
+
|
6
|
+
attr_reader :factory
|
7
|
+
|
8
|
+
def initialize(name, factory, overrides)
|
9
|
+
super(name)
|
10
|
+
@factory = factory
|
11
|
+
@overrides = overrides
|
12
|
+
end
|
13
|
+
|
14
|
+
def add_to(proxy)
|
15
|
+
proxy.associate(name, @factory, @overrides)
|
16
|
+
end
|
17
|
+
|
18
|
+
def association?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
class Attribute #:nodoc:
|
3
|
+
|
4
|
+
class Callback < Attribute #:nodoc:
|
5
|
+
def initialize(name, block)
|
6
|
+
@name = name.to_sym
|
7
|
+
@block = block
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_to(proxy)
|
11
|
+
proxy.add_callback(name, @block)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
class Attribute #:nodoc:
|
3
|
+
|
4
|
+
class Dynamic < Attribute #:nodoc:
|
5
|
+
def initialize(name, block)
|
6
|
+
super(name)
|
7
|
+
@block = block
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_to(proxy)
|
11
|
+
value = @block.arity == 1 ? @block.call(proxy) : proxy.instance_exec(&@block)
|
12
|
+
if FactoryGirl::Sequence === value
|
13
|
+
raise SequenceAbuseError
|
14
|
+
end
|
15
|
+
method = !ignored? ? :set : :ignore
|
16
|
+
proxy.send(method, name, value)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
class Attribute
|
3
|
+
|
4
|
+
class Implicit < Attribute
|
5
|
+
def initialize(name)
|
6
|
+
super(name)
|
7
|
+
end
|
8
|
+
|
9
|
+
def add_to(proxy)
|
10
|
+
implementation.add_to(proxy)
|
11
|
+
end
|
12
|
+
|
13
|
+
def association?
|
14
|
+
implementation.association?
|
15
|
+
end
|
16
|
+
|
17
|
+
def factory
|
18
|
+
name
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def implementation
|
24
|
+
@implementation ||= resolve_name
|
25
|
+
end
|
26
|
+
|
27
|
+
def resolve_name
|
28
|
+
if FactoryGirl.factories.registered?(name)
|
29
|
+
Attribute::Association.new(name, name, {})
|
30
|
+
else
|
31
|
+
Attribute::Sequence.new(name, name)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
class Attribute
|
3
|
+
|
4
|
+
class Sequence < Attribute
|
5
|
+
def initialize(name, sequence)
|
6
|
+
super(name)
|
7
|
+
@sequence = sequence
|
8
|
+
end
|
9
|
+
|
10
|
+
def add_to(proxy)
|
11
|
+
proxy.set(name, FactoryGirl.generate(@sequence))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
class Attribute #:nodoc:
|
3
|
+
|
4
|
+
class Static < Attribute #:nodoc:
|
5
|
+
|
6
|
+
def initialize(name, value)
|
7
|
+
super(name)
|
8
|
+
@value = value
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_to(proxy)
|
12
|
+
method = !ignored? ? :set : :ignore
|
13
|
+
proxy.send(method, name, @value)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
|
3
|
+
# Raised when defining an invalid attribute:
|
4
|
+
# * Defining an attribute which has a name ending in "="
|
5
|
+
# * Defining an attribute with both a static and lazy value
|
6
|
+
# * Defining an attribute twice in the same factory
|
7
|
+
class AttributeDefinitionError < RuntimeError
|
8
|
+
end
|
9
|
+
|
10
|
+
class Attribute #:nodoc:
|
11
|
+
|
12
|
+
attr_reader :name
|
13
|
+
|
14
|
+
def initialize(name)
|
15
|
+
@name = name.to_sym
|
16
|
+
|
17
|
+
if @name.to_s =~ /=$/
|
18
|
+
attribute_name = $`
|
19
|
+
raise AttributeDefinitionError,
|
20
|
+
"factory_girl uses 'f.#{attribute_name} value' syntax " +
|
21
|
+
"rather than 'f.#{attribute_name} = value'"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def add_to(proxy)
|
26
|
+
end
|
27
|
+
|
28
|
+
def association?
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
def ignore
|
33
|
+
@ignored = true
|
34
|
+
self
|
35
|
+
end
|
36
|
+
|
37
|
+
def ignored?
|
38
|
+
@ignored
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,147 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
class DefinitionProxy
|
3
|
+
instance_methods.each do |method|
|
4
|
+
undef_method(method) unless method =~ /(^__|^nil\?$|^send$|^object_id$|^extend$|^instance_eval$)/
|
5
|
+
end
|
6
|
+
|
7
|
+
def initialize(factory)
|
8
|
+
@factory = factory
|
9
|
+
end
|
10
|
+
|
11
|
+
# Adds an attribute that should be assigned on generated instances for this
|
12
|
+
# factory.
|
13
|
+
#
|
14
|
+
# This method should be called with either a value or block, but not both. If
|
15
|
+
# called with a block, the attribute will be generated "lazily," whenever an
|
16
|
+
# instance is generated. Lazy attribute blocks will not be called if that
|
17
|
+
# attribute is overridden for a specific instance.
|
18
|
+
#
|
19
|
+
# When defining lazy attributes, an instance of FactoryGirl::Proxy will
|
20
|
+
# be yielded, allowing associations to be built using the correct build
|
21
|
+
# strategy.
|
22
|
+
#
|
23
|
+
# Arguments:
|
24
|
+
# * name: +Symbol+ or +String+
|
25
|
+
# The name of this attribute. This will be assigned using "name=" for
|
26
|
+
# generated instances.
|
27
|
+
# * value: +Object+
|
28
|
+
# If no block is given, this value will be used for this attribute.
|
29
|
+
def add_attribute(name, value = nil, &block)
|
30
|
+
if block_given?
|
31
|
+
if value
|
32
|
+
raise AttributeDefinitionError, "Both value and block given"
|
33
|
+
else
|
34
|
+
attribute = Attribute::Dynamic.new(name, block)
|
35
|
+
end
|
36
|
+
else
|
37
|
+
attribute = Attribute::Static.new(name, value)
|
38
|
+
end
|
39
|
+
|
40
|
+
@factory.define_attribute(attribute)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Calls add_attribute using the missing method name as the name of the
|
44
|
+
# attribute, so that:
|
45
|
+
#
|
46
|
+
# factory :user do
|
47
|
+
# name 'Billy Idol'
|
48
|
+
# end
|
49
|
+
#
|
50
|
+
# and:
|
51
|
+
#
|
52
|
+
# factory :user do
|
53
|
+
# add_attribute :name, 'Billy Idol'
|
54
|
+
# end
|
55
|
+
#
|
56
|
+
# are equivilent.
|
57
|
+
#
|
58
|
+
# If no argument or block is given, factory_girl will look for a sequence
|
59
|
+
# or association with the same name. This means that:
|
60
|
+
#
|
61
|
+
# factory :user do
|
62
|
+
# email { create(:email) }
|
63
|
+
# association :account
|
64
|
+
# end
|
65
|
+
#
|
66
|
+
# and:
|
67
|
+
#
|
68
|
+
# factory :user do
|
69
|
+
# email
|
70
|
+
# account
|
71
|
+
# end
|
72
|
+
#
|
73
|
+
# are equivilent.
|
74
|
+
def method_missing(name, *args, &block)
|
75
|
+
if args.empty? && block.nil?
|
76
|
+
@factory.define_attribute(Attribute::Implicit.new(name))
|
77
|
+
else
|
78
|
+
add_attribute(name, *args, &block)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Adds an attribute that will have unique values generated by a sequence with
|
83
|
+
# a specified format.
|
84
|
+
#
|
85
|
+
# The result of:
|
86
|
+
# factory :user do
|
87
|
+
# sequence(:email) { |n| "person#{n}@example.com" }
|
88
|
+
# end
|
89
|
+
#
|
90
|
+
# Is equal to:
|
91
|
+
# sequence(:email) { |n| "person#{n}@example.com" }
|
92
|
+
#
|
93
|
+
# factory :user do
|
94
|
+
# email { FactoryGirl.create(:email) }
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
# Except that no globally available sequence will be defined.
|
98
|
+
def sequence(name, start_value = 1, &block)
|
99
|
+
sequence = Sequence.new(name, start_value, &block)
|
100
|
+
add_attribute(name) { sequence.next }
|
101
|
+
end
|
102
|
+
|
103
|
+
# Adds an attribute that builds an association. The associated instance will
|
104
|
+
# be built using the same build strategy as the parent instance.
|
105
|
+
#
|
106
|
+
# Example:
|
107
|
+
# factory :user do
|
108
|
+
# name 'Joey'
|
109
|
+
# end
|
110
|
+
#
|
111
|
+
# factory :post do
|
112
|
+
# association :author, :factory => :user
|
113
|
+
# end
|
114
|
+
#
|
115
|
+
# Arguments:
|
116
|
+
# * name: +Symbol+
|
117
|
+
# The name of this attribute.
|
118
|
+
# * options: +Hash+
|
119
|
+
#
|
120
|
+
# Options:
|
121
|
+
# * factory: +Symbol+ or +String+
|
122
|
+
# The name of the factory to use when building the associated instance.
|
123
|
+
# If no name is given, the name of the attribute is assumed to be the
|
124
|
+
# name of the factory. For example, a "user" association will by
|
125
|
+
# default use the "user" factory.
|
126
|
+
def association(name, options = {})
|
127
|
+
factory_name = options.delete(:factory) || name
|
128
|
+
@factory.define_attribute(Attribute::Association.new(name, factory_name, options))
|
129
|
+
end
|
130
|
+
|
131
|
+
def after_build(&block)
|
132
|
+
@factory.add_callback(:after_build, &block)
|
133
|
+
end
|
134
|
+
|
135
|
+
def after_create(&block)
|
136
|
+
@factory.add_callback(:after_create, &block)
|
137
|
+
end
|
138
|
+
|
139
|
+
def after_stub(&block)
|
140
|
+
@factory.add_callback(:after_stub, &block)
|
141
|
+
end
|
142
|
+
|
143
|
+
def to_create(&block)
|
144
|
+
@factory.to_create(&block)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Factory
|
2
|
+
def self.method_missing(name, *args, &block)
|
3
|
+
if FactoryGirl.respond_to?(name)
|
4
|
+
$stderr.puts "DEPRECATION WARNING: Change Factory.#{name} to FactoryGirl.#{name}"
|
5
|
+
FactoryGirl.send(name, *args, &block)
|
6
|
+
else
|
7
|
+
super(name, *args, &block)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.const_missing(name)
|
12
|
+
if FactoryGirl.const_defined?(name)
|
13
|
+
FactoryGirl.const_get(name)
|
14
|
+
else
|
15
|
+
super(name)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,196 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
# Raised when a factory is defined that attempts to instantiate itself.
|
3
|
+
class AssociationDefinitionError < RuntimeError
|
4
|
+
end
|
5
|
+
|
6
|
+
# Raised when a callback is defined that has an invalid name
|
7
|
+
class InvalidCallbackNameError < RuntimeError
|
8
|
+
end
|
9
|
+
|
10
|
+
# Raised when a factory is defined with the same name as a previously-defined factory.
|
11
|
+
class DuplicateDefinitionError < RuntimeError
|
12
|
+
end
|
13
|
+
|
14
|
+
class Factory
|
15
|
+
attr_reader :name #:nodoc:
|
16
|
+
attr_reader :attributes #:nodoc:
|
17
|
+
|
18
|
+
def factory_name
|
19
|
+
puts "WARNING: factory.factory_name is deprecated. Use factory.name instead."
|
20
|
+
name
|
21
|
+
end
|
22
|
+
|
23
|
+
def class_name #:nodoc:
|
24
|
+
@options[:class] || name
|
25
|
+
end
|
26
|
+
|
27
|
+
def build_class #:nodoc:
|
28
|
+
@build_class ||= class_for(class_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def default_strategy #:nodoc:
|
32
|
+
@options[:default_strategy] || :create
|
33
|
+
end
|
34
|
+
|
35
|
+
def initialize(name, options = {}) #:nodoc:
|
36
|
+
assert_valid_options(options)
|
37
|
+
@name = factory_name_for(name)
|
38
|
+
@options = options
|
39
|
+
@attributes = Attribute::List.new
|
40
|
+
end
|
41
|
+
|
42
|
+
def inherit_from(parent) #:nodoc:
|
43
|
+
@options[:class] ||= parent.class_name
|
44
|
+
@options[:default_strategy] ||= parent.default_strategy
|
45
|
+
|
46
|
+
new_attributes = []
|
47
|
+
parent.attributes.each do |attribute|
|
48
|
+
unless attribute_defined?(attribute.name)
|
49
|
+
new_attributes << attribute.clone
|
50
|
+
end
|
51
|
+
end
|
52
|
+
@attributes.unshift *new_attributes
|
53
|
+
end
|
54
|
+
|
55
|
+
def define_attribute(attribute)
|
56
|
+
name = attribute.name
|
57
|
+
# TODO: move these checks into Attribute
|
58
|
+
if attribute_defined?(name)
|
59
|
+
raise AttributeDefinitionError, "Attribute already defined: #{name}"
|
60
|
+
end
|
61
|
+
if attribute.respond_to?(:factory) && attribute.factory == self.name
|
62
|
+
raise AssociationDefinitionError, "Self-referencing association '#{name}' in factory '#{self.name}'"
|
63
|
+
end
|
64
|
+
@attributes << attribute
|
65
|
+
end
|
66
|
+
|
67
|
+
def add_callback(name, &block)
|
68
|
+
unless [:after_build, :after_create, :after_stub].include?(name.to_sym)
|
69
|
+
raise InvalidCallbackNameError, "#{name} is not a valid callback name. Valid callback names are :after_build, :after_create, and :after_stub"
|
70
|
+
end
|
71
|
+
@attributes << Attribute::Callback.new(name.to_sym, block)
|
72
|
+
end
|
73
|
+
|
74
|
+
def run(proxy_class, overrides) #:nodoc:
|
75
|
+
proxy = proxy_class.new(build_class)
|
76
|
+
overrides = symbolize_keys(overrides)
|
77
|
+
overrides.each {|attr, val| proxy.set(attr, val) }
|
78
|
+
passed_keys = overrides.keys.collect {|k| FactoryGirl.aliases_for(k) }.flatten
|
79
|
+
@attributes.each do |attribute|
|
80
|
+
unless passed_keys.include?(attribute.name)
|
81
|
+
attribute.add_to(proxy)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
proxy.result(@to_create_block)
|
85
|
+
end
|
86
|
+
|
87
|
+
def human_name(*args, &block)
|
88
|
+
name.to_s.gsub('_', ' ')
|
89
|
+
end
|
90
|
+
|
91
|
+
def associations
|
92
|
+
attributes.select {|attribute| attribute.association? }
|
93
|
+
end
|
94
|
+
|
95
|
+
# Names for this factory, including aliases.
|
96
|
+
#
|
97
|
+
# Example:
|
98
|
+
#
|
99
|
+
# factory :user, :aliases => [:author] do
|
100
|
+
# # ...
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
# FactoryGirl.create(:author).class
|
104
|
+
# # => User
|
105
|
+
#
|
106
|
+
# Because an attribute defined without a value or block will build an
|
107
|
+
# association with the same name, this allows associations to be defined
|
108
|
+
# without factories, such as:
|
109
|
+
#
|
110
|
+
# factory :user, :aliases => [:author] do
|
111
|
+
# # ...
|
112
|
+
# end
|
113
|
+
#
|
114
|
+
# factory :post do
|
115
|
+
# author
|
116
|
+
# end
|
117
|
+
#
|
118
|
+
# FactoryGirl.create(:post).author.class
|
119
|
+
# # => User
|
120
|
+
def names
|
121
|
+
[name] + (@options[:aliases] || [])
|
122
|
+
end
|
123
|
+
|
124
|
+
def to_create(&block)
|
125
|
+
@to_create_block = block
|
126
|
+
end
|
127
|
+
|
128
|
+
private
|
129
|
+
|
130
|
+
def class_for (class_or_to_s)
|
131
|
+
if class_or_to_s.respond_to?(:to_sym)
|
132
|
+
class_name = variable_name_to_class_name(class_or_to_s)
|
133
|
+
class_name.split('::').inject(Object) do |object, string|
|
134
|
+
object.const_get(string)
|
135
|
+
end
|
136
|
+
else
|
137
|
+
class_or_to_s
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
def factory_name_for(class_or_to_s)
|
142
|
+
if class_or_to_s.respond_to?(:to_sym)
|
143
|
+
class_or_to_s.to_sym
|
144
|
+
else
|
145
|
+
class_name_to_variable_name(class_or_to_s).to_sym
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def attribute_defined? (name)
|
150
|
+
!@attributes.detect {|attr| attr.name == name && !attr.is_a?(Attribute::Callback) }.nil?
|
151
|
+
end
|
152
|
+
|
153
|
+
def assert_valid_options(options)
|
154
|
+
invalid_keys = options.keys - [:class, :parent, :default_strategy, :aliases]
|
155
|
+
unless invalid_keys == []
|
156
|
+
raise ArgumentError, "Unknown arguments: #{invalid_keys.inspect}"
|
157
|
+
end
|
158
|
+
if options[:default_strategy]
|
159
|
+
assert_valid_strategy(options[:default_strategy])
|
160
|
+
puts "WARNING: default_strategy is deprecated."
|
161
|
+
puts "Override to_create if you need to prevent a call to #save!."
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def assert_valid_strategy(strategy)
|
166
|
+
unless Proxy.const_defined? variable_name_to_class_name(strategy)
|
167
|
+
raise ArgumentError, "Unknown strategy: #{strategy}"
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
# Based on ActiveSupport's underscore inflector
|
172
|
+
def class_name_to_variable_name(name)
|
173
|
+
name.to_s.gsub(/::/, '/').
|
174
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
175
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
176
|
+
tr("-", "_").
|
177
|
+
downcase
|
178
|
+
end
|
179
|
+
|
180
|
+
# Based on ActiveSupport's camelize inflector
|
181
|
+
def variable_name_to_class_name(name)
|
182
|
+
name.to_s.
|
183
|
+
gsub(/\/(.?)/) { "::#{$1.upcase}" }.
|
184
|
+
gsub(/(?:^|_)(.)/) { $1.upcase }
|
185
|
+
end
|
186
|
+
|
187
|
+
# From ActiveSupport
|
188
|
+
def symbolize_keys(hash)
|
189
|
+
hash.inject({}) do |options, (key, value)|
|
190
|
+
options[(key.to_sym rescue key) || key] = value
|
191
|
+
options
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
end
|
196
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
|
3
|
+
class << self
|
4
|
+
# An Array of strings specifying locations that should be searched for
|
5
|
+
# factory definitions. By default, factory_girl will attempt to require
|
6
|
+
# "factories," "test/factories," and "spec/factories." Only the first
|
7
|
+
# existing file will be loaded.
|
8
|
+
attr_accessor :definition_file_paths
|
9
|
+
end
|
10
|
+
self.definition_file_paths = %w(factories test/factories spec/factories)
|
11
|
+
|
12
|
+
def self.find_definitions #:nodoc:
|
13
|
+
definition_file_paths.each do |path|
|
14
|
+
require("#{path}.rb") if File.exists?("#{path}.rb")
|
15
|
+
|
16
|
+
if File.directory? path
|
17
|
+
Dir[File.join(path, '*.rb')].sort.each do |file|
|
18
|
+
require file
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|