dima-exe-factory_girl 1.1.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,337 @@
1
+ class Factory
2
+
3
+ class ScopeDefinitionError < RuntimeError
4
+ end
5
+
6
+ class ScopeNotFoundError < RuntimeError
7
+ end
8
+
9
+ class << self
10
+ attr_accessor :factories #:nodoc:
11
+
12
+ # An Array of strings specifying locations that should be searched for
13
+ # factory definitions. By default, factory_girl will attempt to require
14
+ # "factories," "test/factories," and "spec/factories." Only the first
15
+ # existing file will be loaded.
16
+ attr_accessor :definition_file_paths
17
+ end
18
+
19
+ self.factories = {}
20
+ self.definition_file_paths = %w(factories test/factories spec/factories)
21
+
22
+ attr_reader :factory_name
23
+
24
+ # Defines a new factory that can be used by the build strategies (create and
25
+ # build) to build new objects.
26
+ #
27
+ # Arguments:
28
+ # name: (Symbol)
29
+ # A unique name used to identify this factory.
30
+ # options: (Hash)
31
+ # class: the class that will be used when generating instances for this
32
+ # factory. If not specified, the class will be guessed from the
33
+ # factory name.
34
+ #
35
+ # Yields:
36
+ # The newly created factory (Factory)
37
+ def self.define (name, options = {})
38
+ instance = Factory.new(name, options)
39
+ yield(instance)
40
+ self.factories[instance.factory_name] = instance
41
+ end
42
+
43
+ def build_class #:nodoc:
44
+ @build_class ||= class_for(@options[:class] || factory_name)
45
+ end
46
+
47
+ def initialize (name, options = {}) #:nodoc:
48
+ assert_valid_options(options)
49
+ @factory_name = factory_name_for(name)
50
+ @options = options
51
+ @attributes = []
52
+ @scopes = {}
53
+ end
54
+
55
+ # Adds an attribute that should be assigned on generated instances for this
56
+ # factory.
57
+ #
58
+ # This method should be called with either a value or block, but not both. If
59
+ # called with a block, the attribute will be generated "lazily," whenever an
60
+ # instance is generated. Lazy attribute blocks will not be called if that
61
+ # attribute is overriden for a specific instance.
62
+ #
63
+ # When defining lazy attributes, an instance of Factory::AttributeProxy will
64
+ # be yielded, allowing associations to be built using the correct build
65
+ # strategy.
66
+ #
67
+ # Arguments:
68
+ # name: (Symbol)
69
+ # The name of this attribute. This will be assigned using :"#{name}=" for
70
+ # generated instances.
71
+ # value: (Object)
72
+ # If no block is given, this value will be used for this attribute.
73
+ def add_attribute (name, value = nil, &block)
74
+ attribute = Attribute.new(name, value, block)
75
+
76
+ if attribute_defined?(attribute.name)
77
+ raise AttributeDefinitionError, "Attribute already defined: #{name}"
78
+ end
79
+
80
+ @attributes << attribute
81
+ end
82
+
83
+ def add_scope(name, attr = {})
84
+ if @scopes.has_key?(name.to_sym)
85
+ raise ScopeDefinitionError, "Scope already defined: #{name}"
86
+ end
87
+
88
+ attrs = []
89
+ attr.each_pair do |k,v|
90
+ attrs << Attribute.new(k, v, nil)
91
+ end
92
+
93
+ @scopes[name.to_sym] = attrs
94
+ end
95
+
96
+ # Calls add_attribute using the missing method name as the name of the
97
+ # attribute, so that:
98
+ #
99
+ # Factory.define :user do |f|
100
+ # f.name 'Billy Idol'
101
+ # end
102
+ #
103
+ # and:
104
+ #
105
+ # Factory.define :user do |f|
106
+ # f.add_attribute :name, 'Billy Idol'
107
+ # end
108
+ #
109
+ # are equivilent.
110
+ def method_missing (name, *args, &block)
111
+ add_attribute(name, *args, &block)
112
+ end
113
+
114
+ def self.method_missing(*args, &block)
115
+ chain = args[0].to_s
116
+ name = args[1]
117
+ factory = Factory.factories[name.to_sym]
118
+ super(*args, &block) unless factory
119
+
120
+ names = factory.scope_names.map(&:to_s)
121
+ super(*args, &block) if names.empty?
122
+
123
+ rs = true
124
+ assigned = {}
125
+ while rs
126
+ rs = names.any? do |n|
127
+ if chain.start_with?(n)
128
+ assigned.merge!(factory.scope_attributes_for(n.to_sym))
129
+ chain.gsub!(/^#{n}_?/, "")
130
+ !chain.empty?
131
+ end
132
+ end
133
+ end
134
+
135
+ raise ScopeNotFoundError, "Can't found scope with name '#{chain}'" unless chain.empty?
136
+ Factory(name.to_sym, assigned)
137
+ end
138
+
139
+ # Adds an attribute that builds an association. The associated instance will
140
+ # be built using the same build strategy as the parent instance.
141
+ #
142
+ # Example:
143
+ # Factory.define :user do |f|
144
+ # f.name 'Joey'
145
+ # end
146
+ #
147
+ # Factory.define :post do |f|
148
+ # f.association :author, :factory => :user
149
+ # end
150
+ #
151
+ # Arguments:
152
+ # name: (Symbol)
153
+ # The name of this attribute.
154
+ # options: (Hash)
155
+ # factory: (Symbol)
156
+ # The name of the factory to use when building the associated instance.
157
+ # If no name is given, the name of the attribute is assumed to be the
158
+ # name of the factory. For example, a "user" association will by
159
+ # default use the "user" factory.
160
+ def association (name, options = {})
161
+ name = name.to_sym
162
+ options = symbolize_keys(options)
163
+ association_factory = options[:factory] || name
164
+
165
+ add_attribute(name) {|a| a.association(association_factory) }
166
+ end
167
+
168
+ def attributes_for (attrs = {}) #:nodoc:
169
+ build_attributes_hash(attrs, :attributes_for)
170
+ end
171
+
172
+ def scope_attributes_for(scope, attrs = {}) #:nodoc
173
+ build_attributes_hash(attrs, :attributes_for, @scopes[scope])
174
+ end
175
+
176
+ def scope_names
177
+ @scopes.keys
178
+ end
179
+
180
+ def build (attrs = {}) #:nodoc:
181
+ build_instance(attrs, :build)
182
+ end
183
+
184
+ def create (attrs = {}) #:nodoc:
185
+ instance = build_instance(attrs, :create)
186
+ instance.save!
187
+ instance
188
+ end
189
+
190
+ class << self
191
+
192
+ # Generates and returns a Hash of attributes from this factory. Attributes
193
+ # can be individually overridden by passing in a Hash of attribute => value
194
+ # pairs.
195
+ #
196
+ # Arguments:
197
+ # attrs: (Hash)
198
+ # Attributes to overwrite for this set.
199
+ #
200
+ # Returns:
201
+ # A set of attributes that can be used to build an instance of the class
202
+ # this factory generates. (Hash)
203
+ def attributes_for (name, attrs = {})
204
+ factory_by_name(name).attributes_for(attrs)
205
+ end
206
+
207
+ def scope_attributes_for(name, scope, attrs = {}) #:nodoc
208
+ factory_by_name(name).scope_attributes_for(scope, attrs)
209
+ end
210
+
211
+ # Generates and returns an instance from this factory. Attributes can be
212
+ # individually overridden by passing in a Hash of attribute => value pairs.
213
+ #
214
+ # Arguments:
215
+ # attrs: (Hash)
216
+ # See attributes_for
217
+ #
218
+ # Returns:
219
+ # An instance of the class this factory generates, with generated
220
+ # attributes assigned.
221
+ def build (name, attrs = {})
222
+ factory_by_name(name).build(attrs)
223
+ end
224
+
225
+ # Generates, saves, and returns an instance from this factory. Attributes can
226
+ # be individually overridden by passing in a Hash of attribute => value
227
+ # pairs.
228
+ #
229
+ # If the instance is not valid, an ActiveRecord::Invalid exception will be
230
+ # raised.
231
+ #
232
+ # Arguments:
233
+ # attrs: (Hash)
234
+ # See attributes_for
235
+ #
236
+ # Returns:
237
+ # A saved instance of the class this factory generates, with generated
238
+ # attributes assigned.
239
+ def create (name, attrs = {})
240
+ factory_by_name(name).create(attrs)
241
+ end
242
+
243
+ def find_definitions #:nodoc:
244
+ definition_file_paths.each do |path|
245
+ require("#{path}.rb") if File.exists?("#{path}.rb")
246
+
247
+ if File.directory? path
248
+ Dir[File.join(path, '*.rb')].each do |file|
249
+ require file
250
+ end
251
+ end
252
+ end
253
+ end
254
+
255
+ private
256
+
257
+ def factory_by_name (name)
258
+ factories[name.to_sym] or raise ArgumentError.new("No such factory: #{name.to_s}")
259
+ end
260
+
261
+ end
262
+
263
+ private
264
+
265
+ def build_attributes_hash (values, strategy, container = nil)
266
+ values = symbolize_keys(values)
267
+ passed_keys = values.keys.collect {|key| Factory.aliases_for(key) }.flatten
268
+ (container || @attributes).each do |attribute|
269
+ unless passed_keys.include?(attribute.name)
270
+ proxy = AttributeProxy.new(self, attribute.name, strategy, values)
271
+ values[attribute.name] = attribute.value(proxy)
272
+ end
273
+ end
274
+ values
275
+ end
276
+
277
+ def build_instance (override, strategy)
278
+ instance = build_class.new
279
+ attrs = build_attributes_hash(override, strategy)
280
+ attrs.each do |attr, value|
281
+ instance.send(:"#{attr}=", value)
282
+ end
283
+ instance
284
+ end
285
+
286
+ def class_for (class_or_to_s)
287
+ if class_or_to_s.respond_to?(:to_sym)
288
+ Object.const_get(variable_name_to_class_name(class_or_to_s))
289
+ else
290
+ class_or_to_s
291
+ end
292
+ end
293
+
294
+ def factory_name_for (class_or_to_s)
295
+ if class_or_to_s.respond_to?(:to_sym)
296
+ class_or_to_s.to_sym
297
+ else
298
+ class_name_to_variable_name(class_or_to_s).to_sym
299
+ end
300
+ end
301
+
302
+ def attribute_defined? (name)
303
+ !@attributes.detect {|attr| attr.name == name }.nil?
304
+ end
305
+
306
+ def assert_valid_options(options)
307
+ invalid_keys = options.keys - [:class]
308
+ unless invalid_keys == []
309
+ raise ArgumentError, "Unknown arguments: #{invalid_keys.inspect}"
310
+ end
311
+ end
312
+
313
+ # Based on ActiveSupport's underscore inflector
314
+ def class_name_to_variable_name(name)
315
+ name.to_s.gsub(/::/, '/').
316
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
317
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
318
+ tr("-", "_").
319
+ downcase
320
+ end
321
+
322
+ # Based on ActiveSupport's camelize inflector
323
+ def variable_name_to_class_name(name)
324
+ name.to_s.
325
+ gsub(/\/(.?)/) { "::#{$1.upcase}" }.
326
+ gsub(/(?:^|_)(.)/) { $1.upcase }
327
+ end
328
+
329
+ # From ActiveSupport
330
+ def symbolize_keys(hash)
331
+ hash.inject({}) do |options, (key, value)|
332
+ options[(key.to_sym rescue key) || key] = value
333
+ options
334
+ end
335
+ end
336
+
337
+ end
@@ -0,0 +1,58 @@
1
+ class Factory
2
+
3
+ class Sequence
4
+
5
+ def initialize (&proc) #:nodoc:
6
+ @proc = proc
7
+ @value = 0
8
+ end
9
+
10
+ # Returns the next value for this sequence
11
+ def next
12
+ @value += 1
13
+ @proc.call(@value)
14
+ end
15
+
16
+ end
17
+
18
+ class << self
19
+ attr_accessor :sequences #:nodoc:
20
+ end
21
+ self.sequences = {}
22
+
23
+ # Defines a new sequence that can be used to generate unique values in a specific format.
24
+ #
25
+ # Arguments:
26
+ # name: (Symbol)
27
+ # A unique name for this sequence. This name will be referenced when
28
+ # calling next to generate new values from this sequence.
29
+ # block: (Proc)
30
+ # The code to generate each value in the sequence. This block will be
31
+ # called with a unique number each time a value in the sequence is to be
32
+ # generated. The block should return the generated value for the
33
+ # sequence.
34
+ #
35
+ # Example:
36
+ #
37
+ # Factory.sequence(:email) {|n| "somebody_#{n}@example.com" }
38
+ def self.sequence (name, &block)
39
+ self.sequences[name] = Sequence.new(&block)
40
+ end
41
+
42
+ # Generates and returns the next value in a sequence.
43
+ #
44
+ # Arguments:
45
+ # name: (Symbol)
46
+ # The name of the sequence that a value should be generated for.
47
+ #
48
+ # Returns:
49
+ # The next value in the sequence. (Object)
50
+ def self.next (sequence)
51
+ unless self.sequences.key?(sequence)
52
+ raise "No such sequence: #{sequence}"
53
+ end
54
+
55
+ self.sequences[sequence].next
56
+ end
57
+
58
+ end
@@ -0,0 +1,27 @@
1
+ require 'active_support'
2
+ require 'factory_girl/factory'
3
+ require 'factory_girl/attribute_proxy'
4
+ require 'factory_girl/attribute'
5
+ require 'factory_girl/sequence'
6
+ require 'factory_girl/aliases'
7
+
8
+ # Shortcut for Factory.create.
9
+ #
10
+ # Example:
11
+ # Factory(:user, :name => 'Joe')
12
+ def Factory (name, attrs = {})
13
+ Factory.create(name, attrs)
14
+ end
15
+
16
+ if defined? Rails
17
+ Rails.configuration.after_initialize do
18
+ Factory.definition_file_paths = [
19
+ File.join(RAILS_ROOT, 'test', 'factories'),
20
+ File.join(RAILS_ROOT, 'spec', 'factories')
21
+ ]
22
+ Factory.find_definitions
23
+ end
24
+ else
25
+ Factory.find_definitions
26
+ end
27
+
@@ -0,0 +1,29 @@
1
+ require(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class AliasesTest < Test::Unit::TestCase
4
+
5
+ should "include an attribute as an alias for itself by default" do
6
+ assert Factory.aliases_for(:test).include?(:test)
7
+ end
8
+
9
+ should "include the root of a foreign key as an alias by default" do
10
+ assert Factory.aliases_for(:test_id).include?(:test)
11
+ end
12
+
13
+ should "include an attribute's foreign key as an alias by default" do
14
+ assert Factory.aliases_for(:test).include?(:test_id)
15
+ end
16
+
17
+ context "after adding an alias" do
18
+
19
+ setup do
20
+ Factory.alias(/(.*)_suffix/, '\1')
21
+ end
22
+
23
+ should "return the alias in the aliases list" do
24
+ assert Factory.aliases_for(:test_suffix).include?(:test)
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,121 @@
1
+ require(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class AttributeProxyTest < Test::Unit::TestCase
4
+
5
+ context "an association proxy" do
6
+
7
+ setup do
8
+ @factory = mock('factory')
9
+ @attr = :user
10
+ @attrs = { :first_name => 'John' }
11
+ @strategy = :create
12
+ @proxy = Factory::AttributeProxy.new(@factory, @attr, @strategy, @attrs)
13
+ end
14
+
15
+ should "have a factory" do
16
+ assert_equal @factory, @proxy.factory
17
+ end
18
+
19
+ should "have an attribute name" do
20
+ assert_equal @attr, @proxy.attribute_name
21
+ end
22
+
23
+ should "have a build strategy" do
24
+ assert_equal @strategy, @proxy.strategy
25
+ end
26
+
27
+ should "have attributes" do
28
+ assert_equal @attrs, @proxy.current_values
29
+ end
30
+
31
+ context "building an association" do
32
+
33
+ setup do
34
+ @association = mock('built-user')
35
+ @name = :user
36
+ @attribs = { :first_name => 'Billy' }
37
+
38
+ Factory.stubs(@strategy).returns(@association)
39
+ end
40
+
41
+ should "delegate to the appropriate method on Factory" do
42
+ Factory.expects(@strategy).with(@name, @attribs).returns(@association)
43
+ @proxy.association(@name, @attribs)
44
+ end
45
+
46
+ should "return the built association" do
47
+ assert_equal @association, @proxy.association(@name)
48
+ end
49
+
50
+ end
51
+
52
+ context "building an association using the attributes_for strategy" do
53
+
54
+ setup do
55
+ @strategy = :attributes_for
56
+ @proxy = Factory::AttributeProxy.new(@factory, @attr, @strategy, @attrs)
57
+ end
58
+
59
+ should "not build the association" do
60
+ Factory.expects(@strategy).never
61
+ @proxy.association(:user)
62
+ end
63
+
64
+ should "return nil for the association" do
65
+ Factory.stubs(@strategy).returns(:user)
66
+ assert_nil @proxy.association(:user)
67
+ end
68
+
69
+ end
70
+
71
+ context "building an association using the build strategy" do
72
+
73
+ setup do
74
+ @strategy = :build
75
+ @built = 'object'
76
+ @proxy = Factory::AttributeProxy.new(@factory, @attr, @strategy, @attrs)
77
+ Factory.stubs(:create).returns(@built)
78
+ end
79
+
80
+ should "create the association" do
81
+ Factory.expects(:create).with(:user, {}).returns(@built)
82
+ @proxy.association(:user)
83
+ end
84
+
85
+ should "return the created object" do
86
+ assert_equal @built, @proxy.association(:user)
87
+ end
88
+
89
+ end
90
+
91
+ context "fetching the value of an attribute" do
92
+
93
+ setup do
94
+ @attr = :first_name
95
+ end
96
+
97
+ should "return the correct value" do
98
+ assert_equal @attrs[@attr], @proxy.value_for(@attr)
99
+ end
100
+
101
+ should "call value_for for undefined methods" do
102
+ assert_equal @attrs[@attr], @proxy.send(@attr)
103
+ end
104
+
105
+ end
106
+
107
+ context "fetching the value of an undefined attribute" do
108
+
109
+ setup do
110
+ @attr = :beachball
111
+ end
112
+
113
+ should "raise an ArgumentError" do
114
+ assert_raise(ArgumentError) { @proxy.value_for(@attr) }
115
+ end
116
+
117
+ end
118
+
119
+ end
120
+
121
+ end
@@ -0,0 +1,70 @@
1
+ require(File.join(File.dirname(__FILE__), 'test_helper'))
2
+
3
+ class AttributeTest < Test::Unit::TestCase
4
+
5
+ def setup
6
+ @proxy = mock('attribute-proxy')
7
+ end
8
+
9
+ context "an attribute" do
10
+
11
+ setup do
12
+ @name = :user
13
+ @attr = Factory::Attribute.new(@name, 'test', nil)
14
+ end
15
+
16
+ should "have a name" do
17
+ assert_equal @name, @attr.name
18
+ end
19
+
20
+ end
21
+
22
+ context "an attribute with a static value" do
23
+
24
+ setup do
25
+ @value = 'test'
26
+ @attr = Factory::Attribute.new(:user, @value, nil)
27
+ end
28
+
29
+ should "return the value" do
30
+ assert_equal @value, @attr.value(@proxy)
31
+ end
32
+
33
+ end
34
+
35
+ context "an attribute with a lazy value" do
36
+
37
+ setup do
38
+ @block = lambda { 'value' }
39
+ @attr = Factory::Attribute.new(:user, nil, @block)
40
+ end
41
+
42
+ should "call the block to return a value" do
43
+ assert_equal 'value', @attr.value(@proxy)
44
+ end
45
+
46
+ should "yield the attribute proxy to the block" do
47
+ @block = lambda {|a| a }
48
+ @attr = Factory::Attribute.new(:user, nil, @block)
49
+ assert_equal @proxy, @attr.value(@proxy)
50
+ end
51
+
52
+ end
53
+
54
+ should "raise an error when defining an attribute writer" do
55
+ assert_raise Factory::AttributeDefinitionError do
56
+ Factory::Attribute.new('test=', nil, nil)
57
+ end
58
+ end
59
+
60
+ should "not allow attributes to be added with both a value parameter and a block" do
61
+ assert_raise(Factory::AttributeDefinitionError) do
62
+ Factory::Attribute.new(:name, 'value', lambda {})
63
+ end
64
+ end
65
+
66
+ should "convert names to symbols" do
67
+ assert_equal :name, Factory::Attribute.new('name', nil, nil).name
68
+ end
69
+
70
+ end