factory_girl 2.0.0.beta1 → 2.0.0.beta2

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 (63) hide show
  1. data/Appraisals +12 -0
  2. data/{CONTRIBUTION_GUIDELINES.rdoc → CONTRIBUTION_GUIDELINES.md} +3 -3
  3. data/GETTING_STARTED.md +246 -0
  4. data/Gemfile +12 -0
  5. data/Gemfile.lock +62 -0
  6. data/README.md +64 -0
  7. data/Rakefile +15 -30
  8. data/features/factory_girl_steps.feature +26 -0
  9. data/features/step_definitions/database_steps.rb +2 -0
  10. data/features/support/env.rb +0 -9
  11. data/features/support/factories.rb +15 -0
  12. data/features/support/test.db +0 -0
  13. data/lib/factory_girl.rb +2 -0
  14. data/lib/factory_girl/definition_proxy.rb +10 -43
  15. data/lib/factory_girl/factory.rb +48 -17
  16. data/lib/factory_girl/proxy.rb +3 -3
  17. data/lib/factory_girl/proxy/attributes_for.rb +1 -1
  18. data/lib/factory_girl/proxy/build.rb +3 -3
  19. data/lib/factory_girl/proxy/create.rb +6 -2
  20. data/lib/factory_girl/proxy/stub.rb +3 -3
  21. data/lib/factory_girl/registry.rb +59 -0
  22. data/lib/factory_girl/sequence.rb +23 -9
  23. data/lib/factory_girl/step_definitions.rb +16 -9
  24. data/lib/factory_girl/syntax/blueprint.rb +2 -2
  25. data/lib/factory_girl/syntax/default.rb +5 -3
  26. data/lib/factory_girl/syntax/generate.rb +6 -6
  27. data/lib/factory_girl/syntax/make.rb +2 -2
  28. data/lib/factory_girl/syntax/methods.rb +75 -0
  29. data/lib/factory_girl/syntax/sham.rb +2 -2
  30. data/lib/factory_girl/syntax/vintage.rb +16 -79
  31. data/lib/factory_girl/version.rb +1 -1
  32. data/spec/acceptance/acceptance_helper.rb +7 -12
  33. data/spec/acceptance/attribute_aliases_spec.rb +26 -0
  34. data/spec/acceptance/attributes_for_spec.rb +48 -0
  35. data/spec/acceptance/build_spec.rb +35 -0
  36. data/spec/acceptance/build_stubbed_spec.rb +79 -0
  37. data/spec/acceptance/callbacks_spec.rb +42 -0
  38. data/spec/acceptance/create_spec.rb +67 -0
  39. data/spec/acceptance/default_strategy_spec.rb +27 -0
  40. data/spec/acceptance/definition_spec.rb +28 -0
  41. data/spec/acceptance/parent_spec.rb +39 -0
  42. data/spec/acceptance/sequence_spec.rb +32 -0
  43. data/spec/acceptance/syntax/blueprint_spec.rb +7 -5
  44. data/spec/acceptance/syntax/generate_spec.rb +10 -4
  45. data/spec/acceptance/syntax/make_spec.rb +7 -4
  46. data/spec/acceptance/syntax/sham_spec.rb +15 -8
  47. data/spec/acceptance/syntax/vintage_spec.rb +57 -17
  48. data/spec/factory_girl/attribute/dynamic_spec.rb +1 -1
  49. data/spec/factory_girl/definition_proxy_spec.rb +13 -11
  50. data/spec/factory_girl/factory_spec.rb +29 -52
  51. data/spec/factory_girl/find_definitions_spec.rb +34 -23
  52. data/spec/factory_girl/proxy/attributes_for_spec.rb +6 -6
  53. data/spec/factory_girl/proxy/build_spec.rb +4 -4
  54. data/spec/factory_girl/proxy/create_spec.rb +11 -3
  55. data/spec/factory_girl/proxy/stub_spec.rb +6 -6
  56. data/spec/factory_girl/proxy_spec.rb +1 -1
  57. data/spec/factory_girl/registry_spec.rb +92 -0
  58. data/spec/factory_girl/sequence_spec.rb +65 -8
  59. data/spec/spec_helper.rb +75 -29
  60. metadata +66 -43
  61. data/README.rdoc +0 -282
  62. data/spec/acceptance/acceptance_spec.rb +0 -288
  63. data/spec/acceptance/models.rb +0 -48
@@ -123,3 +123,29 @@ Feature: Use step definitions generated by factories
123
123
  And 2 categories exist
124
124
  And 2 categories exist with a name of "Future"
125
125
  Then there should be 6 categories
126
+
127
+ Scenario: create a post with an existing category group
128
+ Given the following category exists:
129
+ | ID | name | category group |
130
+ | 123 | fiction | Name: books |
131
+ And the following post exists:
132
+ | Title | Author | Category |
133
+ | a title | Name: Joe | Category Group: Name: books |
134
+ Then I should find the following for the last post:
135
+ | title | category_id |
136
+ | a title | 123 |
137
+
138
+ Scenario: create a post with an existing category group and a new category
139
+ Given the following category group exists:
140
+ | ID | name |
141
+ | 456 | books |
142
+ And the following post exists:
143
+ | Title | Author | Category |
144
+ | a title | Name: Joe | Category Group: Name: books |
145
+ Then I should find the following for the last post:
146
+ | title |
147
+ | a title |
148
+ And I should find the following for the last category:
149
+ | category_group_id |
150
+ | 456 |
151
+
@@ -15,4 +15,6 @@ Before do
15
15
  Post.delete_all
16
16
  User.delete_all
17
17
  Category.delete_all
18
+ CategoryGroup.delete_all
18
19
  end
20
+
@@ -2,14 +2,5 @@ PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
2
2
 
3
3
  $: << File.join(PROJECT_ROOT, 'lib')
4
4
 
5
- case ENV['RAILS_VERSION']
6
- when '2.1' then
7
- gem 'activerecord', '~>2.1.0'
8
- when '3.0' then
9
- gem 'activerecord', '~>3.0.0'
10
- else
11
- gem 'activerecord', '~>2.3.0'
12
- end
13
-
14
5
  require 'active_record'
15
6
  require 'factory_girl'
@@ -12,7 +12,12 @@ class CreateSchema < ActiveRecord::Migration
12
12
  t.string :body
13
13
  end
14
14
 
15
+ create_table :category_groups, :force => true do |t|
16
+ t.string :name
17
+ end
18
+
15
19
  create_table :categories, :force => true do |t|
20
+ t.integer :category_group_id
16
21
  t.string :name
17
22
  end
18
23
 
@@ -28,7 +33,11 @@ CreateSchema.suppress_messages { CreateSchema.migrate(:up) }
28
33
  class User < ActiveRecord::Base
29
34
  end
30
35
 
36
+ class CategoryGroup < ActiveRecord::Base
37
+ end
38
+
31
39
  class Category < ActiveRecord::Base
40
+ belongs_to :category_group
32
41
  end
33
42
 
34
43
  class Post < ActiveRecord::Base
@@ -48,6 +57,11 @@ end
48
57
 
49
58
  Factory.define :category do |f|
50
59
  f.name "programming"
60
+ f.association :category_group
61
+ end
62
+
63
+ Factory.define :category_group do |f|
64
+ f.name "tecnhology"
51
65
  end
52
66
 
53
67
  Factory.define :post do |f|
@@ -60,3 +74,4 @@ Factory.define :non_active_record do |f|
60
74
  end
61
75
 
62
76
  require 'factory_girl/step_definitions'
77
+
Binary file
data/lib/factory_girl.rb CHANGED
@@ -3,6 +3,7 @@ require 'factory_girl/proxy/build'
3
3
  require 'factory_girl/proxy/create'
4
4
  require 'factory_girl/proxy/attributes_for'
5
5
  require 'factory_girl/proxy/stub'
6
+ require 'factory_girl/registry'
6
7
  require 'factory_girl/factory'
7
8
  require 'factory_girl/attribute'
8
9
  require 'factory_girl/attribute/static'
@@ -12,6 +13,7 @@ require 'factory_girl/attribute/callback'
12
13
  require 'factory_girl/sequence'
13
14
  require 'factory_girl/aliases'
14
15
  require 'factory_girl/definition_proxy'
16
+ require 'factory_girl/syntax/methods'
15
17
  require 'factory_girl/syntax/default'
16
18
  require 'factory_girl/syntax/vintage'
17
19
  require 'factory_girl/find_definitions'
@@ -22,7 +22,7 @@ module FactoryGirl
22
22
  #
23
23
  # Arguments:
24
24
  # * name: +Symbol+ or +String+
25
- # The name of this attribute. This will be assigned using :"#{name}=" for
25
+ # The name of this attribute. This will be assigned using "name=" for
26
26
  # generated instances.
27
27
  # * value: +Object+
28
28
  # If no block is given, this value will be used for this attribute.
@@ -59,7 +59,7 @@ module FactoryGirl
59
59
  # or association with the same name. This means that:
60
60
  #
61
61
  # factory :user do
62
- # email { Factory.next(:email) }
62
+ # email { create(:email) }
63
63
  # association :account
64
64
  # end
65
65
  #
@@ -73,11 +73,7 @@ module FactoryGirl
73
73
  # are equivilent.
74
74
  def method_missing(name, *args, &block)
75
75
  if args.empty? && block.nil?
76
- if sequence = FactoryGirl.sequences[name]
77
- add_attribute(name) { sequence.next }
78
- else
79
- association(name)
80
- end
76
+ association(name)
81
77
  else
82
78
  add_attribute(name, *args, &block)
83
79
  end
@@ -95,13 +91,13 @@ module FactoryGirl
95
91
  # sequence(:email) { |n| "person#{n}@example.com" }
96
92
  #
97
93
  # factory :user do
98
- # email { Factory.next(:email) }
94
+ # email { FactoryGirl.create(:email) }
99
95
  # end
100
96
  #
101
97
  # Except that no globally available sequence will be defined.
102
98
  def sequence(name, start_value = 1, &block)
103
- sequence = Sequence.new(start_value, &block)
104
- add_attribute(name) { sequence.next }
99
+ sequence = Sequence.new(name, start_value, &block)
100
+ add_attribute(name) { sequence.run }
105
101
  end
106
102
 
107
103
  # Adds an attribute that builds an association. The associated instance will
@@ -132,39 +128,6 @@ module FactoryGirl
132
128
  @factory.define_attribute(Attribute::Association.new(name, factory_name, options))
133
129
  end
134
130
 
135
- # Registers an alias for this factory using the given name.
136
- #
137
- # Arguments:
138
- # * name: +Symbol+
139
- # The name of the alias.
140
- #
141
- # Example:
142
- #
143
- # factory :user do
144
- # aliased_as :author
145
- # end
146
- #
147
- # Factory(:author).class
148
- # # => User
149
- #
150
- # Because an attribute defined without a value or block will build an
151
- # association with the same name, this allows associations to be defined
152
- # without factories, such as:
153
- #
154
- # factory :user do
155
- # aliased_as :author
156
- # end
157
- #
158
- # factory :post do
159
- # author
160
- # end
161
- #
162
- # Factory(:post).author.class
163
- # # => User
164
- def aliased_as(name)
165
- FactoryGirl.register_factory(@factory, :as => name)
166
- end
167
-
168
131
  def after_build(&block)
169
132
  @factory.add_callback(:after_build, &block)
170
133
  end
@@ -176,5 +139,9 @@ module FactoryGirl
176
139
  def after_stub(&block)
177
140
  @factory.add_callback(:after_stub, &block)
178
141
  end
142
+
143
+ def to_create(&block)
144
+ @factory.to_create(&block)
145
+ end
179
146
  end
180
147
  end
@@ -1,20 +1,14 @@
1
1
  module FactoryGirl
2
- class << self
3
- attr_accessor :factories #:nodoc:
2
+ def self.factories
3
+ puts "WARNING: FactoryGirl.factories is deprecated."
4
+ puts "Use FactoryGirl.registry instead."
5
+ registry
4
6
  end
5
7
 
6
- self.factories = {}
7
-
8
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
- name = options[:as] || factory.name
14
- if self.factories[name]
15
- raise DuplicateDefinitionError, "Factory already defined: #{name}"
16
- end
17
- self.factories[name] = factory
9
+ puts "WARNING: FactoryGirl.factory_by_name is deprecated."
10
+ puts "Use FactoryGirl.find instead."
11
+ registry.find(name)
18
12
  end
19
13
 
20
14
  # Raised when a factory is defined that attempts to instantiate itself.
@@ -89,7 +83,7 @@ module FactoryGirl
89
83
  @attributes << Attribute::Callback.new(name.to_sym, block)
90
84
  end
91
85
 
92
- def run (proxy_class, overrides) #:nodoc:
86
+ def run(proxy_class, overrides) #:nodoc:
93
87
  proxy = proxy_class.new(build_class)
94
88
  overrides = symbolize_keys(overrides)
95
89
  overrides.each {|attr, val| proxy.set(attr, val) }
@@ -99,7 +93,7 @@ module FactoryGirl
99
93
  attribute.add_to(proxy)
100
94
  end
101
95
  end
102
- proxy.result
96
+ proxy.result(@to_create_block)
103
97
  end
104
98
 
105
99
  def human_name(*args, &block)
@@ -110,6 +104,39 @@ module FactoryGirl
110
104
  attributes.select {|attribute| attribute.is_a?(Attribute::Association) }
111
105
  end
112
106
 
107
+ # Names for this factory, including aliases.
108
+ #
109
+ # Example:
110
+ #
111
+ # factory :user, :aliases => [:author] do
112
+ # # ...
113
+ # end
114
+ #
115
+ # FactoryGirl.create(:author).class
116
+ # # => User
117
+ #
118
+ # Because an attribute defined without a value or block will build an
119
+ # association with the same name, this allows associations to be defined
120
+ # without factories, such as:
121
+ #
122
+ # factory :user, :aliases => [:author] do
123
+ # # ...
124
+ # end
125
+ #
126
+ # factory :post do
127
+ # author
128
+ # end
129
+ #
130
+ # FactoryGirl.create(:post).author.class
131
+ # # => User
132
+ def names
133
+ [name] + (@options[:aliases] || [])
134
+ end
135
+
136
+ def to_create(&block)
137
+ @to_create_block = block
138
+ end
139
+
113
140
  private
114
141
 
115
142
  def class_for (class_or_to_s)
@@ -136,11 +163,15 @@ module FactoryGirl
136
163
  end
137
164
 
138
165
  def assert_valid_options(options)
139
- invalid_keys = options.keys - [:class, :parent, :default_strategy]
166
+ invalid_keys = options.keys - [:class, :parent, :default_strategy, :aliases]
140
167
  unless invalid_keys == []
141
168
  raise ArgumentError, "Unknown arguments: #{invalid_keys.inspect}"
142
169
  end
143
- assert_valid_strategy(options[:default_strategy]) if options[:default_strategy]
170
+ if options[:default_strategy]
171
+ assert_valid_strategy(options[:default_strategy])
172
+ puts "WARNING: default_strategy is deprecated."
173
+ puts "Override to_create if you need to prevent a call to #save!."
174
+ end
144
175
  end
145
176
 
146
177
  def assert_valid_strategy(strategy)
@@ -56,11 +56,11 @@ module FactoryGirl
56
56
  # end
57
57
  #
58
58
  # # Builds (but doesn't save) a Post and a User
59
- # Factory.build(:post)
59
+ # FactoryGirl.build(:post)
60
60
  #
61
61
  # # Builds and saves a User, builds a Post, assigns the User to the
62
62
  # # author association, and saves the User.
63
- # Factory.create(:post)
63
+ # FactoryGirl.create(:post)
64
64
  #
65
65
  def association(name, overrides = {})
66
66
  nil
@@ -70,7 +70,7 @@ module FactoryGirl
70
70
  get(method)
71
71
  end
72
72
 
73
- def result
73
+ def result(to_create)
74
74
  raise NotImplementedError, "Strategies must return a result"
75
75
  end
76
76
  end
@@ -13,7 +13,7 @@ module FactoryGirl
13
13
  @hash[attribute] = value
14
14
  end
15
15
 
16
- def result
16
+ def result(to_create)
17
17
  @hash
18
18
  end
19
19
  end
@@ -14,16 +14,16 @@ module FactoryGirl
14
14
  end
15
15
 
16
16
  def associate(name, factory_name, overrides)
17
- factory = FactoryGirl.factory_by_name(factory_name)
17
+ factory = FactoryGirl.find(factory_name)
18
18
  set(name, factory.run(Proxy::Create, overrides))
19
19
  end
20
20
 
21
21
  def association(factory_name, overrides = {})
22
- factory = FactoryGirl.factory_by_name(factory_name)
22
+ factory = FactoryGirl.find(factory_name)
23
23
  factory.run(Proxy::Create, overrides)
24
24
  end
25
25
 
26
- def result
26
+ def result(to_create)
27
27
  run_callbacks(:after_build)
28
28
  @instance
29
29
  end
@@ -1,9 +1,13 @@
1
1
  module FactoryGirl
2
2
  class Proxy #:nodoc:
3
3
  class Create < Build #:nodoc:
4
- def result
4
+ def result(to_create)
5
5
  run_callbacks(:after_build)
6
- @instance.save!
6
+ if to_create
7
+ to_create.call(@instance)
8
+ else
9
+ @instance.save!
10
+ end
7
11
  run_callbacks(:after_create)
8
12
  @instance
9
13
  end
@@ -46,16 +46,16 @@ module FactoryGirl
46
46
  end
47
47
 
48
48
  def associate(name, factory_name, overrides)
49
- factory = FactoryGirl.factory_by_name(factory_name)
49
+ factory = FactoryGirl.find(factory_name)
50
50
  set(name, factory.run(Proxy::Stub, overrides))
51
51
  end
52
52
 
53
53
  def association(factory_name, overrides = {})
54
- factory = FactoryGirl.factory_by_name(factory_name)
54
+ factory = FactoryGirl.find(factory_name)
55
55
  factory.run(Proxy::Stub, overrides)
56
56
  end
57
57
 
58
- def result
58
+ def result(to_create)
59
59
  run_callbacks(:after_stub)
60
60
  @instance
61
61
  end
@@ -0,0 +1,59 @@
1
+ module FactoryGirl
2
+ class Registry
3
+ def initialize
4
+ @items = {}
5
+ end
6
+
7
+ def add(item)
8
+ item.names.each { |name| add_as(name, item) }
9
+ item
10
+ end
11
+
12
+ def find(name)
13
+ @items[name.to_sym] or raise ArgumentError.new("Not registered: #{name.to_s}")
14
+ end
15
+
16
+ def each(&block)
17
+ @items.each(&block)
18
+ end
19
+
20
+ def [](name)
21
+ find(name)
22
+ end
23
+
24
+ def registered?(name)
25
+ @items.key?(name.to_sym)
26
+ end
27
+
28
+ private
29
+
30
+ def add_as(name, item)
31
+ if registered?(name)
32
+ raise DuplicateDefinitionError, "Already defined: #{name}"
33
+ else
34
+ @items[name.to_sym] = item
35
+ end
36
+ end
37
+ end
38
+
39
+ def self.register(item)
40
+ registry.add(item)
41
+ end
42
+
43
+ def self.registered?(name)
44
+ registry.registered?(name)
45
+ end
46
+
47
+ def self.find(name)
48
+ registry.find(name)
49
+ end
50
+
51
+ def self.registry
52
+ @registry ||= Registry.new
53
+ end
54
+
55
+ def self.registry=(registry)
56
+ @registry = registry
57
+ end
58
+ end
59
+