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.
Files changed (71) hide show
  1. data/CONTRIBUTION_GUIDELINES.md +1 -0
  2. data/GETTING_STARTED.md +28 -4
  3. data/Gemfile +2 -1
  4. data/Gemfile.lock +6 -2
  5. data/features/factory_girl_steps.feature +17 -0
  6. data/features/step_definitions/database_steps.rb +22 -0
  7. data/features/support/factories.rb +13 -0
  8. data/features/support/test.db +0 -0
  9. data/lib/factory_girl/aliases.rb +1 -3
  10. data/lib/factory_girl/attribute.rb +16 -8
  11. data/lib/factory_girl/attribute/association.rb +0 -3
  12. data/lib/factory_girl/attribute/callback.rb +0 -2
  13. data/lib/factory_girl/attribute/dynamic.rb +1 -3
  14. data/lib/factory_girl/attribute/static.rb +1 -1
  15. data/lib/factory_girl/attribute_list.rb +30 -9
  16. data/lib/factory_girl/factory.rb +1 -1
  17. data/lib/factory_girl/proxy.rb +7 -5
  18. data/lib/factory_girl/proxy/attributes_for.rb +8 -3
  19. data/lib/factory_girl/proxy/build.rb +12 -3
  20. data/lib/factory_girl/proxy/stub.rb +12 -3
  21. data/lib/factory_girl/sequence.rb +2 -2
  22. data/lib/factory_girl/step_definitions.rb +3 -2
  23. data/lib/factory_girl/version.rb +1 -1
  24. data/spec/acceptance/attribute_aliases_spec.rb +0 -1
  25. data/spec/acceptance/attributes_for_spec.rb +0 -1
  26. data/spec/acceptance/attributes_ordered_spec.rb +24 -6
  27. data/spec/acceptance/build_list_spec.rb +0 -1
  28. data/spec/acceptance/build_spec.rb +0 -1
  29. data/spec/acceptance/build_stubbed_spec.rb +0 -1
  30. data/spec/acceptance/callbacks_spec.rb +0 -1
  31. data/spec/acceptance/create_list_spec.rb +0 -1
  32. data/spec/acceptance/create_spec.rb +0 -1
  33. data/spec/acceptance/default_strategy_spec.rb +0 -1
  34. data/spec/acceptance/definition_spec.rb +0 -1
  35. data/spec/acceptance/definition_without_block_spec.rb +0 -1
  36. data/spec/acceptance/overrides_spec.rb +0 -1
  37. data/spec/acceptance/parent_spec.rb +18 -1
  38. data/spec/acceptance/sequence_spec.rb +0 -1
  39. data/spec/acceptance/syntax/blueprint_spec.rb +0 -1
  40. data/spec/acceptance/syntax/generate_spec.rb +0 -1
  41. data/spec/acceptance/syntax/make_spec.rb +0 -1
  42. data/spec/acceptance/syntax/sham_spec.rb +0 -1
  43. data/spec/acceptance/syntax/vintage_spec.rb +27 -29
  44. data/spec/acceptance/traits_spec.rb +0 -1
  45. data/spec/acceptance/transient_attributes_spec.rb +68 -0
  46. data/spec/factory_girl/aliases_spec.rb +19 -21
  47. data/spec/factory_girl/attribute/association_spec.rb +16 -24
  48. data/spec/factory_girl/attribute/callback_spec.rb +14 -15
  49. data/spec/factory_girl/attribute/dynamic_spec.rb +41 -45
  50. data/spec/factory_girl/attribute/implicit_spec.rb +18 -30
  51. data/spec/factory_girl/attribute/sequence_spec.rb +11 -13
  52. data/spec/factory_girl/attribute/static_spec.rb +13 -20
  53. data/spec/factory_girl/attribute_list_spec.rb +9 -1
  54. data/spec/factory_girl/attribute_spec.rb +17 -28
  55. data/spec/factory_girl/definition_proxy_spec.rb +131 -82
  56. data/spec/factory_girl/deprecated_spec.rb +15 -36
  57. data/spec/factory_girl/factory_spec.rb +106 -135
  58. data/spec/factory_girl/find_definitions_spec.rb +7 -6
  59. data/spec/factory_girl/proxy/attributes_for_spec.rb +23 -32
  60. data/spec/factory_girl/proxy/build_spec.rb +6 -80
  61. data/spec/factory_girl/proxy/create_spec.rb +24 -89
  62. data/spec/factory_girl/proxy/stub_spec.rb +14 -73
  63. data/spec/factory_girl/proxy_spec.rb +53 -53
  64. data/spec/factory_girl/registry_spec.rb +13 -29
  65. data/spec/factory_girl/sequence_spec.rb +24 -72
  66. data/spec/factory_girl_spec.rb +10 -5
  67. data/spec/spec_helper.rb +5 -79
  68. data/spec/support/macros/define_constant.rb +86 -0
  69. data/spec/support/shared_examples/proxy.rb +99 -0
  70. metadata +34 -20
  71. data/spec/acceptance/acceptance_helper.rb +0 -11
@@ -5,5 +5,6 @@ Contributing to factory_girl:
5
5
  3. Send a pull request.
6
6
 
7
7
  Notes:
8
+
8
9
  * Contributions without tests won't be accepted.
9
10
  * Please don't update the Gem version.
@@ -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
- Factory.next:
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
- Factory.next :email
249
+ FactoryGirl.generate :email
226
250
  # => "person1@example.com"
227
251
 
228
- Factory.next :email
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 { Factory.next(:email) }
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
@@ -5,7 +5,8 @@ gem "rspec", "~> 2.0"
5
5
  gem "rcov"
6
6
  gem "activerecord", :require => false
7
7
  gem "activesupport", :require => false
8
- gem "rr"
8
+ gem "mocha"
9
+ gem "bourne"
9
10
  gem "sqlite3-ruby", :require => false
10
11
  gem "appraisal", "~> 0.3.5"
11
12
  gem "yard"
@@ -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
Binary file
@@ -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
- if @name.to_s =~ /=$/
19
- attribute_name = $`
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
- end
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,8 +1,6 @@
1
1
  module FactoryGirl
2
2
  class Attribute #:nodoc:
3
-
4
3
  class Association < Attribute #:nodoc:
5
-
6
4
  attr_reader :factory
7
5
 
8
6
  def initialize(name, factory, overrides)
@@ -19,6 +17,5 @@ module FactoryGirl
19
17
  true
20
18
  end
21
19
  end
22
-
23
20
  end
24
21
  end
@@ -1,6 +1,5 @@
1
1
  module FactoryGirl
2
2
  class Attribute #:nodoc:
3
-
4
3
  class Callback < Attribute #:nodoc:
5
4
  def initialize(name, block)
6
5
  @name = name.to_sym
@@ -11,6 +10,5 @@ module FactoryGirl
11
10
  proxy.add_callback(name, @block)
12
11
  end
13
12
  end
14
-
15
13
  end
16
14
  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
@@ -9,7 +9,7 @@ module FactoryGirl
9
9
  end
10
10
 
11
11
  def add_to(proxy)
12
- proxy.set(name, @value)
12
+ proxy.set(name, @value, @ignored)
13
13
  end
14
14
 
15
15
  def priority
@@ -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
- @attributes << attribute
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
- @attributes << Attribute::Callback.new(name.to_sym, block)
22
+ add_attribute Attribute::Callback.new(name.to_sym, block)
23
23
  end
24
24
 
25
25
  def each(&block)
26
- @attributes.each(&block)
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.delete_if do |attrib|
42
- new_attributes << attrib.clone if attrib.name == attribute.name
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
- @attributes.unshift *new_attributes
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
@@ -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) }
@@ -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.zero? ? block.call : block.call(@instance)
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 User.
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)