vitalish-factory_girl 1.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CONTRIBUTION_GUIDELINES.rdoc +9 -0
- data/Changelog +29 -0
- data/LICENSE +19 -0
- data/README.rdoc +282 -0
- data/Rakefile +66 -0
- data/lib/factory_girl.rb +24 -0
- data/lib/factory_girl/aliases.rb +21 -0
- data/lib/factory_girl/attribute.rb +29 -0
- data/lib/factory_girl/attribute/association.rb +20 -0
- data/lib/factory_girl/attribute/callback.rb +16 -0
- data/lib/factory_girl/attribute/dynamic.rb +20 -0
- data/lib/factory_girl/attribute/static.rb +17 -0
- data/lib/factory_girl/factory.rb +215 -0
- data/lib/factory_girl/proxy.rb +77 -0
- data/lib/factory_girl/proxy/attributes_for.rb +21 -0
- data/lib/factory_girl/proxy/build.rb +32 -0
- data/lib/factory_girl/proxy/create.rb +12 -0
- data/lib/factory_girl/proxy/stub.rb +64 -0
- data/lib/factory_girl/sequence.rb +28 -0
- data/lib/factory_girl/step_definitions.rb +60 -0
- data/lib/factory_girl/syntax.rb +12 -0
- data/lib/factory_girl/syntax/blueprint.rb +42 -0
- data/lib/factory_girl/syntax/generate.rb +73 -0
- data/lib/factory_girl/syntax/make.rb +41 -0
- data/lib/factory_girl/syntax/sham.rb +45 -0
- data/spec/factory_girl/aliases_spec.rb +33 -0
- data/spec/factory_girl/attribute/association_spec.rb +29 -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/static_spec.rb +29 -0
- data/spec/factory_girl/attribute_spec.rb +30 -0
- data/spec/factory_girl/factory_spec.rb +411 -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 +99 -0
- data/spec/factory_girl/proxy/stub_spec.rb +80 -0
- data/spec/factory_girl/proxy_spec.rb +84 -0
- data/spec/factory_girl/sequence_spec.rb +43 -0
- data/spec/spec_helper.rb +93 -0
- metadata +119 -0
@@ -0,0 +1,29 @@
|
|
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
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,20 @@
|
|
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
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
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,20 @@
|
|
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_eval(&@block)
|
12
|
+
if FactoryGirl::Sequence === value
|
13
|
+
raise SequenceAbuseError
|
14
|
+
end
|
15
|
+
proxy.set(name, value)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
class << self
|
3
|
+
attr_accessor :factories #:nodoc:
|
4
|
+
end
|
5
|
+
|
6
|
+
self.factories = {}
|
7
|
+
|
8
|
+
def self.factory_by_name(name)
|
9
|
+
factories[name.to_sym] or raise ArgumentError.new("No such factory: #{name.to_s}")
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.register_factory(factory, options = {})
|
13
|
+
if options[:as]
|
14
|
+
name = options[:as]
|
15
|
+
else
|
16
|
+
name = factory.name
|
17
|
+
factory.aliases.each do |alias_name|
|
18
|
+
register_factory(factory, :as => alias_name)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
if self.factories[name]
|
23
|
+
raise DuplicateDefinitionError, "Factory already defined: #{name}"
|
24
|
+
end
|
25
|
+
|
26
|
+
self.factories[name] = factory
|
27
|
+
end
|
28
|
+
|
29
|
+
# Raised when a factory is defined that attempts to instantiate itself.
|
30
|
+
class AssociationDefinitionError < RuntimeError
|
31
|
+
end
|
32
|
+
|
33
|
+
# Raised when a callback is defined that has an invalid name
|
34
|
+
class InvalidCallbackNameError < RuntimeError
|
35
|
+
end
|
36
|
+
|
37
|
+
# Raised when a factory is defined with the same name as a previously-defined factory.
|
38
|
+
class DuplicateDefinitionError < RuntimeError
|
39
|
+
end
|
40
|
+
|
41
|
+
class Factory
|
42
|
+
attr_reader :name #:nodoc:
|
43
|
+
attr_reader :attributes #:nodoc:
|
44
|
+
|
45
|
+
def factory_name
|
46
|
+
puts "WARNING: factory.factory_name is deprecated. Use factory.name instead."
|
47
|
+
name
|
48
|
+
end
|
49
|
+
|
50
|
+
def class_name #:nodoc:
|
51
|
+
@options[:class] || name
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_class #:nodoc:
|
55
|
+
@build_class ||= class_for(class_name)
|
56
|
+
end
|
57
|
+
|
58
|
+
def default_strategy #:nodoc:
|
59
|
+
@options[:default_strategy] || :create
|
60
|
+
end
|
61
|
+
|
62
|
+
def initialize(name, options = {}) #:nodoc:
|
63
|
+
assert_valid_options(options)
|
64
|
+
@name = factory_name_for(name)
|
65
|
+
@options = options
|
66
|
+
@attributes = []
|
67
|
+
end
|
68
|
+
|
69
|
+
def inherit_from(parent) #:nodoc:
|
70
|
+
@options[:class] ||= parent.class_name
|
71
|
+
@options[:default_strategy] ||= parent.default_strategy
|
72
|
+
|
73
|
+
new_attributes = []
|
74
|
+
parent.attributes.each do |attribute|
|
75
|
+
unless attribute_defined?(attribute.name)
|
76
|
+
new_attributes << attribute.clone
|
77
|
+
end
|
78
|
+
end
|
79
|
+
@attributes.unshift *new_attributes
|
80
|
+
end
|
81
|
+
|
82
|
+
def define_attribute(attribute)
|
83
|
+
name = attribute.name
|
84
|
+
# TODO: move these checks into Attribute
|
85
|
+
if attribute_defined?(name)
|
86
|
+
raise AttributeDefinitionError, "Attribute already defined: #{name}"
|
87
|
+
end
|
88
|
+
if attribute.respond_to?(:factory) && attribute.factory == self.name
|
89
|
+
raise AssociationDefinitionError, "Self-referencing association '#{name}' in factory '#{self.name}'"
|
90
|
+
end
|
91
|
+
@attributes << attribute
|
92
|
+
end
|
93
|
+
|
94
|
+
def add_callback(name, &block)
|
95
|
+
unless [:after_build, :after_create, :after_stub].include?(name.to_sym)
|
96
|
+
raise InvalidCallbackNameError, "#{name} is not a valid callback name. Valid callback names are :after_build, :after_create, and :after_stub"
|
97
|
+
end
|
98
|
+
@attributes << Attribute::Callback.new(name.to_sym, block)
|
99
|
+
end
|
100
|
+
|
101
|
+
def run (proxy_class, overrides) #:nodoc:
|
102
|
+
proxy = proxy_class.new(build_class)
|
103
|
+
overrides = symbolize_keys(overrides)
|
104
|
+
overrides.each {|attr, val| proxy.set(attr, val) }
|
105
|
+
passed_keys = overrides.keys.collect {|k| FactoryGirl.aliases_for(k) }.flatten
|
106
|
+
@attributes.each do |attribute|
|
107
|
+
unless passed_keys.include?(attribute.name)
|
108
|
+
attribute.add_to(proxy)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
proxy.result
|
112
|
+
end
|
113
|
+
|
114
|
+
def human_name(*args, &block)
|
115
|
+
name.to_s.gsub('_', ' ')
|
116
|
+
end
|
117
|
+
|
118
|
+
def associations
|
119
|
+
attributes.select {|attribute| attribute.is_a?(Attribute::Association) }
|
120
|
+
end
|
121
|
+
|
122
|
+
# Alternate names for this factory.
|
123
|
+
#
|
124
|
+
# Example:
|
125
|
+
#
|
126
|
+
# factory :user, :aliases => [:author] do
|
127
|
+
# # ...
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# Factory(:author).class
|
131
|
+
# # => User
|
132
|
+
#
|
133
|
+
# Because an attribute defined without a value or block will build an
|
134
|
+
# association with the same name, this allows associations to be defined
|
135
|
+
# without factories, such as:
|
136
|
+
#
|
137
|
+
# factory :user, :aliases => [:author] do
|
138
|
+
# # ...
|
139
|
+
# end
|
140
|
+
#
|
141
|
+
# factory :post do
|
142
|
+
# author
|
143
|
+
# end
|
144
|
+
#
|
145
|
+
# Factory(:post).author.class
|
146
|
+
# # => User
|
147
|
+
def aliases
|
148
|
+
@options[:aliases] || []
|
149
|
+
end
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def class_for (class_or_to_s)
|
154
|
+
if class_or_to_s.respond_to?(:to_sym)
|
155
|
+
class_name = variable_name_to_class_name(class_or_to_s)
|
156
|
+
class_name.split('::').inject(Object) do |object, string|
|
157
|
+
object.const_get(string)
|
158
|
+
end
|
159
|
+
else
|
160
|
+
class_or_to_s
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def factory_name_for(class_or_to_s)
|
165
|
+
if class_or_to_s.respond_to?(:to_sym)
|
166
|
+
class_or_to_s.to_sym
|
167
|
+
else
|
168
|
+
class_name_to_variable_name(class_or_to_s).to_sym
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def attribute_defined? (name)
|
173
|
+
!@attributes.detect {|attr| attr.name == name && !attr.is_a?(Attribute::Callback) }.nil?
|
174
|
+
end
|
175
|
+
|
176
|
+
def assert_valid_options(options)
|
177
|
+
invalid_keys = options.keys - [:class, :parent, :default_strategy, :aliases]
|
178
|
+
unless invalid_keys == []
|
179
|
+
raise ArgumentError, "Unknown arguments: #{invalid_keys.inspect}"
|
180
|
+
end
|
181
|
+
assert_valid_strategy(options[:default_strategy]) if options[:default_strategy]
|
182
|
+
end
|
183
|
+
|
184
|
+
def assert_valid_strategy(strategy)
|
185
|
+
unless Proxy.const_defined? variable_name_to_class_name(strategy)
|
186
|
+
raise ArgumentError, "Unknown strategy: #{strategy}"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Based on ActiveSupport's underscore inflector
|
191
|
+
def class_name_to_variable_name(name)
|
192
|
+
name.to_s.gsub(/::/, '/').
|
193
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
194
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
195
|
+
tr("-", "_").
|
196
|
+
downcase
|
197
|
+
end
|
198
|
+
|
199
|
+
# Based on ActiveSupport's camelize inflector
|
200
|
+
def variable_name_to_class_name(name)
|
201
|
+
name.to_s.
|
202
|
+
gsub(/\/(.?)/) { "::#{$1.upcase}" }.
|
203
|
+
gsub(/(?:^|_)(.)/) { $1.upcase }
|
204
|
+
end
|
205
|
+
|
206
|
+
# From ActiveSupport
|
207
|
+
def symbolize_keys(hash)
|
208
|
+
hash.inject({}) do |options, (key, value)|
|
209
|
+
options[(key.to_sym rescue key) || key] = value
|
210
|
+
options
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
class Proxy #:nodoc:
|
3
|
+
|
4
|
+
attr_reader :callbacks
|
5
|
+
|
6
|
+
def initialize(klass)
|
7
|
+
end
|
8
|
+
|
9
|
+
def get(attribute)
|
10
|
+
nil
|
11
|
+
end
|
12
|
+
|
13
|
+
def set(attribute, value)
|
14
|
+
end
|
15
|
+
|
16
|
+
def associate(name, factory, attributes)
|
17
|
+
end
|
18
|
+
|
19
|
+
def add_callback(name, block)
|
20
|
+
@callbacks ||= {}
|
21
|
+
@callbacks[name] ||= []
|
22
|
+
@callbacks[name] << block
|
23
|
+
end
|
24
|
+
|
25
|
+
def run_callbacks(name)
|
26
|
+
if @callbacks && @callbacks[name]
|
27
|
+
@callbacks[name].each do |block|
|
28
|
+
block.arity.zero? ? block.call : block.call(@instance)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Generates an association using the current build strategy.
|
34
|
+
#
|
35
|
+
# Arguments:
|
36
|
+
# name: (Symbol)
|
37
|
+
# The name of the factory that should be used to generate this
|
38
|
+
# association.
|
39
|
+
# attributes: (Hash)
|
40
|
+
# A hash of attributes that should be overridden for this association.
|
41
|
+
#
|
42
|
+
# Returns:
|
43
|
+
# The generated association for the current build strategy. Note that
|
44
|
+
# associations are not generated for the attributes_for strategy. Returns
|
45
|
+
# nil in this case.
|
46
|
+
#
|
47
|
+
# Example:
|
48
|
+
#
|
49
|
+
# factory :user do
|
50
|
+
# # ...
|
51
|
+
# end
|
52
|
+
#
|
53
|
+
# factory :post do
|
54
|
+
# # ...
|
55
|
+
# author { |post| post.association(:user, :name => 'Joe') }
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# # Builds (but doesn't save) a Post and a User
|
59
|
+
# Factory.build(:post)
|
60
|
+
#
|
61
|
+
# # Builds and saves a User, builds a Post, assigns the User to the
|
62
|
+
# # author association, and saves the User.
|
63
|
+
# Factory.create(:post)
|
64
|
+
#
|
65
|
+
def association(name, overrides = {})
|
66
|
+
nil
|
67
|
+
end
|
68
|
+
|
69
|
+
def method_missing(method, *args, &block)
|
70
|
+
get(method)
|
71
|
+
end
|
72
|
+
|
73
|
+
def result
|
74
|
+
raise NotImplementedError, "Strategies must return a result"
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
class Proxy #:nodoc:
|
3
|
+
class AttributesFor < Proxy #:nodoc:
|
4
|
+
def initialize(klass)
|
5
|
+
@hash = {}
|
6
|
+
end
|
7
|
+
|
8
|
+
def get(attribute)
|
9
|
+
@hash[attribute]
|
10
|
+
end
|
11
|
+
|
12
|
+
def set(attribute, value)
|
13
|
+
@hash[attribute] = value
|
14
|
+
end
|
15
|
+
|
16
|
+
def result
|
17
|
+
@hash
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module FactoryGirl
|
2
|
+
class Proxy #:nodoc:
|
3
|
+
class Build < Proxy #:nodoc:
|
4
|
+
def initialize(klass)
|
5
|
+
@instance = klass.new
|
6
|
+
end
|
7
|
+
|
8
|
+
def get(attribute)
|
9
|
+
@instance.send(attribute)
|
10
|
+
end
|
11
|
+
|
12
|
+
def set(attribute, value)
|
13
|
+
@instance.send(:"#{attribute}=", value)
|
14
|
+
end
|
15
|
+
|
16
|
+
def associate(name, factory_name, overrides)
|
17
|
+
factory = FactoryGirl.factory_by_name(factory_name)
|
18
|
+
set(name, factory.run(Proxy::Create, overrides))
|
19
|
+
end
|
20
|
+
|
21
|
+
def association(factory_name, overrides = {})
|
22
|
+
factory = FactoryGirl.factory_by_name(factory_name)
|
23
|
+
factory.run(Proxy::Create, overrides)
|
24
|
+
end
|
25
|
+
|
26
|
+
def result
|
27
|
+
run_callbacks(:after_build)
|
28
|
+
@instance
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|