jrun-factory_girl 1.1.3.9999
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog +19 -0
- data/LICENSE +19 -0
- data/README.textile +147 -0
- data/Rakefile +68 -0
- data/lib/factory_girl.rb +15 -0
- data/lib/factory_girl/aliases.rb +37 -0
- data/lib/factory_girl/attribute.rb +38 -0
- data/lib/factory_girl/attribute_proxy.rb +92 -0
- data/lib/factory_girl/factory.rb +239 -0
- data/lib/factory_girl/sequence.rb +56 -0
- data/test/aliases_test.rb +29 -0
- data/test/attribute_proxy_test.rb +101 -0
- data/test/attribute_test.rb +70 -0
- data/test/factory_test.rb +434 -0
- data/test/integration_test.rb +147 -0
- data/test/models.rb +32 -0
- data/test/sequence_test.rb +76 -0
- data/test/test_helper.rb +10 -0
- metadata +78 -0
data/Changelog
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
1.1.3 (September 12, 2008)
|
2
|
+
Automatically pull in definitions from factories.rb, test/factories.rb, or
|
3
|
+
spec/factories.rb
|
4
|
+
1.1.2 (July 30, 2008)
|
5
|
+
Improved error handling for invalid and undefined factories/attributes
|
6
|
+
Improved handling of strings vs symbols vs classes
|
7
|
+
Added a prettier syntax for handling associations
|
8
|
+
Updated documentation and fixed compatibility with Rails 2.1
|
9
|
+
|
10
|
+
1.1.1 (June 23, 2008)
|
11
|
+
The attribute "name" no longer requires using #add_attribute
|
12
|
+
|
13
|
+
1.1.0 (June 3, 2008)
|
14
|
+
Added support for dependent attributes
|
15
|
+
Fixed the attributes_for build strategy to not build associations
|
16
|
+
Added support for sequences
|
17
|
+
|
18
|
+
1.0.0 (May 31, 208)
|
19
|
+
First version
|
data/LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2008 Joe Ferris and thoughtbot, inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
19
|
+
THE SOFTWARE.
|
data/README.textile
ADDED
@@ -0,0 +1,147 @@
|
|
1
|
+
h1. factory_girl
|
2
|
+
|
3
|
+
Written by "Joe Ferris":mailto:jferris@thoughtbot.com.
|
4
|
+
|
5
|
+
Thanks to Tammer Saleh, Dan Croak, and Jon Yurek of thoughtbot, inc.
|
6
|
+
|
7
|
+
Copyright 2008 Joe Ferris and thoughtbot, inc.
|
8
|
+
|
9
|
+
h2. Download
|
10
|
+
|
11
|
+
Github: "Page":http://github.com/thoughtbot/factory_girl/tree/master "Clone":git://github.com/thoughtbot/factory_girl.git
|
12
|
+
|
13
|
+
Gem: <pre>gem install thoughtbot-factory_girl --source http://gems.github.com</pre>
|
14
|
+
|
15
|
+
Note: if you install factory_girl using the gem from Github, you'll need this
|
16
|
+
in your environment.rb if you want to use Rails 2.1's dependency manager:
|
17
|
+
|
18
|
+
config.gem "thoughtbot-factory_girl",
|
19
|
+
:lib => "factory_girl",
|
20
|
+
:source => "http://gems.github.com"
|
21
|
+
|
22
|
+
h2. Defining factories
|
23
|
+
|
24
|
+
<pre><code># This will guess the User class
|
25
|
+
Factory.define :user do |u|
|
26
|
+
u.first_name 'John'
|
27
|
+
u.last_name 'Doe'
|
28
|
+
u.admin false
|
29
|
+
end
|
30
|
+
|
31
|
+
# This will use the User class (Admin would have been guessed)
|
32
|
+
Factory.define :admin, :class => User do |u|
|
33
|
+
u.first_name 'Admin'
|
34
|
+
u.last_name 'User'
|
35
|
+
u.admin true
|
36
|
+
end</code></pre>
|
37
|
+
|
38
|
+
|
39
|
+
It is recommended that you create a test/factories.rb file and define your
|
40
|
+
factories there. This file can be included from test_helper or directly from
|
41
|
+
your test files. Don't forget:
|
42
|
+
<pre><code>require 'factory_girl'</code></pre>
|
43
|
+
|
44
|
+
|
45
|
+
h2. Lazy Attributes
|
46
|
+
|
47
|
+
Most attributes can be added using static values that are evaluated when the
|
48
|
+
factory is defined, but some attributes (such as associations and other
|
49
|
+
attributes that must be dynamically generated) will need values assigned each
|
50
|
+
time an instance is generated. These "lazy" attributes can be added by passing
|
51
|
+
a block instead of a parameter:
|
52
|
+
|
53
|
+
<pre><code>Factory.define :user do |u|
|
54
|
+
# ...
|
55
|
+
u.activation_code { User.generate_activation_code }
|
56
|
+
end</code></pre>
|
57
|
+
|
58
|
+
|
59
|
+
h2. Dependent Attributes
|
60
|
+
|
61
|
+
Some attributes may need to be generated based on the values of other
|
62
|
+
attributes. This can be done by calling the attribute name on
|
63
|
+
Factory::AttributeProxy, which is yielded to lazy attribute blocks:
|
64
|
+
|
65
|
+
<pre><code>Factory.define :user do |u|
|
66
|
+
u.first_name 'Joe'
|
67
|
+
u.last_name 'Blow'
|
68
|
+
u.email {|a| "#{a.first_name}.#{a.last_name}@example.com".downcase }
|
69
|
+
end
|
70
|
+
|
71
|
+
Factory(:user, :last_name => 'Doe').email
|
72
|
+
# => "joe.doe@example.com"</code></pre>
|
73
|
+
|
74
|
+
|
75
|
+
h2. Associations
|
76
|
+
|
77
|
+
Associated instances can be generated by using the association method when
|
78
|
+
defining a lazy attribute:
|
79
|
+
|
80
|
+
<pre><code>Factory.define :post do |p|
|
81
|
+
# ...
|
82
|
+
p.author {|author| author.association(:user, :last_name => 'Writely') }
|
83
|
+
end</code></pre>
|
84
|
+
|
85
|
+
|
86
|
+
When using the association method, the same build strategy (build, create, or attributes_for) will be used for all generated instances:
|
87
|
+
|
88
|
+
<pre><code># Builds and saves a User and a Post
|
89
|
+
post = Factory(:post)
|
90
|
+
post.new_record? # => false
|
91
|
+
post.author.new_record # => false
|
92
|
+
|
93
|
+
# Builds but does not save a User and a Post
|
94
|
+
Factory.build(:post)
|
95
|
+
post.new_record? # => true
|
96
|
+
post.author.new_record # => true</code></pre>
|
97
|
+
|
98
|
+
Because this pattern is so common, a prettier syntax is available for defining
|
99
|
+
associations:
|
100
|
+
|
101
|
+
<pre><code># The following definitions are equivilent:
|
102
|
+
Factory.define :post do |p|
|
103
|
+
p.author {|a| a.association(:user) }
|
104
|
+
end
|
105
|
+
|
106
|
+
Factory.define :post do |p|
|
107
|
+
p.association :author, :factory => :user
|
108
|
+
end</code></pre>
|
109
|
+
|
110
|
+
If the factory name is the same as the association name, the factory name can
|
111
|
+
be left out.
|
112
|
+
|
113
|
+
|
114
|
+
h2. Sequences
|
115
|
+
|
116
|
+
Unique values in a specific format (for example, e-mail addresses) can be
|
117
|
+
generated using sequences. Sequences are defined by calling Factory.sequence,
|
118
|
+
and values in a sequence are generated by calling Factory.next:
|
119
|
+
|
120
|
+
<pre><code># Defines a new sequence
|
121
|
+
Factory.sequence :email do |n|
|
122
|
+
"person#{n}@example.com"
|
123
|
+
end
|
124
|
+
|
125
|
+
Factory.next :email
|
126
|
+
# => "person1@example.com"
|
127
|
+
|
128
|
+
Factory.next :email
|
129
|
+
# => "person2@example.com"</code></pre>
|
130
|
+
|
131
|
+
|
132
|
+
h2. Using factories
|
133
|
+
|
134
|
+
<pre><code># Build and save a User instance
|
135
|
+
Factory(:user)
|
136
|
+
|
137
|
+
# Build a User instance and override the first_name property
|
138
|
+
Factory.build(:user, :first_name => 'Joe')
|
139
|
+
|
140
|
+
# Return an attributes Hash that can be used to build a User instance
|
141
|
+
attrs = Factory.attributes_for(:user)</code></pre>
|
142
|
+
|
143
|
+
h2. More Information
|
144
|
+
|
145
|
+
"Our blog":http://giantrobots.thoughtbot.com
|
146
|
+
|
147
|
+
"factory_girl rdoc":http://dev.thoughtbot.com/factory_girl
|
data/Rakefile
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/testtask'
|
4
|
+
require 'rake/rdoctask'
|
5
|
+
require 'rake/gempackagetask'
|
6
|
+
require 'date'
|
7
|
+
|
8
|
+
desc 'Default: run unit tests.'
|
9
|
+
task :default => :test
|
10
|
+
|
11
|
+
desc 'Test the factory_girl plugin.'
|
12
|
+
Rake::TestTask.new(:test) do |t|
|
13
|
+
t.libs << 'lib'
|
14
|
+
t.pattern = 'test/**/*_test.rb'
|
15
|
+
t.verbose = true
|
16
|
+
end
|
17
|
+
|
18
|
+
desc 'Generate documentation for the factory_girl plugin.'
|
19
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
20
|
+
rdoc.rdoc_dir = 'rdoc'
|
21
|
+
rdoc.title = 'Factory Girl'
|
22
|
+
rdoc.options << '--line-numbers' << '--inline-source' << "--main" << "README.textile"
|
23
|
+
rdoc.rdoc_files.include('README.textile')
|
24
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
25
|
+
end
|
26
|
+
|
27
|
+
desc 'Update documentation on website'
|
28
|
+
task :sync_docs => 'rdoc' do
|
29
|
+
`rsync -ave ssh rdoc/ dev@dev.thoughtbot.com:/home/dev/www/dev.thoughtbot.com/factory_girl`
|
30
|
+
end
|
31
|
+
|
32
|
+
spec = Gem::Specification.new do |s|
|
33
|
+
s.name = %q{factory_girl}
|
34
|
+
s.version = "1.1.4.9999"
|
35
|
+
s.summary = %q{factory_girl provides a framework and DSL for defining and
|
36
|
+
using model instance factories.}
|
37
|
+
s.description = %q{factory_girl provides a framework and DSL for defining and
|
38
|
+
using factories - less error-prone, more explicit, and
|
39
|
+
all-around easier to work with than fixtures.}
|
40
|
+
|
41
|
+
s.files = FileList['[A-Z]*', 'lib/**/*.rb', 'test/**/*.rb']
|
42
|
+
s.require_path = 'lib'
|
43
|
+
s.test_files = Dir[*['test/**/*_test.rb']]
|
44
|
+
|
45
|
+
s.has_rdoc = true
|
46
|
+
s.extra_rdoc_files = ["README.textile"]
|
47
|
+
s.rdoc_options = ['--line-numbers', '--inline-source', "--main", "README.textile"]
|
48
|
+
|
49
|
+
s.authors = ["Joe Ferris"]
|
50
|
+
s.email = %q{jferris@thoughtbot.com}
|
51
|
+
|
52
|
+
s.platform = Gem::Platform::RUBY
|
53
|
+
end
|
54
|
+
|
55
|
+
Rake::GemPackageTask.new spec do |pkg|
|
56
|
+
pkg.need_tar = true
|
57
|
+
pkg.need_zip = true
|
58
|
+
end
|
59
|
+
|
60
|
+
desc "Clean files generated by rake tasks"
|
61
|
+
task :clobber => [:clobber_rdoc, :clobber_package]
|
62
|
+
|
63
|
+
desc "Generate a gemspec file"
|
64
|
+
task :gemspec do
|
65
|
+
File.open("#{spec.name}.gemspec", 'w') do |f|
|
66
|
+
f.write spec.to_ruby
|
67
|
+
end
|
68
|
+
end
|
data/lib/factory_girl.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'factory_girl/factory'
|
2
|
+
require 'factory_girl/attribute_proxy'
|
3
|
+
require 'factory_girl/attribute'
|
4
|
+
require 'factory_girl/sequence'
|
5
|
+
require 'factory_girl/aliases'
|
6
|
+
|
7
|
+
# Shortcut for Factory.create.
|
8
|
+
#
|
9
|
+
# Example:
|
10
|
+
# Factory(:user, :name => 'Joe')
|
11
|
+
def Factory (name, attrs = {})
|
12
|
+
Factory.create(name, attrs)
|
13
|
+
end
|
14
|
+
|
15
|
+
Factory.find_definitions
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Factory
|
2
|
+
|
3
|
+
cattr_accessor :aliases #:nodoc:
|
4
|
+
self.aliases = [
|
5
|
+
[/(.*)_id/, '\1'],
|
6
|
+
[/(.*)/, '\1_id']
|
7
|
+
]
|
8
|
+
|
9
|
+
# Defines a new alias for attributes
|
10
|
+
#
|
11
|
+
# Arguments:
|
12
|
+
# pattern: (Regexp)
|
13
|
+
# A pattern that will be matched against attributes when looking for
|
14
|
+
# aliases. Contents captured in the pattern can be used in the alias.
|
15
|
+
# replace: (String)
|
16
|
+
# The alias that results from the matched pattern. Captured strings can
|
17
|
+
# be insert like String#sub.
|
18
|
+
#
|
19
|
+
# Example:
|
20
|
+
#
|
21
|
+
# Factory.alias /(.*)_confirmation/, '\1'
|
22
|
+
def self.alias (pattern, replace)
|
23
|
+
self.aliases << [pattern, replace]
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.aliases_for (attribute) #:nodoc:
|
27
|
+
aliases.collect do |params|
|
28
|
+
pattern, replace = *params
|
29
|
+
if pattern.match(attribute.to_s)
|
30
|
+
attribute.to_s.sub(pattern, replace).to_sym
|
31
|
+
else
|
32
|
+
nil
|
33
|
+
end
|
34
|
+
end.compact << attribute
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
class Factory
|
2
|
+
|
3
|
+
class AttributeDefinitionError < RuntimeError
|
4
|
+
end
|
5
|
+
|
6
|
+
class Attribute #:nodoc:
|
7
|
+
|
8
|
+
attr_reader :name
|
9
|
+
|
10
|
+
def initialize (name, static_value, lazy_block)
|
11
|
+
name = name.to_sym
|
12
|
+
|
13
|
+
if name.to_s =~ /=$/
|
14
|
+
raise AttributeDefinitionError,
|
15
|
+
"factory_girl uses 'f.#{name.to_s.chop} value' syntax " +
|
16
|
+
"rather than 'f.#{name} = value'"
|
17
|
+
end
|
18
|
+
|
19
|
+
unless static_value.nil? || lazy_block.nil?
|
20
|
+
raise AttributeDefinitionError, "Both value and block given"
|
21
|
+
end
|
22
|
+
|
23
|
+
@name = name
|
24
|
+
@static_value = static_value
|
25
|
+
@lazy_block = lazy_block
|
26
|
+
end
|
27
|
+
|
28
|
+
def value (proxy)
|
29
|
+
if @lazy_block.nil?
|
30
|
+
@static_value
|
31
|
+
else
|
32
|
+
@lazy_block.call(proxy)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
class Factory
|
2
|
+
|
3
|
+
class AttributeProxy
|
4
|
+
|
5
|
+
attr_accessor :factory, :attribute_name, :strategy, :current_values #:nodoc:
|
6
|
+
|
7
|
+
def initialize (factory, attr, strategy, values) #:nodoc:
|
8
|
+
@factory = factory
|
9
|
+
@attribute_name = attr
|
10
|
+
@strategy = strategy
|
11
|
+
@current_values = values
|
12
|
+
end
|
13
|
+
|
14
|
+
# Generates an association using the current build strategy.
|
15
|
+
#
|
16
|
+
# Arguments:
|
17
|
+
# name: (Symbol)
|
18
|
+
# The name of the factory that should be used to generate this
|
19
|
+
# association.
|
20
|
+
# attributes: (Hash)
|
21
|
+
# A hash of attributes that should be overridden for this association.
|
22
|
+
#
|
23
|
+
# Returns:
|
24
|
+
# The generated association for the current build strategy. Note that
|
25
|
+
# assocaitions are not generated for the attributes_for strategy. Returns
|
26
|
+
# nil in this case.
|
27
|
+
#
|
28
|
+
# Example:
|
29
|
+
#
|
30
|
+
# Factory.define :user do |f|
|
31
|
+
# # ...
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# Factory.define :post do |f|
|
35
|
+
# # ...
|
36
|
+
# f.author {|a| a.association :user, :name => 'Joe' }
|
37
|
+
# end
|
38
|
+
#
|
39
|
+
# # Builds (but doesn't save) a Post and a User
|
40
|
+
# Factory.build(:post)
|
41
|
+
#
|
42
|
+
# # Builds and saves a User, builds a Post, assigns the User to the
|
43
|
+
# # author association, and saves the User.
|
44
|
+
# Factory.create(:post)
|
45
|
+
#
|
46
|
+
def association (name, attributes = {})
|
47
|
+
if strategy == :attributes_for
|
48
|
+
nil
|
49
|
+
else
|
50
|
+
Factory.send(strategy, name, attributes)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the value for specified attribute. A value will only be available
|
55
|
+
# if it was overridden when calling the factory, or if a value is added
|
56
|
+
# earlier in the definition of the factory.
|
57
|
+
#
|
58
|
+
# Arguments:
|
59
|
+
# attribute: (Symbol)
|
60
|
+
# The attribute whose value should be returned.
|
61
|
+
#
|
62
|
+
# Returns:
|
63
|
+
# The value of the requested attribute. (Object)
|
64
|
+
def value_for (attribute)
|
65
|
+
unless current_values.key?(attribute)
|
66
|
+
raise ArgumentError, "No such attribute: #{attribute.inspect}"
|
67
|
+
end
|
68
|
+
current_values[attribute]
|
69
|
+
end
|
70
|
+
|
71
|
+
# Undefined methods are delegated to value_for, which means that:
|
72
|
+
#
|
73
|
+
# Factory.define :user do |f|
|
74
|
+
# f.first_name 'Ben'
|
75
|
+
# f.last_name {|a| a.value_for(:first_name) }
|
76
|
+
# end
|
77
|
+
#
|
78
|
+
# and:
|
79
|
+
#
|
80
|
+
# Factory.define :user do |f|
|
81
|
+
# f.first_name 'Ben'
|
82
|
+
# f.last_name {|a| a.first_name }
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# are equivilent.
|
86
|
+
def method_missing (name, *args, &block)
|
87
|
+
current_values[name]
|
88
|
+
end
|
89
|
+
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,239 @@
|
|
1
|
+
class Factory
|
2
|
+
|
3
|
+
cattr_accessor :factories #:nodoc:
|
4
|
+
self.factories = {}
|
5
|
+
|
6
|
+
# An Array of strings specifying locations that should be searched for
|
7
|
+
# factory definitions. By default, factory_girl will attempt to require
|
8
|
+
# "factories," "test/factories," and "spec/factories." Only the first
|
9
|
+
# existing file will be loaded.
|
10
|
+
cattr_accessor :definition_file_paths
|
11
|
+
self.definition_file_paths = %w(factories test/factories spec/factories)
|
12
|
+
|
13
|
+
attr_reader :factory_name
|
14
|
+
|
15
|
+
# Defines a new factory that can be used by the build strategies (create and
|
16
|
+
# build) to build new objects.
|
17
|
+
#
|
18
|
+
# Arguments:
|
19
|
+
# name: (Symbol)
|
20
|
+
# A unique name used to identify this factory.
|
21
|
+
# options: (Hash)
|
22
|
+
# class: the class that will be used when generating instances for this
|
23
|
+
# factory. If not specified, the class will be guessed from the
|
24
|
+
# factory name.
|
25
|
+
#
|
26
|
+
# Yields:
|
27
|
+
# The newly created factory (Factory)
|
28
|
+
def self.define (name, options = {})
|
29
|
+
instance = Factory.new(name, options)
|
30
|
+
yield(instance)
|
31
|
+
self.factories[instance.factory_name] = instance
|
32
|
+
end
|
33
|
+
|
34
|
+
def build_class #:nodoc:
|
35
|
+
@build_class ||= class_for(@options[:class] || factory_name)
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize (name, options = {}) #:nodoc:
|
39
|
+
options.assert_valid_keys(:class)
|
40
|
+
@factory_name = factory_name_for(name)
|
41
|
+
@options = options
|
42
|
+
@attributes = []
|
43
|
+
end
|
44
|
+
|
45
|
+
# Adds an attribute that should be assigned on generated instances for this
|
46
|
+
# factory.
|
47
|
+
#
|
48
|
+
# This method should be called with either a value or block, but not both. If
|
49
|
+
# called with a block, the attribute will be generated "lazily," whenever an
|
50
|
+
# instance is generated. Lazy attribute blocks will not be called if that
|
51
|
+
# attribute is overriden for a specific instance.
|
52
|
+
#
|
53
|
+
# When defining lazy attributes, an instance of Factory::AttributeProxy will
|
54
|
+
# be yielded, allowing associations to be built using the correct build
|
55
|
+
# strategy.
|
56
|
+
#
|
57
|
+
# Arguments:
|
58
|
+
# name: (Symbol)
|
59
|
+
# The name of this attribute. This will be assigned using :"#{name}=" for
|
60
|
+
# generated instances.
|
61
|
+
# value: (Object)
|
62
|
+
# If no block is given, this value will be used for this attribute.
|
63
|
+
def add_attribute (name, value = nil, &block)
|
64
|
+
attribute = Attribute.new(name, value, block)
|
65
|
+
|
66
|
+
if attribute_defined?(attribute.name)
|
67
|
+
raise AttributeDefinitionError, "Attribute already defined: #{name}"
|
68
|
+
end
|
69
|
+
|
70
|
+
@attributes << attribute
|
71
|
+
end
|
72
|
+
|
73
|
+
# Calls add_attribute using the missing method name as the name of the
|
74
|
+
# attribute, so that:
|
75
|
+
#
|
76
|
+
# Factory.define :user do |f|
|
77
|
+
# f.name 'Billy Idol'
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# and:
|
81
|
+
#
|
82
|
+
# Factory.define :user do |f|
|
83
|
+
# f.add_attribute :name, 'Billy Idol'
|
84
|
+
# end
|
85
|
+
#
|
86
|
+
# are equivilent.
|
87
|
+
def method_missing (name, *args, &block)
|
88
|
+
add_attribute(name, *args, &block)
|
89
|
+
end
|
90
|
+
|
91
|
+
# Adds an attribute that builds an association. The associated instance will
|
92
|
+
# be built using the same build strategy as the parent instance.
|
93
|
+
#
|
94
|
+
# Example:
|
95
|
+
# Factory.define :user do |f|
|
96
|
+
# f.name 'Joey'
|
97
|
+
# end
|
98
|
+
#
|
99
|
+
# Factory.define :post do |f|
|
100
|
+
# f.association :author, :factory => :user
|
101
|
+
# end
|
102
|
+
#
|
103
|
+
# Arguments:
|
104
|
+
# name: (Symbol)
|
105
|
+
# The name of this attribute.
|
106
|
+
# options: (Hash)
|
107
|
+
# factory: (Symbol)
|
108
|
+
# The name of the factory to use when building the associated instance.
|
109
|
+
# If no name is given, the name of the attribute is assumed to be the
|
110
|
+
# name of the factory. For example, a "user" association will by
|
111
|
+
# default use the "user" factory.
|
112
|
+
def association (name, options = {})
|
113
|
+
name = name.to_sym
|
114
|
+
options = options.symbolize_keys
|
115
|
+
association_factory = options[:factory] || name
|
116
|
+
|
117
|
+
add_attribute(name) {|a| a.association(association_factory) }
|
118
|
+
end
|
119
|
+
|
120
|
+
def attributes_for (attrs = {}) #:nodoc:
|
121
|
+
build_attributes_hash(attrs, :attributes_for)
|
122
|
+
end
|
123
|
+
|
124
|
+
def build (attrs = {}) #:nodoc:
|
125
|
+
build_instance(attrs, :build)
|
126
|
+
end
|
127
|
+
|
128
|
+
def create (attrs = {}) #:nodoc:
|
129
|
+
instance = build_instance(attrs, :create)
|
130
|
+
instance.save!
|
131
|
+
instance
|
132
|
+
end
|
133
|
+
|
134
|
+
class << self
|
135
|
+
|
136
|
+
# Generates and returns a Hash of attributes from this factory. Attributes
|
137
|
+
# can be individually overridden by passing in a Hash of attribute => value
|
138
|
+
# pairs.
|
139
|
+
#
|
140
|
+
# Arguments:
|
141
|
+
# attrs: (Hash)
|
142
|
+
# Attributes to overwrite for this set.
|
143
|
+
#
|
144
|
+
# Returns:
|
145
|
+
# A set of attributes that can be used to build an instance of the class
|
146
|
+
# this factory generates. (Hash)
|
147
|
+
def attributes_for (name, attrs = {})
|
148
|
+
factory_by_name(name).attributes_for(attrs)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Generates and returns an instance from this factory. Attributes can be
|
152
|
+
# individually overridden by passing in a Hash of attribute => value pairs.
|
153
|
+
#
|
154
|
+
# Arguments:
|
155
|
+
# attrs: (Hash)
|
156
|
+
# See attributes_for
|
157
|
+
#
|
158
|
+
# Returns:
|
159
|
+
# An instance of the class this factory generates, with generated
|
160
|
+
# attributes assigned.
|
161
|
+
def build (name, attrs = {})
|
162
|
+
factory_by_name(name).build(attrs)
|
163
|
+
end
|
164
|
+
|
165
|
+
# Generates, saves, and returns an instance from this factory. Attributes can
|
166
|
+
# be individually overridden by passing in a Hash of attribute => value
|
167
|
+
# pairs.
|
168
|
+
#
|
169
|
+
# If the instance is not valid, an ActiveRecord::Invalid exception will be
|
170
|
+
# raised.
|
171
|
+
#
|
172
|
+
# Arguments:
|
173
|
+
# attrs: (Hash)
|
174
|
+
# See attributes_for
|
175
|
+
#
|
176
|
+
# Returns:
|
177
|
+
# A saved instance of the class this factory generates, with generated
|
178
|
+
# attributes assigned.
|
179
|
+
def create (name, attrs = {})
|
180
|
+
factory_by_name(name).create(attrs)
|
181
|
+
end
|
182
|
+
|
183
|
+
def find_definitions #:nodoc:
|
184
|
+
definition_file_paths.each do |path|
|
185
|
+
begin
|
186
|
+
require(path)
|
187
|
+
break
|
188
|
+
rescue LoadError
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
private
|
194
|
+
|
195
|
+
def factory_by_name (name)
|
196
|
+
factories[name.to_sym] or raise ArgumentError.new("No such factory: #{name.to_s}")
|
197
|
+
end
|
198
|
+
|
199
|
+
end
|
200
|
+
|
201
|
+
private
|
202
|
+
|
203
|
+
def build_attributes_hash (values, strategy)
|
204
|
+
values = values.symbolize_keys
|
205
|
+
passed_keys = values.keys.collect {|key| Factory.aliases_for(key) }.flatten
|
206
|
+
@attributes.each do |attribute|
|
207
|
+
unless passed_keys.include?(attribute.name)
|
208
|
+
proxy = AttributeProxy.new(self, attribute.name, strategy, values)
|
209
|
+
values[attribute.name] = attribute.value(proxy)
|
210
|
+
end
|
211
|
+
end
|
212
|
+
values
|
213
|
+
end
|
214
|
+
|
215
|
+
def build_instance (override, strategy)
|
216
|
+
build_class.new build_attributes_hash(override, strategy)
|
217
|
+
end
|
218
|
+
|
219
|
+
def class_for (class_or_to_s)
|
220
|
+
if class_or_to_s.respond_to?(:to_sym)
|
221
|
+
class_or_to_s.to_s.classify.constantize
|
222
|
+
else
|
223
|
+
class_or_to_s
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def factory_name_for (class_or_to_s)
|
228
|
+
if class_or_to_s.respond_to?(:to_sym)
|
229
|
+
class_or_to_s.to_sym
|
230
|
+
else
|
231
|
+
class_or_to_s.to_s.underscore.to_sym
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def attribute_defined? (name)
|
236
|
+
!@attributes.detect {|attr| attr.name == name }.nil?
|
237
|
+
end
|
238
|
+
|
239
|
+
end
|