factory_girl 2.2.0 → 2.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/.autotest +1 -1
  2. data/CONTRIBUTION_GUIDELINES.md +1 -1
  3. data/Changelog +16 -3
  4. data/GETTING_STARTED.md +22 -2
  5. data/Gemfile.lock +1 -1
  6. data/Rakefile +11 -15
  7. data/gemfiles/2.1.gemfile.lock +1 -1
  8. data/gemfiles/2.3.gemfile.lock +1 -1
  9. data/gemfiles/3.0.gemfile.lock +1 -1
  10. data/gemfiles/3.1.gemfile.lock +1 -1
  11. data/lib/factory_girl.rb +8 -3
  12. data/lib/factory_girl/attribute.rb +8 -0
  13. data/lib/factory_girl/attribute/dynamic.rb +1 -5
  14. data/lib/factory_girl/attribute/sequence.rb +1 -5
  15. data/lib/factory_girl/attribute/static.rb +1 -5
  16. data/lib/factory_girl/attribute_list.rb +18 -44
  17. data/lib/factory_girl/callback.rb +5 -0
  18. data/lib/factory_girl/declaration.rb +3 -0
  19. data/lib/factory_girl/declaration/association.rb +8 -0
  20. data/lib/factory_girl/declaration/dynamic.rb +9 -0
  21. data/lib/factory_girl/declaration/implicit.rb +11 -2
  22. data/lib/factory_girl/declaration/static.rb +9 -0
  23. data/lib/factory_girl/declaration_list.rb +48 -0
  24. data/lib/factory_girl/definition.rb +62 -0
  25. data/lib/factory_girl/definition_proxy.rb +11 -11
  26. data/lib/factory_girl/factory.rb +100 -111
  27. data/lib/factory_girl/null_factory.rb +15 -0
  28. data/lib/factory_girl/proxy.rb +14 -9
  29. data/lib/factory_girl/proxy/attributes_for.rb +2 -3
  30. data/lib/factory_girl/proxy/build.rb +12 -20
  31. data/lib/factory_girl/proxy/create.rb +0 -6
  32. data/lib/factory_girl/proxy/stub.rb +4 -10
  33. data/lib/factory_girl/registry.rb +4 -3
  34. data/lib/factory_girl/step_definitions.rb +1 -1
  35. data/lib/factory_girl/syntax/default.rb +3 -4
  36. data/lib/factory_girl/syntax/methods.rb +38 -16
  37. data/lib/factory_girl/trait.rb +13 -21
  38. data/lib/factory_girl/version.rb +1 -1
  39. data/spec/acceptance/modify_factories_spec.rb +1 -1
  40. data/spec/acceptance/traits_spec.rb +87 -1
  41. data/spec/factory_girl/attribute/dynamic_spec.rb +1 -1
  42. data/spec/factory_girl/attribute_list_spec.rb +9 -58
  43. data/spec/factory_girl/declaration_list_spec.rb +71 -0
  44. data/spec/factory_girl/definition_proxy_spec.rb +135 -139
  45. data/spec/factory_girl/definition_spec.rb +81 -0
  46. data/spec/factory_girl/factory_spec.rb +42 -17
  47. data/spec/factory_girl/null_factory_spec.rb +12 -0
  48. data/spec/factory_girl/proxy/build_spec.rb +1 -24
  49. data/spec/factory_girl/proxy/create_spec.rb +14 -11
  50. data/spec/factory_girl/proxy_spec.rb +23 -40
  51. data/spec/factory_girl/registry_spec.rb +4 -3
  52. data/spec/spec_helper.rb +2 -0
  53. data/spec/support/matchers/callback.rb +9 -0
  54. data/spec/support/matchers/declaration.rb +71 -0
  55. data/spec/support/matchers/delegate.rb +44 -0
  56. data/spec/support/matchers/trait.rb +9 -0
  57. data/spec/support/shared_examples/proxy.rb +4 -5
  58. metadata +191 -115
@@ -6,6 +6,15 @@ module FactoryGirl
6
6
  @block = block
7
7
  end
8
8
 
9
+ def ==(other)
10
+ name == other.name &&
11
+ ignored == other.ignored &&
12
+ block == other.block
13
+ end
14
+
15
+ protected
16
+ attr_reader :block
17
+
9
18
  private
10
19
 
11
20
  def build
@@ -6,6 +6,15 @@ module FactoryGirl
6
6
  @factory = factory
7
7
  end
8
8
 
9
+ def ==(other)
10
+ name == other.name &&
11
+ factory == other.factory &&
12
+ ignored == other.ignored
13
+ end
14
+
15
+ protected
16
+ attr_reader :factory
17
+
9
18
  private
10
19
 
11
20
  def build
@@ -14,8 +23,8 @@ module FactoryGirl
14
23
  elsif FactoryGirl.sequences.registered?(name)
15
24
  [Attribute::Sequence.new(name, name, @ignored)]
16
25
  else
17
- trait_root = @factory || FactoryGirl
18
- trait_root.trait_by_name(name).attributes.to_a
26
+ @factory.inherit_traits([name])
27
+ []
19
28
  end
20
29
  end
21
30
  end
@@ -6,6 +6,15 @@ module FactoryGirl
6
6
  @value = value
7
7
  end
8
8
 
9
+ def ==(other)
10
+ name == other.name &&
11
+ value == other.value &&
12
+ ignored == other.ignored
13
+ end
14
+
15
+ protected
16
+ attr_reader :value
17
+
9
18
  private
10
19
 
11
20
  def build
@@ -0,0 +1,48 @@
1
+ module FactoryGirl
2
+ class DeclarationList
3
+ include Enumerable
4
+
5
+ def initialize(name = nil)
6
+ @declarations = []
7
+ @name = name
8
+ @overridable = false
9
+ end
10
+
11
+ def declare_attribute(declaration)
12
+ delete_declaration(declaration) if overridable?
13
+
14
+ @declarations << declaration
15
+ declaration
16
+ end
17
+
18
+ def overridable
19
+ @overridable = true
20
+ end
21
+
22
+ def attribute_list
23
+ AttributeList.new(@name).tap do |list|
24
+ to_attributes.each do |attribute|
25
+ list.define_attribute(attribute)
26
+ end
27
+ end
28
+ end
29
+
30
+ def each(&block)
31
+ @declarations.each(&block)
32
+ end
33
+
34
+ private
35
+
36
+ def delete_declaration(declaration)
37
+ @declarations.delete_if {|decl| decl.name == declaration.name }
38
+ end
39
+
40
+ def to_attributes
41
+ @declarations.inject([]) {|result, declaration| result += declaration.to_attributes }
42
+ end
43
+
44
+ def overridable?
45
+ @overridable
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,62 @@
1
+ module FactoryGirl
2
+ class Definition
3
+ attr_reader :callbacks, :defined_traits, :declarations
4
+
5
+ def initialize(name = nil)
6
+ @declarations = DeclarationList.new(name)
7
+ @callbacks = []
8
+ @defined_traits = []
9
+ @to_create = nil
10
+ @traits = []
11
+ end
12
+
13
+ delegate :declare_attribute, :to => :declarations
14
+
15
+ def attributes
16
+ @attributes ||= declarations.attribute_list
17
+ end
18
+
19
+ def compile
20
+ attributes
21
+ end
22
+
23
+ def overridable
24
+ declarations.overridable
25
+ self
26
+ end
27
+
28
+ def traits
29
+ @traits.reverse.map { |name| trait_by_name(name) }
30
+ end
31
+
32
+ def inherit_traits(new_traits)
33
+ @traits += new_traits
34
+ end
35
+
36
+ def add_callback(callback)
37
+ @callbacks << callback
38
+ end
39
+
40
+ def to_create(&block)
41
+ if block_given?
42
+ @to_create = block
43
+ else
44
+ @to_create
45
+ end
46
+ end
47
+
48
+ def define_trait(trait)
49
+ @defined_traits << trait
50
+ end
51
+
52
+ private
53
+
54
+ def trait_by_name(name)
55
+ trait_for(name) || FactoryGirl.trait_by_name(name)
56
+ end
57
+
58
+ def trait_for(name)
59
+ defined_traits.detect {|trait| trait.name == name }
60
+ end
61
+ end
62
+ end
@@ -8,8 +8,8 @@ module FactoryGirl
8
8
 
9
9
  attr_reader :child_factories
10
10
 
11
- def initialize(factory, ignore = false)
12
- @factory = factory
11
+ def initialize(definition, ignore = false)
12
+ @definition = definition
13
13
  @ignore = ignore
14
14
  @child_factories = []
15
15
  end
@@ -41,11 +41,11 @@ module FactoryGirl
41
41
  Declaration::Static.new(name, value, @ignore)
42
42
  end
43
43
 
44
- @factory.declare_attribute(declaration)
44
+ @definition.declare_attribute(declaration)
45
45
  end
46
46
 
47
47
  def ignore(&block)
48
- proxy = DefinitionProxy.new(@factory, true)
48
+ proxy = DefinitionProxy.new(@definition, true)
49
49
  proxy.instance_eval(&block)
50
50
  end
51
51
 
@@ -82,7 +82,7 @@ module FactoryGirl
82
82
  # are equivalent.
83
83
  def method_missing(name, *args, &block)
84
84
  if args.empty? && block.nil?
85
- @factory.declare_attribute(Declaration::Implicit.new(name, @factory, @ignore))
85
+ @definition.declare_attribute(Declaration::Implicit.new(name, @definition, @ignore))
86
86
  elsif args.first.is_a?(Hash) && args.first.has_key?(:factory)
87
87
  association(name, *args)
88
88
  else
@@ -135,23 +135,23 @@ module FactoryGirl
135
135
  # name of the factory. For example, a "user" association will by
136
136
  # default use the "user" factory.
137
137
  def association(name, options = {})
138
- @factory.declare_attribute(Declaration::Association.new(name, options))
138
+ @definition.declare_attribute(Declaration::Association.new(name, options))
139
139
  end
140
140
 
141
141
  def after_build(&block)
142
- @factory.add_callback(:after_build, &block)
142
+ @definition.add_callback(Callback.new(:after_build, block))
143
143
  end
144
144
 
145
145
  def after_create(&block)
146
- @factory.add_callback(:after_create, &block)
146
+ @definition.add_callback(Callback.new(:after_create, block))
147
147
  end
148
148
 
149
149
  def after_stub(&block)
150
- @factory.add_callback(:after_stub, &block)
150
+ @definition.add_callback(Callback.new(:after_stub, block))
151
151
  end
152
152
 
153
153
  def to_create(&block)
154
- @factory.to_create(&block)
154
+ @definition.to_create(&block)
155
155
  end
156
156
 
157
157
  def factory(name, options = {}, &block)
@@ -159,7 +159,7 @@ module FactoryGirl
159
159
  end
160
160
 
161
161
  def trait(name, &block)
162
- @factory.define_trait(Trait.new(name, &block))
162
+ @definition.define_trait(Trait.new(name, &block))
163
163
  end
164
164
  end
165
165
  end
@@ -3,76 +3,46 @@ require "active_support/inflector"
3
3
 
4
4
  module FactoryGirl
5
5
  class Factory
6
- attr_reader :name #:nodoc:
6
+ attr_reader :name, :definition #:nodoc:
7
7
 
8
8
  def initialize(name, options = {}) #:nodoc:
9
9
  assert_valid_options(options)
10
- @name = name.to_s.underscore.to_sym
10
+ @name = name.is_a?(Symbol) ? name : name.to_s.underscore.to_sym
11
11
  @parent = options[:parent]
12
12
  @aliases = options[:aliases] || []
13
- @traits = options[:traits] || []
14
13
  @class_name = options[:class]
15
14
  @default_strategy = options[:default_strategy]
16
- @defined_traits = []
17
- @attribute_list = AttributeList.new
18
- @compiled = false
15
+ @definition = Definition.new(@name)
16
+
17
+ inherit_traits(options[:traits] || [])
19
18
  end
20
19
 
20
+ delegate :add_callback, :declare_attribute, :to_create, :define_trait,
21
+ :defined_traits, :traits, :inherit_traits, :to => :@definition
22
+
21
23
  def factory_name
22
24
  $stderr.puts "DEPRECATION WARNING: factory.factory_name is deprecated; use factory.name instead."
23
25
  name
24
26
  end
25
27
 
26
28
  def build_class #:nodoc:
27
- @build_class ||= class_name.to_s.camelize.constantize
29
+ class_name.to_s.camelize.constantize
28
30
  end
29
31
 
30
32
  def default_strategy #:nodoc:
31
- @default_strategy || (parent && parent.default_strategy) || :create
32
- end
33
-
34
- def allow_overrides
35
- @compiled = false
36
- @attribute_list.overridable
37
- self
38
- end
39
-
40
- def allow_overrides?
41
- @attribute_list.overridable?
42
- end
43
-
44
- def define_trait(trait)
45
- @defined_traits << trait
46
- end
47
-
48
- def add_callback(name, &block)
49
- @attribute_list.add_callback(Callback.new(name, block))
33
+ @default_strategy || parent.default_strategy || :create
50
34
  end
51
35
 
52
36
  def run(proxy_class, overrides, &block) #:nodoc:
53
- ensure_compiled
54
- proxy = proxy_class.new(build_class)
55
- callbacks.each { |callback| proxy.add_callback(callback) }
56
- overrides = overrides.symbolize_keys
57
-
58
- attributes.each do |attribute|
59
- factory_overrides = overrides.select { |attr, val| attribute.aliases_for?(attr) }
60
- if factory_overrides.empty?
61
- attribute.add_to(proxy)
62
- else
63
- factory_overrides.each do |attr, val|
64
- if attribute.ignored
65
- proxy.set_ignored(attr, val)
66
- else
67
- proxy.set(attr, val)
68
- end
69
-
70
- overrides.delete(attr)
71
- end
72
- end
73
- end
74
- overrides.each { |attr, val| proxy.set(attr, val) }
75
- result = proxy.result(@to_create_block)
37
+ runner_options = {
38
+ :attributes => attributes,
39
+ :callbacks => callbacks,
40
+ :to_create => to_create,
41
+ :build_class => build_class,
42
+ :proxy_class => proxy_class
43
+ }
44
+
45
+ result = Runner.new(runner_options).run(overrides)
76
46
 
77
47
  block ? block.call(result) : result
78
48
  end
@@ -85,16 +55,6 @@ module FactoryGirl
85
55
  attributes.select {|attribute| attribute.association? }
86
56
  end
87
57
 
88
- def trait_by_name(name)
89
- if existing_attribute = trait_for(name)
90
- existing_attribute
91
- elsif parent
92
- parent.trait_by_name(name)
93
- else
94
- FactoryGirl.trait_by_name(name)
95
- end
96
- end
97
-
98
58
  # Names for this factory, including aliases.
99
59
  #
100
60
  # Example:
@@ -124,94 +84,123 @@ module FactoryGirl
124
84
  [name] + @aliases
125
85
  end
126
86
 
127
- def to_create(&block)
128
- @to_create_block = block
129
- end
130
-
131
- def ensure_compiled
132
- compile unless @compiled
87
+ def compile
88
+ parent.defined_traits.each {|trait| define_trait(trait) }
89
+ parent.compile
90
+ @definition.compile
133
91
  end
134
92
 
135
- def declare_attribute(declaration)
136
- @attribute_list.declare_attribute(declaration)
93
+ def with_traits(traits)
94
+ self.clone.tap do |factory_with_traits|
95
+ factory_with_traits.inherit_traits traits
96
+ end
137
97
  end
138
98
 
139
99
  protected
140
100
 
141
101
  def class_name #:nodoc:
142
- @class_name || (parent && parent.class_name) || name
102
+ @class_name || parent.class_name || name
143
103
  end
144
104
 
145
105
  def attributes
146
- ensure_compiled
147
- AttributeList.new.tap do |list|
148
- @traits.reverse.map { |name| trait_by_name(name) }.each do |trait|
106
+ compile
107
+ AttributeList.new(@name).tap do |list|
108
+ traits.each do |trait|
149
109
  list.apply_attributes(trait.attributes)
150
110
  end
151
111
 
152
- list.apply_attributes(@attribute_list)
153
- list.apply_attributes(parent.attributes) if parent
112
+ list.apply_attributes(@definition.attributes)
113
+ list.apply_attributes(parent.attributes)
154
114
  end
155
115
  end
156
116
 
157
- private
158
-
159
117
  def callbacks
160
- attributes.callbacks
118
+ [parent.callbacks, traits.map(&:callbacks), @definition.callbacks].flatten
161
119
  end
162
120
 
163
- def compile
164
- inherit_factory(parent) if parent
121
+ private
165
122
 
166
- declarations.each do |declaration|
167
- declaration.to_attributes.each do |attribute|
168
- define_attribute(attribute)
169
- end
170
- end
123
+ def assert_valid_options(options)
124
+ options.assert_valid_keys(:class, :parent, :default_strategy, :aliases, :traits)
171
125
 
172
- @compiled = true
126
+ if options[:default_strategy]
127
+ Proxy.ensure_strategy_exists!(options[:default_strategy])
128
+ $stderr.puts "DEPRECATION WARNING: default_strategy is deprecated."
129
+ $stderr.puts "Override to_create if you need to prevent a call to #save!."
130
+ end
173
131
  end
174
132
 
175
- def inherit_factory(parent) #:nodoc:
176
- parent.ensure_compiled
177
- allow_overrides if parent.allow_overrides?
133
+ def parent
134
+ if @parent
135
+ FactoryGirl.factory_by_name(@parent)
136
+ else
137
+ NullFactory.new
138
+ end
178
139
  end
179
140
 
180
- def declarations
181
- @attribute_list.declarations
141
+ def initialize_copy(source)
142
+ super
143
+ @definition = @definition.clone
182
144
  end
183
145
 
184
- def define_attribute(attribute)
185
- if attribute.respond_to?(:factory) && attribute.factory == self.name
186
- raise AssociationDefinitionError, "Self-referencing association '#{attribute.name}' in factory '#{self.name}'"
146
+ class Runner
147
+ def initialize(options = {})
148
+ @attributes = options[:attributes]
149
+ @callbacks = options[:callbacks]
150
+ @to_create = options[:to_create]
151
+ @build_class = options[:build_class]
152
+ @proxy_class = options[:proxy_class]
153
+
154
+ @overrides = {}
187
155
  end
188
156
 
189
- @attribute_list.define_attribute(attribute)
190
- end
157
+ def run(overrides = {})
158
+ @overrides = overrides.symbolize_keys
191
159
 
192
- def assert_valid_options(options)
193
- options.assert_valid_keys(:class, :parent, :default_strategy, :aliases, :traits)
160
+ apply_attributes
161
+ apply_remaining_overrides
194
162
 
195
- if options[:default_strategy]
196
- assert_valid_strategy(options[:default_strategy])
197
- $stderr.puts "DEPRECATION WARNING: default_strategy is deprecated."
198
- $stderr.puts "Override to_create if you need to prevent a call to #save!."
163
+ proxy.result(@to_create)
199
164
  end
200
- end
201
165
 
202
- def assert_valid_strategy(strategy)
203
- unless Proxy.const_defined? strategy.to_s.camelize
204
- raise ArgumentError, "Unknown strategy: #{strategy}"
166
+ private
167
+
168
+ def apply_attributes
169
+ @attributes.each do |attribute|
170
+ if overrides_for_attribute(attribute).any?
171
+ handle_attribute_with_overrides(attribute)
172
+ else
173
+ handle_attribute_without_overrides(attribute)
174
+ end
175
+ end
205
176
  end
206
- end
207
177
 
208
- def trait_for(name)
209
- @defined_traits.detect {|trait| trait.name == name }
210
- end
178
+ def apply_remaining_overrides
179
+ @overrides.each { |attr, val| add_static_attribute(attr, val) }
180
+ end
211
181
 
212
- def parent
213
- return unless @parent
214
- FactoryGirl.factory_by_name(@parent)
182
+ def overrides_for_attribute(attribute)
183
+ @overrides.select { |attr, val| attribute.aliases_for?(attr) }
184
+ end
185
+
186
+ def handle_attribute_with_overrides(attribute)
187
+ overrides_for_attribute(attribute).each do |attr, val|
188
+ add_static_attribute(attr, val, attribute.ignored)
189
+ @overrides.delete(attr)
190
+ end
191
+ end
192
+
193
+ def add_static_attribute(attr, val, ignored = false)
194
+ Attribute::Static.new(attr, val, ignored).add_to(proxy)
195
+ end
196
+
197
+ def handle_attribute_without_overrides(attribute)
198
+ attribute.add_to(proxy)
199
+ end
200
+
201
+ def proxy
202
+ @proxy ||= @proxy_class.new(@build_class, @callbacks)
203
+ end
215
204
  end
216
205
  end
217
206
  end