factory_girl 2.1.0 → 2.1.2

Sign up to get free protection for your applications and to get access to all the features.
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