factory_girl 2.0.4 → 2.0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CONTRIBUTION_GUIDELINES.md +1 -0
- data/GETTING_STARTED.md +28 -4
- data/Gemfile +2 -1
- data/Gemfile.lock +6 -2
- data/features/factory_girl_steps.feature +17 -0
- data/features/step_definitions/database_steps.rb +22 -0
- data/features/support/factories.rb +13 -0
- data/features/support/test.db +0 -0
- data/lib/factory_girl/aliases.rb +1 -3
- data/lib/factory_girl/attribute.rb +16 -8
- data/lib/factory_girl/attribute/association.rb +0 -3
- data/lib/factory_girl/attribute/callback.rb +0 -2
- data/lib/factory_girl/attribute/dynamic.rb +1 -3
- data/lib/factory_girl/attribute/static.rb +1 -1
- data/lib/factory_girl/attribute_list.rb +30 -9
- data/lib/factory_girl/factory.rb +1 -1
- data/lib/factory_girl/proxy.rb +7 -5
- data/lib/factory_girl/proxy/attributes_for.rb +8 -3
- data/lib/factory_girl/proxy/build.rb +12 -3
- data/lib/factory_girl/proxy/stub.rb +12 -3
- data/lib/factory_girl/sequence.rb +2 -2
- data/lib/factory_girl/step_definitions.rb +3 -2
- data/lib/factory_girl/version.rb +1 -1
- data/spec/acceptance/attribute_aliases_spec.rb +0 -1
- data/spec/acceptance/attributes_for_spec.rb +0 -1
- data/spec/acceptance/attributes_ordered_spec.rb +24 -6
- data/spec/acceptance/build_list_spec.rb +0 -1
- data/spec/acceptance/build_spec.rb +0 -1
- data/spec/acceptance/build_stubbed_spec.rb +0 -1
- data/spec/acceptance/callbacks_spec.rb +0 -1
- data/spec/acceptance/create_list_spec.rb +0 -1
- data/spec/acceptance/create_spec.rb +0 -1
- data/spec/acceptance/default_strategy_spec.rb +0 -1
- data/spec/acceptance/definition_spec.rb +0 -1
- data/spec/acceptance/definition_without_block_spec.rb +0 -1
- data/spec/acceptance/overrides_spec.rb +0 -1
- data/spec/acceptance/parent_spec.rb +18 -1
- data/spec/acceptance/sequence_spec.rb +0 -1
- data/spec/acceptance/syntax/blueprint_spec.rb +0 -1
- data/spec/acceptance/syntax/generate_spec.rb +0 -1
- data/spec/acceptance/syntax/make_spec.rb +0 -1
- data/spec/acceptance/syntax/sham_spec.rb +0 -1
- data/spec/acceptance/syntax/vintage_spec.rb +27 -29
- data/spec/acceptance/traits_spec.rb +0 -1
- data/spec/acceptance/transient_attributes_spec.rb +68 -0
- data/spec/factory_girl/aliases_spec.rb +19 -21
- data/spec/factory_girl/attribute/association_spec.rb +16 -24
- data/spec/factory_girl/attribute/callback_spec.rb +14 -15
- data/spec/factory_girl/attribute/dynamic_spec.rb +41 -45
- data/spec/factory_girl/attribute/implicit_spec.rb +18 -30
- data/spec/factory_girl/attribute/sequence_spec.rb +11 -13
- data/spec/factory_girl/attribute/static_spec.rb +13 -20
- data/spec/factory_girl/attribute_list_spec.rb +9 -1
- data/spec/factory_girl/attribute_spec.rb +17 -28
- data/spec/factory_girl/definition_proxy_spec.rb +131 -82
- data/spec/factory_girl/deprecated_spec.rb +15 -36
- data/spec/factory_girl/factory_spec.rb +106 -135
- data/spec/factory_girl/find_definitions_spec.rb +7 -6
- data/spec/factory_girl/proxy/attributes_for_spec.rb +23 -32
- data/spec/factory_girl/proxy/build_spec.rb +6 -80
- data/spec/factory_girl/proxy/create_spec.rb +24 -89
- data/spec/factory_girl/proxy/stub_spec.rb +14 -73
- data/spec/factory_girl/proxy_spec.rb +53 -53
- data/spec/factory_girl/registry_spec.rb +13 -29
- data/spec/factory_girl/sequence_spec.rb +24 -72
- data/spec/factory_girl_spec.rb +10 -5
- data/spec/spec_helper.rb +5 -79
- data/spec/support/macros/define_constant.rb +86 -0
- data/spec/support/shared_examples/proxy.rb +99 -0
- metadata +34 -20
- data/spec/acceptance/acceptance_helper.rb +0 -11
data/CONTRIBUTION_GUIDELINES.md
CHANGED
data/GETTING_STARTED.md
CHANGED
@@ -149,6 +149,30 @@ Attributes can be based on the values of other attributes using the proxy that i
|
|
149
149
|
FactoryGirl.create(:user, :last_name => 'Doe').email
|
150
150
|
# => "joe.doe@example.com"
|
151
151
|
|
152
|
+
Transient Attributes
|
153
|
+
--------------------
|
154
|
+
|
155
|
+
There may be times where your code can be DRYed up by passing in transient attributes to factories.
|
156
|
+
|
157
|
+
factory :user do
|
158
|
+
rockstar(true).ignore
|
159
|
+
upcased { false }.ignore
|
160
|
+
|
161
|
+
name { "John Doe#{" - Rockstar" if rockstar}" }
|
162
|
+
email { "#{name.downcase}@example.com" }
|
163
|
+
|
164
|
+
after_create do |user, proxy|
|
165
|
+
user.name.upcase! if proxy.upcased
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
FactoryGirl.create(:user, :upcased => true).name
|
170
|
+
# => "JOHN DOE - ROCKSTAR"
|
171
|
+
|
172
|
+
Static and dynamic attributes can be ignored. Ignored attributes will be ignored within attributes_for and won't be set on the model, even if the attribute exists or you attempt to override it.
|
173
|
+
|
174
|
+
Within Factory Girl's dynamic attributes, you can access ignored attributes as you would expect. If you need to access the proxy in a Factory Girl callback, you'll need to declare a second block argument (for the proxy) and access ignored attributes from there.
|
175
|
+
|
152
176
|
Associations
|
153
177
|
------------
|
154
178
|
|
@@ -213,7 +237,7 @@ Sequences
|
|
213
237
|
Unique values in a specific format (for example, e-mail addresses) can be
|
214
238
|
generated using sequences. Sequences are defined by calling sequence in a
|
215
239
|
definition block, and values in a sequence are generated by calling
|
216
|
-
|
240
|
+
FactoryGirl.generate:
|
217
241
|
|
218
242
|
# Defines a new sequence
|
219
243
|
FactoryGirl.define do
|
@@ -222,10 +246,10 @@ Factory.next:
|
|
222
246
|
end
|
223
247
|
end
|
224
248
|
|
225
|
-
|
249
|
+
FactoryGirl.generate :email
|
226
250
|
# => "person1@example.com"
|
227
251
|
|
228
|
-
|
252
|
+
FactoryGirl.generate :email
|
229
253
|
# => "person2@example.com"
|
230
254
|
|
231
255
|
Sequences can be used as attributes:
|
@@ -237,7 +261,7 @@ Sequences can be used as attributes:
|
|
237
261
|
Or in lazy attributes:
|
238
262
|
|
239
263
|
factory :invite do
|
240
|
-
invitee {
|
264
|
+
invitee { FactoryGirl.generate(:email) }
|
241
265
|
end
|
242
266
|
|
243
267
|
And it's also possible to define an in-line sequence that is only used in
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -22,6 +22,8 @@ GEM
|
|
22
22
|
cucumber (>= 0.10.5)
|
23
23
|
rspec (>= 2.6.0)
|
24
24
|
bluecloth (2.0.9)
|
25
|
+
bourne (1.0)
|
26
|
+
mocha (= 0.9.8)
|
25
27
|
builder (2.1.2)
|
26
28
|
childprocess (0.1.9)
|
27
29
|
ffi (~> 1.0.6)
|
@@ -37,9 +39,10 @@ GEM
|
|
37
39
|
json (>= 1.4.6)
|
38
40
|
i18n (0.4.2)
|
39
41
|
json (1.5.3)
|
42
|
+
mocha (0.9.8)
|
43
|
+
rake
|
40
44
|
rake (0.9.2)
|
41
45
|
rcov (0.9.9)
|
42
|
-
rr (1.0.2)
|
43
46
|
rspec (2.6.0)
|
44
47
|
rspec-core (~> 2.6.0)
|
45
48
|
rspec-expectations (~> 2.6.0)
|
@@ -61,10 +64,11 @@ DEPENDENCIES
|
|
61
64
|
activesupport
|
62
65
|
appraisal (~> 0.3.5)
|
63
66
|
bluecloth
|
67
|
+
bourne
|
64
68
|
cucumber (~> 1.0.0)
|
69
|
+
mocha
|
65
70
|
rake
|
66
71
|
rcov
|
67
|
-
rr
|
68
72
|
rspec (~> 2.0)
|
69
73
|
sqlite3-ruby
|
70
74
|
yard
|
@@ -198,3 +198,20 @@ Feature: Use step definitions generated by factories
|
|
198
198
|
Then I should find the following for the last user:
|
199
199
|
| id | name | admin |
|
200
200
|
| 123 | Joe | true |
|
201
|
+
|
202
|
+
Scenario: Transform parses string data into array before assigning to an association
|
203
|
+
Given the following tags exist:
|
204
|
+
| name |
|
205
|
+
| funky |
|
206
|
+
| cool |
|
207
|
+
| hip |
|
208
|
+
And the following post exists:
|
209
|
+
| title | tags |
|
210
|
+
| Tagged post | cool, hip |
|
211
|
+
Then the post "Tagged post" should have the following tags:
|
212
|
+
| name |
|
213
|
+
| cool |
|
214
|
+
| hip |
|
215
|
+
And the post "Tagged post" should not have the following tags:
|
216
|
+
| name |
|
217
|
+
| funky |
|
@@ -11,8 +11,30 @@ Then /^there should be (\d+) (.*)$/ do |count, model|
|
|
11
11
|
model_class.count.should == count.to_i
|
12
12
|
end
|
13
13
|
|
14
|
+
Then /^the post "([^"]*)" should (not )?have the following tags?:$/ do |post_title, negate, table|
|
15
|
+
post = Post.find_by_title!(post_title)
|
16
|
+
|
17
|
+
table.hashes.each do |row|
|
18
|
+
tag = Tag.find_by_name(row[:name])
|
19
|
+
|
20
|
+
if negate
|
21
|
+
post.tags.should_not include(tag)
|
22
|
+
else
|
23
|
+
post.tags.should include(tag)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
Transform /^table:(?:.*,)?tags(?:,.*)?$/ do |table|
|
29
|
+
table.map_column!("tags") do |tags|
|
30
|
+
tags.split(',').map {|tag| Tag.find_by_name! tag.strip }
|
31
|
+
end
|
32
|
+
table
|
33
|
+
end
|
34
|
+
|
14
35
|
Before do
|
15
36
|
Post.delete_all
|
37
|
+
Tag.delete_all
|
16
38
|
User.delete_all
|
17
39
|
Category.delete_all
|
18
40
|
CategoryGroup.delete_all
|
@@ -21,6 +21,11 @@ class CreateSchema < ActiveRecord::Migration
|
|
21
21
|
t.string :name
|
22
22
|
end
|
23
23
|
|
24
|
+
create_table :tags, :force => true do |t|
|
25
|
+
t.integer :post_id
|
26
|
+
t.string :name
|
27
|
+
end
|
28
|
+
|
24
29
|
create_table :users, :force => true do |t|
|
25
30
|
t.string :name
|
26
31
|
t.boolean :admin, :default => false, :null => false
|
@@ -43,6 +48,11 @@ end
|
|
43
48
|
class Post < ActiveRecord::Base
|
44
49
|
belongs_to :author, :class_name => 'User'
|
45
50
|
belongs_to :category
|
51
|
+
has_many :tags
|
52
|
+
end
|
53
|
+
|
54
|
+
class Tag < ActiveRecord::Base
|
55
|
+
belongs_to :post
|
46
56
|
end
|
47
57
|
|
48
58
|
class NonActiveRecord
|
@@ -74,6 +84,9 @@ FactoryGirl.define do
|
|
74
84
|
category
|
75
85
|
end
|
76
86
|
|
87
|
+
factory :tag do
|
88
|
+
post
|
89
|
+
end
|
77
90
|
# This is here to ensure that factory step definitions don't raise for a non-AR factory
|
78
91
|
factory :non_active_record do
|
79
92
|
end
|
data/features/support/test.db
CHANGED
Binary file
|
data/lib/factory_girl/aliases.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
module FactoryGirl
|
2
|
-
|
3
2
|
class << self
|
4
3
|
attr_accessor :aliases #:nodoc:
|
5
4
|
end
|
5
|
+
|
6
6
|
self.aliases = [
|
7
7
|
[/(.+)_id/, '\1'],
|
8
8
|
[/(.*)/, '\1_id']
|
@@ -13,8 +13,6 @@ module FactoryGirl
|
|
13
13
|
pattern, replace = *params
|
14
14
|
if pattern.match(attribute.to_s)
|
15
15
|
attribute.to_s.sub(pattern, replace).to_sym
|
16
|
-
else
|
17
|
-
nil
|
18
16
|
end
|
19
17
|
end.compact << attribute
|
20
18
|
end
|
@@ -10,17 +10,16 @@ module FactoryGirl
|
|
10
10
|
class Attribute #:nodoc:
|
11
11
|
include Comparable
|
12
12
|
|
13
|
-
attr_reader :name
|
13
|
+
attr_reader :name, :ignored
|
14
14
|
|
15
15
|
def initialize(name)
|
16
16
|
@name = name.to_sym
|
17
|
+
@ignored = false
|
18
|
+
ensure_non_attribute_writer!
|
19
|
+
end
|
17
20
|
|
18
|
-
|
19
|
-
|
20
|
-
raise AttributeDefinitionError,
|
21
|
-
"factory_girl uses 'f.#{attribute_name} value' syntax " +
|
22
|
-
"rather than 'f.#{attribute_name} = value'"
|
23
|
-
end
|
21
|
+
def ignore
|
22
|
+
@ignored = true
|
24
23
|
end
|
25
24
|
|
26
25
|
def add_to(proxy)
|
@@ -43,6 +42,15 @@ module FactoryGirl
|
|
43
42
|
self.priority <=> another.priority
|
44
43
|
end
|
45
44
|
|
46
|
-
|
45
|
+
private
|
47
46
|
|
47
|
+
def ensure_non_attribute_writer!
|
48
|
+
if @name.to_s =~ /=$/
|
49
|
+
attribute_name = $`
|
50
|
+
raise AttributeDefinitionError,
|
51
|
+
"factory_girl uses 'f.#{attribute_name} value' syntax " +
|
52
|
+
"rather than 'f.#{attribute_name} = value'"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
48
56
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
module FactoryGirl
|
2
2
|
class Attribute #:nodoc:
|
3
|
-
|
4
3
|
class Dynamic < Attribute #:nodoc:
|
5
4
|
def initialize(name, block)
|
6
5
|
super(name)
|
@@ -12,9 +11,8 @@ module FactoryGirl
|
|
12
11
|
if FactoryGirl::Sequence === value
|
13
12
|
raise SequenceAbuseError
|
14
13
|
end
|
15
|
-
proxy.set(name, value)
|
14
|
+
proxy.set(name, value, @ignored)
|
16
15
|
end
|
17
16
|
end
|
18
|
-
|
19
17
|
end
|
20
18
|
end
|
@@ -3,7 +3,7 @@ module FactoryGirl
|
|
3
3
|
include Enumerable
|
4
4
|
|
5
5
|
def initialize
|
6
|
-
@attributes =
|
6
|
+
@attributes = {}
|
7
7
|
end
|
8
8
|
|
9
9
|
def define_attribute(attribute)
|
@@ -11,7 +11,7 @@ module FactoryGirl
|
|
11
11
|
raise AttributeDefinitionError, "Attribute already defined: #{attribute.name}"
|
12
12
|
end
|
13
13
|
|
14
|
-
|
14
|
+
add_attribute attribute
|
15
15
|
end
|
16
16
|
|
17
17
|
def add_callback(name, &block)
|
@@ -19,15 +19,15 @@ module FactoryGirl
|
|
19
19
|
raise InvalidCallbackNameError, "#{name} is not a valid callback name. Valid callback names are #{valid_callback_names.inspect}"
|
20
20
|
end
|
21
21
|
|
22
|
-
|
22
|
+
add_attribute Attribute::Callback.new(name.to_sym, block)
|
23
23
|
end
|
24
24
|
|
25
25
|
def each(&block)
|
26
|
-
|
26
|
+
flattened_attributes.each(&block)
|
27
27
|
end
|
28
28
|
|
29
29
|
def attribute_defined?(attribute_name)
|
30
|
-
!@attributes.detect do |attribute|
|
30
|
+
!@attributes.values.flatten.detect do |attribute|
|
31
31
|
attribute.name == attribute_name &&
|
32
32
|
!attribute.is_a?(FactoryGirl::Attribute::Callback)
|
33
33
|
end.nil?
|
@@ -38,16 +38,17 @@ module FactoryGirl
|
|
38
38
|
|
39
39
|
attributes_to_apply.each do |attribute|
|
40
40
|
if attribute_defined?(attribute.name)
|
41
|
-
@attributes.
|
42
|
-
|
41
|
+
@attributes.each_value do |attributes|
|
42
|
+
attributes.delete_if do |attrib|
|
43
|
+
new_attributes << attrib.clone if attrib.name == attribute.name
|
44
|
+
end
|
43
45
|
end
|
44
46
|
else
|
45
47
|
new_attributes << attribute.clone
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
49
|
-
|
50
|
-
@attributes = @attributes.partition {|attr| attr.priority.zero? }.flatten
|
51
|
+
prepend_attributes new_attributes
|
51
52
|
end
|
52
53
|
|
53
54
|
private
|
@@ -55,5 +56,25 @@ module FactoryGirl
|
|
55
56
|
def valid_callback_names
|
56
57
|
[:after_build, :after_create, :after_stub]
|
57
58
|
end
|
59
|
+
|
60
|
+
def add_attribute(attribute)
|
61
|
+
@attributes[attribute.priority] ||= []
|
62
|
+
@attributes[attribute.priority] << attribute
|
63
|
+
attribute
|
64
|
+
end
|
65
|
+
|
66
|
+
def prepend_attributes(new_attributes)
|
67
|
+
new_attributes.group_by {|attr| attr.priority }.each do |priority, attributes|
|
68
|
+
@attributes[priority] ||= []
|
69
|
+
@attributes[priority].unshift *attributes
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def flattened_attributes
|
74
|
+
@attributes.keys.sort.inject([]) do |result, key|
|
75
|
+
result << @attributes[key]
|
76
|
+
result
|
77
|
+
end.flatten
|
78
|
+
end
|
58
79
|
end
|
59
80
|
end
|
data/lib/factory_girl/factory.rb
CHANGED
@@ -86,7 +86,7 @@ module FactoryGirl
|
|
86
86
|
if factory_overrides.empty?
|
87
87
|
attribute.add_to(proxy)
|
88
88
|
else
|
89
|
-
factory_overrides.each { |attr, val| proxy.set(attr, val) }
|
89
|
+
factory_overrides.each { |attr, val| proxy.set(attr, val, attribute.ignored); overrides.delete(attr) }
|
90
90
|
end
|
91
91
|
end
|
92
92
|
overrides.each { |attr, val| proxy.set(attr, val) }
|
data/lib/factory_girl/proxy.rb
CHANGED
@@ -7,10 +7,9 @@ module FactoryGirl
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def get(attribute)
|
10
|
-
nil
|
11
10
|
end
|
12
11
|
|
13
|
-
def set(attribute, value)
|
12
|
+
def set(attribute, value, ignored = false)
|
14
13
|
end
|
15
14
|
|
16
15
|
def associate(name, factory, attributes)
|
@@ -25,7 +24,11 @@ module FactoryGirl
|
|
25
24
|
def run_callbacks(name)
|
26
25
|
if @callbacks && @callbacks[name]
|
27
26
|
@callbacks[name].each do |block|
|
28
|
-
block.arity
|
27
|
+
case block.arity
|
28
|
+
when 0 then block.call
|
29
|
+
when 2 then block.call(@instance, self)
|
30
|
+
else block.call(@instance)
|
31
|
+
end
|
29
32
|
end
|
30
33
|
end
|
31
34
|
end
|
@@ -59,11 +62,10 @@ module FactoryGirl
|
|
59
62
|
# FactoryGirl.build(:post)
|
60
63
|
#
|
61
64
|
# # Builds and saves a User, builds a Post, assigns the User to the
|
62
|
-
# # author association, and saves the
|
65
|
+
# # author association, and saves the Post.
|
63
66
|
# FactoryGirl.create(:post)
|
64
67
|
#
|
65
68
|
def association(name, overrides = {})
|
66
|
-
nil
|
67
69
|
end
|
68
70
|
|
69
71
|
def method_missing(method, *args, &block)
|