vitalish-factory_girl 1.2.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/CONTRIBUTION_GUIDELINES.rdoc +9 -0
  2. data/Changelog +29 -0
  3. data/LICENSE +19 -0
  4. data/README.rdoc +282 -0
  5. data/Rakefile +66 -0
  6. data/lib/factory_girl.rb +24 -0
  7. data/lib/factory_girl/aliases.rb +21 -0
  8. data/lib/factory_girl/attribute.rb +29 -0
  9. data/lib/factory_girl/attribute/association.rb +20 -0
  10. data/lib/factory_girl/attribute/callback.rb +16 -0
  11. data/lib/factory_girl/attribute/dynamic.rb +20 -0
  12. data/lib/factory_girl/attribute/static.rb +17 -0
  13. data/lib/factory_girl/factory.rb +215 -0
  14. data/lib/factory_girl/proxy.rb +77 -0
  15. data/lib/factory_girl/proxy/attributes_for.rb +21 -0
  16. data/lib/factory_girl/proxy/build.rb +32 -0
  17. data/lib/factory_girl/proxy/create.rb +12 -0
  18. data/lib/factory_girl/proxy/stub.rb +64 -0
  19. data/lib/factory_girl/sequence.rb +28 -0
  20. data/lib/factory_girl/step_definitions.rb +60 -0
  21. data/lib/factory_girl/syntax.rb +12 -0
  22. data/lib/factory_girl/syntax/blueprint.rb +42 -0
  23. data/lib/factory_girl/syntax/generate.rb +73 -0
  24. data/lib/factory_girl/syntax/make.rb +41 -0
  25. data/lib/factory_girl/syntax/sham.rb +45 -0
  26. data/spec/factory_girl/aliases_spec.rb +33 -0
  27. data/spec/factory_girl/attribute/association_spec.rb +29 -0
  28. data/spec/factory_girl/attribute/callback_spec.rb +23 -0
  29. data/spec/factory_girl/attribute/dynamic_spec.rb +60 -0
  30. data/spec/factory_girl/attribute/static_spec.rb +29 -0
  31. data/spec/factory_girl/attribute_spec.rb +30 -0
  32. data/spec/factory_girl/factory_spec.rb +411 -0
  33. data/spec/factory_girl/proxy/attributes_for_spec.rb +52 -0
  34. data/spec/factory_girl/proxy/build_spec.rb +86 -0
  35. data/spec/factory_girl/proxy/create_spec.rb +99 -0
  36. data/spec/factory_girl/proxy/stub_spec.rb +80 -0
  37. data/spec/factory_girl/proxy_spec.rb +84 -0
  38. data/spec/factory_girl/sequence_spec.rb +43 -0
  39. data/spec/spec_helper.rb +93 -0
  40. metadata +119 -0
@@ -0,0 +1,29 @@
1
+ module FactoryGirl
2
+
3
+ # Raised when defining an invalid attribute:
4
+ # * Defining an attribute which has a name ending in "="
5
+ # * Defining an attribute with both a static and lazy value
6
+ # * Defining an attribute twice in the same factory
7
+ class AttributeDefinitionError < RuntimeError
8
+ end
9
+
10
+ class Attribute #:nodoc:
11
+
12
+ attr_reader :name
13
+
14
+ def initialize(name)
15
+ @name = name.to_sym
16
+
17
+ if @name.to_s =~ /=$/
18
+ attribute_name = $`
19
+ raise AttributeDefinitionError,
20
+ "factory_girl uses 'f.#{attribute_name} value' syntax " +
21
+ "rather than 'f.#{attribute_name} = value'"
22
+ end
23
+ end
24
+
25
+ def add_to(proxy)
26
+ end
27
+ end
28
+
29
+ end
@@ -0,0 +1,20 @@
1
+ module FactoryGirl
2
+ class Attribute #:nodoc:
3
+
4
+ class Association < Attribute #:nodoc:
5
+
6
+ attr_reader :factory
7
+
8
+ def initialize(name, factory, overrides)
9
+ super(name)
10
+ @factory = factory
11
+ @overrides = overrides
12
+ end
13
+
14
+ def add_to(proxy)
15
+ proxy.associate(name, @factory, @overrides)
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ module FactoryGirl
2
+ class Attribute #:nodoc:
3
+
4
+ class Callback < Attribute #:nodoc:
5
+ def initialize(name, block)
6
+ @name = name.to_sym
7
+ @block = block
8
+ end
9
+
10
+ def add_to(proxy)
11
+ proxy.add_callback(name, @block)
12
+ end
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,20 @@
1
+ module FactoryGirl
2
+ class Attribute #:nodoc:
3
+
4
+ class Dynamic < Attribute #:nodoc:
5
+ def initialize(name, block)
6
+ super(name)
7
+ @block = block
8
+ end
9
+
10
+ def add_to(proxy)
11
+ value = @block.arity == 1 ? @block.call(proxy) : proxy.instance_eval(&@block)
12
+ if FactoryGirl::Sequence === value
13
+ raise SequenceAbuseError
14
+ end
15
+ proxy.set(name, value)
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ module FactoryGirl
2
+ class Attribute #:nodoc:
3
+
4
+ class Static < Attribute #:nodoc:
5
+
6
+ def initialize(name, value)
7
+ super(name)
8
+ @value = value
9
+ end
10
+
11
+ def add_to(proxy)
12
+ proxy.set(name, @value)
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,215 @@
1
+ module FactoryGirl
2
+ class << self
3
+ attr_accessor :factories #:nodoc:
4
+ end
5
+
6
+ self.factories = {}
7
+
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
+ if options[:as]
14
+ name = options[:as]
15
+ else
16
+ name = factory.name
17
+ factory.aliases.each do |alias_name|
18
+ register_factory(factory, :as => alias_name)
19
+ end
20
+ end
21
+
22
+ if self.factories[name]
23
+ raise DuplicateDefinitionError, "Factory already defined: #{name}"
24
+ end
25
+
26
+ self.factories[name] = factory
27
+ end
28
+
29
+ # Raised when a factory is defined that attempts to instantiate itself.
30
+ class AssociationDefinitionError < RuntimeError
31
+ end
32
+
33
+ # Raised when a callback is defined that has an invalid name
34
+ class InvalidCallbackNameError < RuntimeError
35
+ end
36
+
37
+ # Raised when a factory is defined with the same name as a previously-defined factory.
38
+ class DuplicateDefinitionError < RuntimeError
39
+ end
40
+
41
+ class Factory
42
+ attr_reader :name #:nodoc:
43
+ attr_reader :attributes #:nodoc:
44
+
45
+ def factory_name
46
+ puts "WARNING: factory.factory_name is deprecated. Use factory.name instead."
47
+ name
48
+ end
49
+
50
+ def class_name #:nodoc:
51
+ @options[:class] || name
52
+ end
53
+
54
+ def build_class #:nodoc:
55
+ @build_class ||= class_for(class_name)
56
+ end
57
+
58
+ def default_strategy #:nodoc:
59
+ @options[:default_strategy] || :create
60
+ end
61
+
62
+ def initialize(name, options = {}) #:nodoc:
63
+ assert_valid_options(options)
64
+ @name = factory_name_for(name)
65
+ @options = options
66
+ @attributes = []
67
+ end
68
+
69
+ def inherit_from(parent) #:nodoc:
70
+ @options[:class] ||= parent.class_name
71
+ @options[:default_strategy] ||= parent.default_strategy
72
+
73
+ new_attributes = []
74
+ parent.attributes.each do |attribute|
75
+ unless attribute_defined?(attribute.name)
76
+ new_attributes << attribute.clone
77
+ end
78
+ end
79
+ @attributes.unshift *new_attributes
80
+ end
81
+
82
+ def define_attribute(attribute)
83
+ name = attribute.name
84
+ # TODO: move these checks into Attribute
85
+ if attribute_defined?(name)
86
+ raise AttributeDefinitionError, "Attribute already defined: #{name}"
87
+ end
88
+ if attribute.respond_to?(:factory) && attribute.factory == self.name
89
+ raise AssociationDefinitionError, "Self-referencing association '#{name}' in factory '#{self.name}'"
90
+ end
91
+ @attributes << attribute
92
+ end
93
+
94
+ def add_callback(name, &block)
95
+ unless [:after_build, :after_create, :after_stub].include?(name.to_sym)
96
+ raise InvalidCallbackNameError, "#{name} is not a valid callback name. Valid callback names are :after_build, :after_create, and :after_stub"
97
+ end
98
+ @attributes << Attribute::Callback.new(name.to_sym, block)
99
+ end
100
+
101
+ def run (proxy_class, overrides) #:nodoc:
102
+ proxy = proxy_class.new(build_class)
103
+ overrides = symbolize_keys(overrides)
104
+ overrides.each {|attr, val| proxy.set(attr, val) }
105
+ passed_keys = overrides.keys.collect {|k| FactoryGirl.aliases_for(k) }.flatten
106
+ @attributes.each do |attribute|
107
+ unless passed_keys.include?(attribute.name)
108
+ attribute.add_to(proxy)
109
+ end
110
+ end
111
+ proxy.result
112
+ end
113
+
114
+ def human_name(*args, &block)
115
+ name.to_s.gsub('_', ' ')
116
+ end
117
+
118
+ def associations
119
+ attributes.select {|attribute| attribute.is_a?(Attribute::Association) }
120
+ end
121
+
122
+ # Alternate names for this factory.
123
+ #
124
+ # Example:
125
+ #
126
+ # factory :user, :aliases => [:author] do
127
+ # # ...
128
+ # end
129
+ #
130
+ # Factory(:author).class
131
+ # # => User
132
+ #
133
+ # Because an attribute defined without a value or block will build an
134
+ # association with the same name, this allows associations to be defined
135
+ # without factories, such as:
136
+ #
137
+ # factory :user, :aliases => [:author] do
138
+ # # ...
139
+ # end
140
+ #
141
+ # factory :post do
142
+ # author
143
+ # end
144
+ #
145
+ # Factory(:post).author.class
146
+ # # => User
147
+ def aliases
148
+ @options[:aliases] || []
149
+ end
150
+
151
+ private
152
+
153
+ def class_for (class_or_to_s)
154
+ if class_or_to_s.respond_to?(:to_sym)
155
+ class_name = variable_name_to_class_name(class_or_to_s)
156
+ class_name.split('::').inject(Object) do |object, string|
157
+ object.const_get(string)
158
+ end
159
+ else
160
+ class_or_to_s
161
+ end
162
+ end
163
+
164
+ def factory_name_for(class_or_to_s)
165
+ if class_or_to_s.respond_to?(:to_sym)
166
+ class_or_to_s.to_sym
167
+ else
168
+ class_name_to_variable_name(class_or_to_s).to_sym
169
+ end
170
+ end
171
+
172
+ def attribute_defined? (name)
173
+ !@attributes.detect {|attr| attr.name == name && !attr.is_a?(Attribute::Callback) }.nil?
174
+ end
175
+
176
+ def assert_valid_options(options)
177
+ invalid_keys = options.keys - [:class, :parent, :default_strategy, :aliases]
178
+ unless invalid_keys == []
179
+ raise ArgumentError, "Unknown arguments: #{invalid_keys.inspect}"
180
+ end
181
+ assert_valid_strategy(options[:default_strategy]) if options[:default_strategy]
182
+ end
183
+
184
+ def assert_valid_strategy(strategy)
185
+ unless Proxy.const_defined? variable_name_to_class_name(strategy)
186
+ raise ArgumentError, "Unknown strategy: #{strategy}"
187
+ end
188
+ end
189
+
190
+ # Based on ActiveSupport's underscore inflector
191
+ def class_name_to_variable_name(name)
192
+ name.to_s.gsub(/::/, '/').
193
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
194
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
195
+ tr("-", "_").
196
+ downcase
197
+ end
198
+
199
+ # Based on ActiveSupport's camelize inflector
200
+ def variable_name_to_class_name(name)
201
+ name.to_s.
202
+ gsub(/\/(.?)/) { "::#{$1.upcase}" }.
203
+ gsub(/(?:^|_)(.)/) { $1.upcase }
204
+ end
205
+
206
+ # From ActiveSupport
207
+ def symbolize_keys(hash)
208
+ hash.inject({}) do |options, (key, value)|
209
+ options[(key.to_sym rescue key) || key] = value
210
+ options
211
+ end
212
+ end
213
+
214
+ end
215
+ end
@@ -0,0 +1,77 @@
1
+ module FactoryGirl
2
+ class Proxy #:nodoc:
3
+
4
+ attr_reader :callbacks
5
+
6
+ def initialize(klass)
7
+ end
8
+
9
+ def get(attribute)
10
+ nil
11
+ end
12
+
13
+ def set(attribute, value)
14
+ end
15
+
16
+ def associate(name, factory, attributes)
17
+ end
18
+
19
+ def add_callback(name, block)
20
+ @callbacks ||= {}
21
+ @callbacks[name] ||= []
22
+ @callbacks[name] << block
23
+ end
24
+
25
+ def run_callbacks(name)
26
+ if @callbacks && @callbacks[name]
27
+ @callbacks[name].each do |block|
28
+ block.arity.zero? ? block.call : block.call(@instance)
29
+ end
30
+ end
31
+ end
32
+
33
+ # Generates an association using the current build strategy.
34
+ #
35
+ # Arguments:
36
+ # name: (Symbol)
37
+ # The name of the factory that should be used to generate this
38
+ # association.
39
+ # attributes: (Hash)
40
+ # A hash of attributes that should be overridden for this association.
41
+ #
42
+ # Returns:
43
+ # The generated association for the current build strategy. Note that
44
+ # associations are not generated for the attributes_for strategy. Returns
45
+ # nil in this case.
46
+ #
47
+ # Example:
48
+ #
49
+ # factory :user do
50
+ # # ...
51
+ # end
52
+ #
53
+ # factory :post do
54
+ # # ...
55
+ # author { |post| post.association(:user, :name => 'Joe') }
56
+ # end
57
+ #
58
+ # # Builds (but doesn't save) a Post and a User
59
+ # Factory.build(:post)
60
+ #
61
+ # # Builds and saves a User, builds a Post, assigns the User to the
62
+ # # author association, and saves the User.
63
+ # Factory.create(:post)
64
+ #
65
+ def association(name, overrides = {})
66
+ nil
67
+ end
68
+
69
+ def method_missing(method, *args, &block)
70
+ get(method)
71
+ end
72
+
73
+ def result
74
+ raise NotImplementedError, "Strategies must return a result"
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,21 @@
1
+ module FactoryGirl
2
+ class Proxy #:nodoc:
3
+ class AttributesFor < Proxy #:nodoc:
4
+ def initialize(klass)
5
+ @hash = {}
6
+ end
7
+
8
+ def get(attribute)
9
+ @hash[attribute]
10
+ end
11
+
12
+ def set(attribute, value)
13
+ @hash[attribute] = value
14
+ end
15
+
16
+ def result
17
+ @hash
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,32 @@
1
+ module FactoryGirl
2
+ class Proxy #:nodoc:
3
+ class Build < Proxy #:nodoc:
4
+ def initialize(klass)
5
+ @instance = klass.new
6
+ end
7
+
8
+ def get(attribute)
9
+ @instance.send(attribute)
10
+ end
11
+
12
+ def set(attribute, value)
13
+ @instance.send(:"#{attribute}=", value)
14
+ end
15
+
16
+ def associate(name, factory_name, overrides)
17
+ factory = FactoryGirl.factory_by_name(factory_name)
18
+ set(name, factory.run(Proxy::Create, overrides))
19
+ end
20
+
21
+ def association(factory_name, overrides = {})
22
+ factory = FactoryGirl.factory_by_name(factory_name)
23
+ factory.run(Proxy::Create, overrides)
24
+ end
25
+
26
+ def result
27
+ run_callbacks(:after_build)
28
+ @instance
29
+ end
30
+ end
31
+ end
32
+ end