factory_girl 2.1.0 → 2.1.2

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 (43) hide show
  1. data/.travis.yml +3 -0
  2. data/Changelog +9 -0
  3. data/GETTING_STARTED.md +47 -16
  4. data/Gemfile.lock +10 -9
  5. data/gemfiles/2.1.gemfile.lock +2 -1
  6. data/gemfiles/2.3.gemfile.lock +2 -1
  7. data/gemfiles/3.0.gemfile.lock +2 -1
  8. data/gemfiles/3.1.gemfile.lock +2 -1
  9. data/lib/factory_girl.rb +6 -3
  10. data/lib/factory_girl/attribute/static.rb +8 -0
  11. data/lib/factory_girl/attribute_list.rb +20 -12
  12. data/lib/factory_girl/callback.rb +30 -0
  13. data/lib/factory_girl/declaration.rb +19 -0
  14. data/lib/factory_girl/declaration/association.rb +17 -0
  15. data/lib/factory_girl/declaration/dynamic.rb +16 -0
  16. data/lib/factory_girl/declaration/implicit.rb +23 -0
  17. data/lib/factory_girl/declaration/static.rb +16 -0
  18. data/lib/factory_girl/definition_proxy.rb +6 -6
  19. data/lib/factory_girl/factory.rb +63 -79
  20. data/lib/factory_girl/proxy.rb +7 -11
  21. data/lib/factory_girl/proxy/attributes_for.rb +1 -0
  22. data/lib/factory_girl/proxy/build.rb +1 -0
  23. data/lib/factory_girl/proxy/stub.rb +1 -0
  24. data/lib/factory_girl/syntax/default.rb +4 -2
  25. data/lib/factory_girl/syntax/vintage.rb +1 -1
  26. data/lib/factory_girl/trait.rb +12 -4
  27. data/lib/factory_girl/version.rb +1 -1
  28. data/spec/acceptance/callbacks_spec.rb +7 -1
  29. data/spec/acceptance/modify_inherited_spec.rb +52 -0
  30. data/spec/acceptance/syntax/vintage_spec.rb +19 -7
  31. data/spec/factory_girl/attribute_list_spec.rb +18 -45
  32. data/spec/factory_girl/callback_spec.rb +41 -0
  33. data/spec/factory_girl/{attribute → declaration}/implicit_spec.rb +16 -11
  34. data/spec/factory_girl/definition_proxy_spec.rb +16 -12
  35. data/spec/factory_girl/factory_spec.rb +43 -34
  36. data/spec/factory_girl/proxy/create_spec.rb +7 -9
  37. data/spec/factory_girl/proxy_spec.rb +26 -39
  38. data/spec/support/shared_examples/proxy.rb +1 -1
  39. metadata +137 -114
  40. data/lib/factory_girl/attribute/callback.rb +0 -14
  41. data/lib/factory_girl/attribute/implicit.rb +0 -39
  42. data/lib/factory_girl/attribute/trait.rb +0 -22
  43. data/spec/factory_girl/attribute/callback_spec.rb +0 -22
@@ -0,0 +1,23 @@
1
+ module FactoryGirl
2
+ class Declaration
3
+ class Implicit < Declaration
4
+ def initialize(name, factory = nil)
5
+ super(name)
6
+ @factory = factory
7
+ end
8
+
9
+ private
10
+
11
+ def build
12
+ if FactoryGirl.factories.registered?(name)
13
+ [Attribute::Association.new(name, name, {})]
14
+ elsif FactoryGirl.sequences.registered?(name)
15
+ [Attribute::Sequence.new(name, name)]
16
+ else
17
+ trait_root = @factory || FactoryGirl
18
+ trait_root.trait_by_name(name).attributes.to_a
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,16 @@
1
+ module FactoryGirl
2
+ class Declaration
3
+ class Static < Declaration
4
+ def initialize(name, value)
5
+ super(name)
6
+ @value = value
7
+ end
8
+
9
+ private
10
+
11
+ def build
12
+ [Attribute::Static.new(name, @value)]
13
+ end
14
+ end
15
+ end
16
+ end
@@ -36,13 +36,14 @@ module FactoryGirl
36
36
  if value
37
37
  raise AttributeDefinitionError, "Both value and block given"
38
38
  else
39
- attribute = Attribute::Dynamic.new(name, block)
39
+ declaration = Declaration::Dynamic.new(name, block)
40
40
  end
41
41
  else
42
- attribute = Attribute::Static.new(name, value)
42
+ declaration = FactoryGirl::Declaration::Static.new(name, value)
43
43
  end
44
44
 
45
- @factory.define_attribute(attribute)
45
+ @factory.declare_attribute(declaration)
46
+ declaration
46
47
  end
47
48
 
48
49
  # Calls add_attribute using the missing method name as the name of the
@@ -78,7 +79,7 @@ module FactoryGirl
78
79
  # are equivalent.
79
80
  def method_missing(name, *args, &block)
80
81
  if args.empty? && block.nil?
81
- @factory.define_attribute(Attribute::Implicit.new(name, @factory))
82
+ @factory.declare_attribute(Declaration::Implicit.new(name, @factory))
82
83
  elsif args.first.is_a?(Hash) && args.first.has_key?(:factory)
83
84
  association(name, *args)
84
85
  else
@@ -131,8 +132,7 @@ module FactoryGirl
131
132
  # name of the factory. For example, a "user" association will by
132
133
  # default use the "user" factory.
133
134
  def association(name, options = {})
134
- factory_name = options.delete(:factory) || name
135
- @factory.define_attribute(Attribute::Association.new(name, factory_name, options))
135
+ @factory.declare_attribute(Declaration::Association.new(name, options))
136
136
  end
137
137
 
138
138
  def after_build(&block)
@@ -1,3 +1,6 @@
1
+ require "active_support/core_ext/hash/keys"
2
+ require "active_support/inflector"
3
+
1
4
  module FactoryGirl
2
5
  # Raised when a factory is defined that attempts to instantiate itself.
3
6
  class AssociationDefinitionError < RuntimeError
@@ -13,7 +16,6 @@ module FactoryGirl
13
16
 
14
17
  class Factory
15
18
  attr_reader :name #:nodoc:
16
- attr_reader :traits #:nodoc:
17
19
 
18
20
  def factory_name
19
21
  puts "WARNING: factory.factory_name is deprecated. Use factory.name instead."
@@ -25,7 +27,7 @@ module FactoryGirl
25
27
  end
26
28
 
27
29
  def build_class #:nodoc:
28
- @build_class ||= class_for(class_name)
30
+ @build_class ||= class_name.to_s.camelize.constantize
29
31
  end
30
32
 
31
33
  def default_strategy #:nodoc:
@@ -34,18 +36,19 @@ module FactoryGirl
34
36
 
35
37
  def initialize(name, options = {}) #:nodoc:
36
38
  assert_valid_options(options)
37
- @name = factory_name_for(name)
38
- @parent = options[:parent]
39
- @options = options
40
- @traits = []
41
- @children = []
42
- @attribute_list = AttributeList.new
43
- @inherited_attribute_list = AttributeList.new
39
+ @name = name.to_s.underscore.to_sym
40
+ @parent = options[:parent]
41
+ @parent_factory = nil
42
+ @options = options
43
+ @defined_traits = []
44
+ @traits = []
45
+ @children = []
46
+ @attribute_list = AttributeList.new
47
+ @compiled = false
44
48
  end
45
49
 
46
50
  def allow_overrides
47
51
  @attribute_list.overridable
48
- @inherited_attribute_list.overridable
49
52
  self
50
53
  end
51
54
 
@@ -53,56 +56,48 @@ module FactoryGirl
53
56
  @attribute_list.overridable?
54
57
  end
55
58
 
56
- def inherit_from(parent) #:nodoc:
59
+ def inherit_factory(parent) #:nodoc:
57
60
  @options[:class] ||= parent.class_name
58
61
  @options[:default_strategy] ||= parent.default_strategy
59
62
 
60
63
  allow_overrides if parent.allow_overrides?
61
64
  parent.add_child(self)
62
65
 
63
- @inherited_attribute_list.apply_attributes(parent.attributes)
66
+ @parent_factory = parent
64
67
  end
65
68
 
66
69
  def add_child(factory)
67
70
  @children << factory unless @children.include?(factory)
68
71
  end
69
72
 
70
- def apply_traits(traits) #:nodoc:
71
- traits.reverse.map { |name| trait_by_name(name) }.each do |trait|
72
- apply_attributes(trait.attributes)
73
- end
74
- end
75
-
76
- def apply_attributes(attributes_to_apply)
77
- @attribute_list.apply_attributes(attributes_to_apply)
78
- end
79
-
80
- def define_attribute(attribute)
81
- if attribute.respond_to?(:factory) && attribute.factory == self.name
82
- raise AssociationDefinitionError, "Self-referencing association '#{attribute.name}' in factory '#{self.name}'"
83
- end
84
-
85
- @attribute_list.define_attribute(attribute).tap { update_children }
73
+ def inherit_traits(traits)
74
+ @traits = traits
86
75
  end
87
76
 
88
77
  def define_trait(trait)
89
- @traits << trait
78
+ @defined_traits << trait
90
79
  end
91
80
 
92
81
  def add_callback(name, &block)
93
- @attribute_list.add_callback(name, &block)
82
+ @attribute_list.add_callback(Callback.new(name, block))
94
83
  end
95
84
 
96
85
  def attributes
86
+ ensure_compiled
97
87
  AttributeList.new.tap do |list|
88
+ @traits.reverse.map { |name| trait_by_name(name) }.each do |trait|
89
+ list.apply_attributes(trait.attributes)
90
+ end
91
+
98
92
  list.apply_attributes(@attribute_list)
99
- list.apply_attributes(@inherited_attribute_list)
100
- end.to_a
93
+ list.apply_attributes(@parent_factory.attributes) if @parent_factory
94
+ end
101
95
  end
102
96
 
103
97
  def run(proxy_class, overrides) #:nodoc:
104
98
  proxy = proxy_class.new(build_class)
105
- overrides = symbolize_keys(overrides)
99
+ callbacks.each { |callback| proxy.add_callback(callback) }
100
+ overrides = overrides.symbolize_keys
106
101
 
107
102
  attributes.each do |attribute|
108
103
  factory_overrides = overrides.select { |attr, val| attribute.aliases_for?(attr) }
@@ -117,7 +112,7 @@ module FactoryGirl
117
112
  end
118
113
 
119
114
  def human_names
120
- names.map {|name| name.to_s.gsub('_', ' ') }
115
+ names.map {|name| name.to_s.humanize.downcase }
121
116
  end
122
117
 
123
118
  def associations
@@ -167,36 +162,45 @@ module FactoryGirl
167
162
  @to_create_block = block
168
163
  end
169
164
 
170
- private
171
-
172
- def update_children
173
- @children.each { |child| child.inherit_from(self) }
165
+ def callbacks
166
+ attributes.callbacks
174
167
  end
175
168
 
176
- def class_for (class_or_to_s)
177
- if class_or_to_s.respond_to?(:to_sym)
178
- class_name = variable_name_to_class_name(class_or_to_s)
179
- class_name.split('::').inject(Object) do |object, string|
180
- object.const_get(string)
169
+ def compile
170
+ declarations.each do |declaration|
171
+ declaration.to_attributes.each do |attribute|
172
+ define_attribute(attribute)
181
173
  end
182
- else
183
- class_or_to_s
184
174
  end
175
+ @compiled = true
185
176
  end
186
177
 
187
- def factory_name_for(class_or_to_s)
188
- if class_or_to_s.respond_to?(:to_sym)
189
- class_or_to_s.to_sym
190
- else
191
- class_name_to_variable_name(class_or_to_s).to_sym
178
+ def declare_attribute(declaration)
179
+ @attribute_list.declare_attribute(declaration)
180
+ end
181
+
182
+ private
183
+
184
+ def declarations
185
+ @attribute_list.declarations
186
+ end
187
+
188
+ def update_children
189
+ @children.each { |child| child.inherit_factory(self) }
190
+ end
191
+
192
+ def define_attribute(attribute)
193
+ if attribute.respond_to?(:factory) && attribute.factory == self.name
194
+ raise AssociationDefinitionError, "Self-referencing association '#{attribute.name}' in factory '#{self.name}'"
192
195
  end
196
+
197
+ @attribute_list.define_attribute(attribute)
198
+ update_children if allow_overrides?
193
199
  end
194
200
 
195
201
  def assert_valid_options(options)
196
- invalid_keys = options.keys - [:class, :parent, :default_strategy, :aliases, :traits]
197
- unless invalid_keys == []
198
- raise ArgumentError, "Unknown arguments: #{invalid_keys.inspect}"
199
- end
202
+ options.assert_valid_keys(:class, :parent, :default_strategy, :aliases, :traits)
203
+
200
204
  if options[:default_strategy]
201
205
  assert_valid_strategy(options[:default_strategy])
202
206
  puts "WARNING: default_strategy is deprecated."
@@ -205,37 +209,17 @@ module FactoryGirl
205
209
  end
206
210
 
207
211
  def assert_valid_strategy(strategy)
208
- unless Proxy.const_defined? variable_name_to_class_name(strategy)
212
+ unless Proxy.const_defined? strategy.to_s.camelize
209
213
  raise ArgumentError, "Unknown strategy: #{strategy}"
210
214
  end
211
215
  end
212
216
 
213
- # Based on ActiveSupport's underscore inflector
214
- def class_name_to_variable_name(name)
215
- name.to_s.gsub(/::/, '/').
216
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
217
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
218
- tr("-", "_").
219
- downcase
220
- end
221
-
222
- # Based on ActiveSupport's camelize inflector
223
- def variable_name_to_class_name(name)
224
- name.to_s.
225
- gsub(/\/(.?)/) { "::#{$1.upcase}" }.
226
- gsub(/(?:^|_)(.)/) { $1.upcase }
227
- end
228
-
229
- # From ActiveSupport
230
- def symbolize_keys(hash)
231
- hash.inject({}) do |options, (key, value)|
232
- options[(key.to_sym rescue key) || key] = value
233
- options
234
- end
217
+ def trait_for(name)
218
+ @defined_traits.detect {|trait| trait.name == name }
235
219
  end
236
220
 
237
- def trait_for(name)
238
- traits.detect {|trait| trait.name == name }
221
+ def ensure_compiled
222
+ compile unless @compiled
239
223
  end
240
224
  end
241
225
  end
@@ -4,6 +4,7 @@ module FactoryGirl
4
4
  attr_reader :callbacks
5
5
 
6
6
  def initialize(klass)
7
+ @callbacks = {}
7
8
  end
8
9
 
9
10
  def get(attribute)
@@ -15,20 +16,15 @@ module FactoryGirl
15
16
  def associate(name, factory, attributes)
16
17
  end
17
18
 
18
- def add_callback(name, block)
19
- @callbacks ||= {}
20
- @callbacks[name] ||= []
21
- @callbacks[name] << block
19
+ def add_callback(callback)
20
+ @callbacks[callback.name] ||= []
21
+ @callbacks[callback.name] << callback
22
22
  end
23
23
 
24
24
  def run_callbacks(name)
25
- if @callbacks && @callbacks[name]
26
- @callbacks[name].each do |block|
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
25
+ if @callbacks[name]
26
+ @callbacks[name].each do |callback|
27
+ callback.run(@instance, self)
32
28
  end
33
29
  end
34
30
  end
@@ -2,6 +2,7 @@ module FactoryGirl
2
2
  class Proxy #:nodoc:
3
3
  class AttributesFor < Proxy #:nodoc:
4
4
  def initialize(klass)
5
+ super(klass)
5
6
  @hash = {}
6
7
  @ignored_attributes = {}
7
8
  end
@@ -2,6 +2,7 @@ module FactoryGirl
2
2
  class Proxy #:nodoc:
3
3
  class Build < Proxy #:nodoc:
4
4
  def initialize(klass)
5
+ super(klass)
5
6
  @instance = klass.new
6
7
  @ignored_attributes = {}
7
8
  end
@@ -4,6 +4,7 @@ module FactoryGirl
4
4
  @@next_id = 1000
5
5
 
6
6
  def initialize(klass)
7
+ super(klass)
7
8
  @instance = klass.new
8
9
  @ignored_attributes = {}
9
10
  @instance.id = next_id
@@ -22,12 +22,13 @@ module FactoryGirl
22
22
  proxy.instance_eval(&block) if block_given?
23
23
 
24
24
  if traits = options.delete(:traits)
25
- factory.apply_traits(traits)
25
+ factory.inherit_traits(traits)
26
26
  end
27
27
 
28
28
  if parent = options.delete(:parent)
29
- factory.inherit_from(FactoryGirl.factory_by_name(parent))
29
+ factory.inherit_factory(FactoryGirl.factory_by_name(parent))
30
30
  end
31
+
31
32
  FactoryGirl.register_factory(factory)
32
33
 
33
34
  proxy.child_factories.each do |(child_name, child_options, child_block)|
@@ -53,6 +54,7 @@ module FactoryGirl
53
54
  factory = FactoryGirl.factory_by_name(name).allow_overrides
54
55
  proxy = FactoryGirl::DefinitionProxy.new(factory)
55
56
  proxy.instance_eval(&block)
57
+ factory.compile
56
58
  end
57
59
  end
58
60
  end
@@ -29,7 +29,7 @@ module FactoryGirl
29
29
  proxy = FactoryGirl::DefinitionProxy.new(factory)
30
30
  yield(proxy)
31
31
  if parent = options.delete(:parent)
32
- factory.inherit_from(FactoryGirl.factory_by_name(parent))
32
+ factory.inherit_factory(FactoryGirl.factory_by_name(parent))
33
33
  end
34
34
  FactoryGirl.register_factory(factory)
35
35
  end
@@ -10,16 +10,24 @@ module FactoryGirl
10
10
  proxy.instance_eval(&block) if block_given?
11
11
  end
12
12
 
13
- def define_attribute(attribute)
14
- @attribute_list.define_attribute(attribute)
13
+ def declare_attribute(declaration)
14
+ @attribute_list.declare_attribute(declaration)
15
+ declaration
15
16
  end
16
17
 
17
18
  def add_callback(name, &block)
18
- @attribute_list.add_callback(name, &block)
19
+ @attribute_list.add_callback(Callback.new(name, block))
19
20
  end
20
21
 
21
22
  def attributes
22
- @attribute_list.to_a
23
+ AttributeList.new.tap do |list|
24
+ @attribute_list.declarations.each do |declaration|
25
+ declaration.to_attributes.each do |attribute|
26
+ list.define_attribute(attribute)
27
+ end
28
+ end
29
+ list.apply_attributes @attribute_list
30
+ end
23
31
  end
24
32
 
25
33
  def names
@@ -1,4 +1,4 @@
1
1
  module FactoryGirl
2
- VERSION = "2.1.0"
2
+ VERSION = "2.1.2"
3
3
  end
4
4