factory_girl_kibiz0r 2.0.0.beta2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|