factory_girl 2.0.4 → 2.0.5

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.
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)