factory_girl 2.0.3 → 2.0.4

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.
@@ -259,6 +259,78 @@ Without a block, the value will increment itself, starting at its initial value:
259
259
  sequence(:position)
260
260
  end
261
261
 
262
+ Traits
263
+ ------
264
+
265
+ Traits allow you to group attributes together and then apply them
266
+ to any factory.
267
+
268
+ factory :user, :aliases => [:author]
269
+
270
+ factory :story do
271
+ title "My awesome story"
272
+ author
273
+
274
+ trait :published do
275
+ published true
276
+ end
277
+
278
+ trait :unpublished do
279
+ published false
280
+ end
281
+
282
+ trait :week_long_publishing do
283
+ start_at { 1.week.ago }
284
+ end_at { Time.now }
285
+ end
286
+
287
+ trait :month_long_publishing do
288
+ start_at { 1.month.ago }
289
+ end_at { Time.now }
290
+ end
291
+
292
+ factory :week_long_published_story, :traits => [:published, :week_long_publishing]
293
+ factory :month_long_published_story, :traits => [:published, :month_long_publishing]
294
+ factory :week_long_unpublished_story, :traits => [:unpublished, :week_long_publishing]
295
+ factory :month_long_unpublished_story, :traits => [:unpublished, :month_long_publishing]
296
+ end
297
+
298
+ Traits can be used as attributes:
299
+
300
+ factory :week_long_published_story_with_title, :parent => :story do
301
+ published
302
+ week_long_publishing
303
+ title { "Publishing that was started at {start_at}" }
304
+ end
305
+
306
+ Traits that define the same attributes won't raise AttributeDefinitionErrors;
307
+ the trait that defines the attribute latest gets precedence.
308
+
309
+ factory :user do
310
+ name "Friendly User"
311
+ login { name }
312
+
313
+ trait :male do
314
+ name "John Doe"
315
+ gender "Male"
316
+ login { "#{name} (M)" }
317
+ end
318
+
319
+ trait :female do
320
+ name "Jane Doe"
321
+ gender "Female"
322
+ login { "#{name} (F)" }
323
+ end
324
+
325
+ trait :admin do
326
+ admin true
327
+ login { "admin-#{name}" }
328
+ end
329
+
330
+ factory :male_admin, :traits => [:male, :admin] # login will be "admin-John Doe"
331
+ factory :female_admin, :traits => [:admin, :female] # login will be "Jane Doe (F)"
332
+ end
333
+
262
334
  Callbacks
263
335
  ---------
264
336
 
data/Gemfile CHANGED
@@ -4,6 +4,7 @@ gem "rake"
4
4
  gem "rspec", "~> 2.0"
5
5
  gem "rcov"
6
6
  gem "activerecord", :require => false
7
+ gem "activesupport", :require => false
7
8
  gem "rr"
8
9
  gem "sqlite3-ruby", :require => false
9
10
  gem "appraisal", "~> 0.3.5"
@@ -58,6 +58,7 @@ PLATFORMS
58
58
 
59
59
  DEPENDENCIES
60
60
  activerecord
61
+ activesupport
61
62
  appraisal (~> 0.3.5)
62
63
  bluecloth
63
64
  cucumber (~> 1.0.0)
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # factory_girl [![Build Status](http://travis-ci.org/thoughtbot/factory_girl.png)](http://travis-ci.org/thoughtbot/factory_girl)
1
+ # factory_girl [![Build Status](https://secure.travis-ci.org/thoughtbot/factory_girl.png)](http://travis-ci.org/thoughtbot/factory_girl)
2
2
 
3
3
  factory_girl is a fixtures replacement with a straightforward definition syntax, support for multiple build strategies (saved instances, unsaved instances, attribute hashes, and stubbed objects), and support for multiple factories for the same class (user, admin_user, and so on), including factory inheritance.
4
4
 
Binary file
@@ -12,7 +12,10 @@ require 'factory_girl/attribute/association'
12
12
  require 'factory_girl/attribute/callback'
13
13
  require 'factory_girl/attribute/sequence'
14
14
  require 'factory_girl/attribute/implicit'
15
+ require 'factory_girl/attribute/trait'
15
16
  require 'factory_girl/sequence'
17
+ require 'factory_girl/attribute_list'
18
+ require 'factory_girl/trait'
16
19
  require 'factory_girl/aliases'
17
20
  require 'factory_girl/definition_proxy'
18
21
  require 'factory_girl/syntax/methods'
@@ -50,4 +53,16 @@ module FactoryGirl
50
53
  def self.sequence_by_name(name)
51
54
  sequences.find(name)
52
55
  end
56
+
57
+ def self.traits
58
+ @traits ||= Registry.new
59
+ end
60
+
61
+ def self.register_trait(trait)
62
+ traits.add(trait)
63
+ end
64
+
65
+ def self.trait_by_name(name)
66
+ traits.find(name)
67
+ end
53
68
  end
@@ -34,6 +34,10 @@ module FactoryGirl
34
34
  1
35
35
  end
36
36
 
37
+ def aliases_for?(attr)
38
+ FactoryGirl.aliases_for(attr).include?(name)
39
+ end
40
+
37
41
  def <=>(another)
38
42
  return nil unless another.is_a? Attribute
39
43
  self.priority <=> another.priority
@@ -2,8 +2,9 @@ module FactoryGirl
2
2
  class Attribute
3
3
 
4
4
  class Implicit < Attribute
5
- def initialize(name)
5
+ def initialize(name, factory = nil)
6
6
  super(name)
7
+ @factory = factory
7
8
  end
8
9
 
9
10
  def add_to(proxy)
@@ -27,8 +28,10 @@ module FactoryGirl
27
28
  def resolve_name
28
29
  if FactoryGirl.factories.registered?(name)
29
30
  Attribute::Association.new(name, name, {})
30
- else
31
+ elsif FactoryGirl.sequences.registered?(name)
31
32
  Attribute::Sequence.new(name, name)
33
+ else
34
+ Attribute::Trait.new(name, @factory)
32
35
  end
33
36
  end
34
37
  end
@@ -0,0 +1,22 @@
1
+ module FactoryGirl
2
+ class Attribute #:nodoc:
3
+
4
+ class Trait < Attribute #:nodoc:
5
+ def initialize(name, factory)
6
+ super(name)
7
+ @factory = factory
8
+ end
9
+
10
+ def add_to(proxy)
11
+ trait.attributes.each { |attr| attr.add_to(proxy) }
12
+ end
13
+
14
+ private
15
+
16
+ def trait
17
+ (@factory || FactoryGirl).trait_by_name(name)
18
+ end
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,59 @@
1
+ module FactoryGirl
2
+ class AttributeList
3
+ include Enumerable
4
+
5
+ def initialize
6
+ @attributes = []
7
+ end
8
+
9
+ def define_attribute(attribute)
10
+ if attribute_defined?(attribute.name)
11
+ raise AttributeDefinitionError, "Attribute already defined: #{attribute.name}"
12
+ end
13
+
14
+ @attributes << attribute
15
+ end
16
+
17
+ def add_callback(name, &block)
18
+ unless valid_callback_names.include?(name.to_sym)
19
+ raise InvalidCallbackNameError, "#{name} is not a valid callback name. Valid callback names are #{valid_callback_names.inspect}"
20
+ end
21
+
22
+ @attributes << Attribute::Callback.new(name.to_sym, block)
23
+ end
24
+
25
+ def each(&block)
26
+ @attributes.each(&block)
27
+ end
28
+
29
+ def attribute_defined?(attribute_name)
30
+ !@attributes.detect do |attribute|
31
+ attribute.name == attribute_name &&
32
+ !attribute.is_a?(FactoryGirl::Attribute::Callback)
33
+ end.nil?
34
+ end
35
+
36
+ def apply_attributes(attributes_to_apply)
37
+ new_attributes = []
38
+
39
+ attributes_to_apply.each do |attribute|
40
+ if attribute_defined?(attribute.name)
41
+ @attributes.delete_if do |attrib|
42
+ new_attributes << attrib.clone if attrib.name == attribute.name
43
+ end
44
+ else
45
+ new_attributes << attribute.clone
46
+ end
47
+ end
48
+
49
+ @attributes.unshift *new_attributes
50
+ @attributes = @attributes.partition {|attr| attr.priority.zero? }.flatten
51
+ end
52
+
53
+ private
54
+
55
+ def valid_callback_names
56
+ [:after_build, :after_create, :after_stub]
57
+ end
58
+ end
59
+ end
@@ -78,7 +78,7 @@ module FactoryGirl
78
78
  # are equivalent.
79
79
  def method_missing(name, *args, &block)
80
80
  if args.empty? && block.nil?
81
- @factory.define_attribute(Attribute::Implicit.new(name))
81
+ @factory.define_attribute(Attribute::Implicit.new(name, @factory))
82
82
  elsif args.first.is_a?(Hash) && args.first.has_key?(:factory)
83
83
  association(name, *args)
84
84
  else
@@ -154,5 +154,9 @@ module FactoryGirl
154
154
  def factory(name, options = {}, &block)
155
155
  @child_factories << [name, options, block]
156
156
  end
157
+
158
+ def trait(name, &block)
159
+ @factory.define_trait(Trait.new(name, &block))
160
+ end
157
161
  end
158
162
  end
@@ -13,7 +13,7 @@ module FactoryGirl
13
13
 
14
14
  class Factory
15
15
  attr_reader :name #:nodoc:
16
- attr_reader :attributes #:nodoc:
16
+ attr_reader :traits #:nodoc:
17
17
 
18
18
  def factory_name
19
19
  puts "WARNING: factory.factory_name is deprecated. Use factory.name instead."
@@ -34,55 +34,62 @@ module FactoryGirl
34
34
 
35
35
  def initialize(name, options = {}) #:nodoc:
36
36
  assert_valid_options(options)
37
- @name = factory_name_for(name)
38
- @options = options
39
- @attributes = []
37
+ @name = factory_name_for(name)
38
+ @parent = options[:parent]
39
+ @options = options
40
+ @attribute_list = AttributeList.new
41
+ @traits = []
40
42
  end
41
43
 
42
44
  def inherit_from(parent) #:nodoc:
43
45
  @options[:class] ||= parent.class_name
44
46
  @options[:default_strategy] ||= parent.default_strategy
45
47
 
46
- new_attributes = []
47
- parent.attributes.each do |attribute|
48
- unless attribute_defined?(attribute.name)
49
- new_attributes << attribute.clone
50
- end
48
+ apply_attributes(parent.attributes)
49
+ end
50
+
51
+ def apply_traits(traits) #:nodoc:
52
+ traits.reverse.map { |name| trait_by_name(name) }.each do |trait|
53
+ apply_attributes(trait.attributes)
51
54
  end
55
+ end
52
56
 
53
- @attributes += new_attributes
54
- @attributes.sort!
57
+ def apply_attributes(attributes_to_apply)
58
+ @attribute_list.apply_attributes(attributes_to_apply)
55
59
  end
56
60
 
57
61
  def define_attribute(attribute)
58
- name = attribute.name
59
- # TODO: move these checks into Attribute
60
- if attribute_defined?(name)
61
- raise AttributeDefinitionError, "Attribute already defined: #{name}"
62
- end
63
62
  if attribute.respond_to?(:factory) && attribute.factory == self.name
64
- raise AssociationDefinitionError, "Self-referencing association '#{name}' in factory '#{self.name}'"
63
+ raise AssociationDefinitionError, "Self-referencing association '#{attribute.name}' in factory '#{self.name}'"
65
64
  end
66
- @attributes << attribute
65
+
66
+ @attribute_list.define_attribute(attribute)
67
+ end
68
+
69
+ def define_trait(trait)
70
+ @traits << trait
67
71
  end
68
72
 
69
73
  def add_callback(name, &block)
70
- unless [:after_build, :after_create, :after_stub].include?(name.to_sym)
71
- raise InvalidCallbackNameError, "#{name} is not a valid callback name. Valid callback names are :after_build, :after_create, and :after_stub"
72
- end
73
- @attributes << Attribute::Callback.new(name.to_sym, block)
74
+ @attribute_list.add_callback(name, &block)
75
+ end
76
+
77
+ def attributes
78
+ @attribute_list.to_a
74
79
  end
75
80
 
76
81
  def run(proxy_class, overrides) #:nodoc:
77
82
  proxy = proxy_class.new(build_class)
78
83
  overrides = symbolize_keys(overrides)
79
- overrides.each {|attr, val| proxy.set(attr, val) }
80
- passed_keys = overrides.keys.collect {|k| FactoryGirl.aliases_for(k) }.flatten
81
- @attributes.each do |attribute|
82
- unless passed_keys.include?(attribute.name)
84
+ @attribute_list.each do |attribute|
85
+ factory_overrides = overrides.select { |attr, val| attribute.aliases_for?(attr) }
86
+ if factory_overrides.empty?
83
87
  attribute.add_to(proxy)
88
+ else
89
+ factory_overrides.each { |attr, val| proxy.set(attr, val) }
84
90
  end
85
91
  end
92
+ overrides.each { |attr, val| proxy.set(attr, val) }
86
93
  proxy.result(@to_create_block)
87
94
  end
88
95
 
@@ -94,6 +101,16 @@ module FactoryGirl
94
101
  attributes.select {|attribute| attribute.association? }
95
102
  end
96
103
 
104
+ def trait_by_name(name)
105
+ if existing_attribute = trait_for(name)
106
+ existing_attribute
107
+ elsif @parent
108
+ FactoryGirl.factory_by_name(@parent).trait_by_name(name)
109
+ else
110
+ FactoryGirl.trait_by_name(name)
111
+ end
112
+ end
113
+
97
114
  # Names for this factory, including aliases.
98
115
  #
99
116
  # Example:
@@ -148,12 +165,8 @@ module FactoryGirl
148
165
  end
149
166
  end
150
167
 
151
- def attribute_defined? (name)
152
- !@attributes.detect {|attr| attr.name == name && !attr.is_a?(Attribute::Callback) }.nil?
153
- end
154
-
155
168
  def assert_valid_options(options)
156
- invalid_keys = options.keys - [:class, :parent, :default_strategy, :aliases]
169
+ invalid_keys = options.keys - [:class, :parent, :default_strategy, :aliases, :traits]
157
170
  unless invalid_keys == []
158
171
  raise ArgumentError, "Unknown arguments: #{invalid_keys.inspect}"
159
172
  end
@@ -194,5 +207,8 @@ module FactoryGirl
194
207
  end
195
208
  end
196
209
 
210
+ def trait_for(name)
211
+ traits.detect {|trait| trait.name == name }
212
+ end
197
213
  end
198
214
  end
@@ -16,6 +16,11 @@ module FactoryGirl
16
16
  factory = Factory.new(name, options)
17
17
  proxy = FactoryGirl::DefinitionProxy.new(factory)
18
18
  proxy.instance_eval(&block) if block_given?
19
+
20
+ if traits = options.delete(:traits)
21
+ factory.apply_traits(traits)
22
+ end
23
+
19
24
  if parent = options.delete(:parent)
20
25
  factory.inherit_from(FactoryGirl.factory_by_name(parent))
21
26
  end
@@ -29,6 +34,10 @@ module FactoryGirl
29
34
  def sequence(name, start_value = 1, &block)
30
35
  FactoryGirl.register_sequence(Sequence.new(name, start_value, &block))
31
36
  end
37
+
38
+ def trait(name, &block)
39
+ FactoryGirl.register_trait(Trait.new(name, &block))
40
+ end
32
41
  end
33
42
  end
34
43
  end
@@ -0,0 +1,29 @@
1
+ module FactoryGirl
2
+ class Trait
3
+ attr_reader :name
4
+
5
+ def initialize(name, &block) #:nodoc:
6
+ @name = name
7
+ @attribute_list = AttributeList.new
8
+
9
+ proxy = FactoryGirl::DefinitionProxy.new(self)
10
+ proxy.instance_eval(&block) if block_given?
11
+ end
12
+
13
+ def define_attribute(attribute)
14
+ @attribute_list.define_attribute(attribute)
15
+ end
16
+
17
+ def add_callback(name, &block)
18
+ @attribute_list.add_callback(name, &block)
19
+ end
20
+
21
+ def attributes
22
+ @attribute_list.to_a
23
+ end
24
+
25
+ def names
26
+ [@name]
27
+ end
28
+ end
29
+ end
@@ -1,4 +1,4 @@
1
1
  module FactoryGirl
2
- VERSION = "2.0.3"
2
+ VERSION = "2.0.4"
3
3
  end
4
4
 
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+ require 'acceptance/acceptance_helper'
3
+
4
+ describe "a generated attributes hash where order matters" do
5
+ include FactoryGirl::Syntax::Methods
6
+
7
+ before do
8
+ define_model('ParentModel', :static => :integer,
9
+ :evaluates_first => :integer,
10
+ :evaluates_second => :integer,
11
+ :evaluates_third => :integer)
12
+
13
+ FactoryGirl.define do
14
+ factory :parent_model do
15
+ evaluates_first { static }
16
+ evaluates_second { evaluates_first }
17
+ evaluates_third { evaluates_second }
18
+
19
+ factory :child_model do
20
+ static 1
21
+ end
22
+ end
23
+ end
24
+ end
25
+
26
+ subject { FactoryGirl.build(:child_model) }
27
+
28
+ it "assigns attributes in the order they're defined with preference to static attributes" do
29
+ subject[:evaluates_first].should == 1
30
+ subject[:evaluates_second].should == 1
31
+ subject[:evaluates_third].should == 1
32
+ end
33
+ end
@@ -0,0 +1,65 @@
1
+ require 'spec_helper'
2
+ require 'acceptance/acceptance_helper'
3
+ require 'active_support/ordered_hash'
4
+
5
+ describe "attribute overrides" do
6
+ before do
7
+ define_model('User', :admin => :boolean)
8
+ define_model('Post', :title => :string,
9
+ :secure => :boolean,
10
+ :user_id => :integer) do
11
+ belongs_to :user
12
+
13
+ def secure=(value)
14
+ return unless user && user.admin?
15
+ write_attribute(:secure, value)
16
+ end
17
+ end
18
+
19
+ FactoryGirl.define do
20
+ factory :user do
21
+ factory :admin do
22
+ admin true
23
+ end
24
+ end
25
+
26
+ factory :post do
27
+ user
28
+ title "default title"
29
+ end
30
+ end
31
+ end
32
+
33
+ let(:admin) { FactoryGirl.create(:admin) }
34
+
35
+ let(:post_attributes) do
36
+ attributes = ActiveSupport::OrderedHash.new
37
+ attributes[:secure] = false
38
+ attributes
39
+ end
40
+
41
+ let(:non_admin_post_attributes) do
42
+ post_attributes[:user] = FactoryGirl.create(:user)
43
+ post_attributes
44
+ end
45
+
46
+ let(:admin_post_attributes) do
47
+ post_attributes[:user] = admin
48
+ post_attributes
49
+ end
50
+
51
+ context "with an admin posting" do
52
+ subject { FactoryGirl.create(:post, admin_post_attributes) }
53
+ its(:secure) { should == false }
54
+ end
55
+
56
+ context "with a non-admin posting" do
57
+ subject { FactoryGirl.create(:post, non_admin_post_attributes) }
58
+ its(:secure) { should be_nil }
59
+ end
60
+
61
+ context "with no user posting" do
62
+ subject { FactoryGirl.create(:post, post_attributes) }
63
+ its(:secure) { should be_nil }
64
+ end
65
+ end
@@ -3,16 +3,18 @@ require 'acceptance/acceptance_helper'
3
3
 
4
4
  describe "an instance generated by a factory that inherits from another factory" do
5
5
  before do
6
- define_model("User", :name => :string, :admin => :boolean, :email => :string)
6
+ define_model("User", :name => :string, :admin => :boolean, :email => :string, :upper_email => :string, :login => :string)
7
7
 
8
8
  FactoryGirl.define do
9
9
  factory :user do
10
10
  name "John"
11
11
  email { "#{name.downcase}@example.com" }
12
+ login { email }
12
13
 
13
14
  factory :admin do
14
15
  name "admin"
15
16
  admin true
17
+ upper_email { email.upcase }
16
18
  end
17
19
 
18
20
  factory :guest do
@@ -29,11 +31,12 @@ describe "an instance generated by a factory that inherits from another factory"
29
31
  end
30
32
 
31
33
  describe "the child class redefining parent's static method used by a dynamic method" do
32
- subject { FactoryGirl.create(:admin) }
33
- it { should be_kind_of(User) }
34
- it { should be_admin }
35
- its(:name) { should == "admin" }
36
- its(:email) { should == "admin@example.com" }
34
+ subject { FactoryGirl.create(:admin) }
35
+ it { should be_kind_of(User) }
36
+ it { should be_admin }
37
+ its(:name) { should == "admin" }
38
+ its(:email) { should == "admin@example.com" }
39
+ its(:upper_email) { should == "ADMIN@EXAMPLE.COM"}
37
40
  end
38
41
 
39
42
  describe "the child class redefining parent's dynamic method" do
@@ -41,6 +44,7 @@ describe "an instance generated by a factory that inherits from another factory"
41
44
  it { should_not be_admin }
42
45
  its(:name) { should == "John" }
43
46
  its(:email) { should eql "John-guest@example.com" }
47
+ its(:login) { should == "John-guest@example.com" }
44
48
  end
45
49
  end
46
50
 
@@ -0,0 +1,171 @@
1
+ require "spec_helper"
2
+ require "acceptance/acceptance_helper"
3
+
4
+ describe "an instance generated by a factory with multiple traits" do
5
+ before do
6
+ define_model("User",
7
+ :name => :string,
8
+ :admin => :boolean,
9
+ :gender => :string,
10
+ :email => :string,
11
+ :date_of_birth => :date,
12
+ :great => :string)
13
+
14
+ FactoryGirl.define do
15
+ factory :user_without_admin_scoping, :class => User do
16
+ admin_trait
17
+ end
18
+
19
+ factory :user do
20
+ name "John"
21
+
22
+ trait :great do
23
+ great "GREAT!!!"
24
+ end
25
+
26
+ trait :admin do
27
+ admin true
28
+ end
29
+
30
+ trait :admin_trait do
31
+ admin true
32
+ end
33
+
34
+ trait :male do
35
+ name "Joe"
36
+ gender "Male"
37
+ end
38
+
39
+ trait :female do
40
+ name "Jane"
41
+ gender "Female"
42
+ end
43
+
44
+ factory :great_user do
45
+ great
46
+ end
47
+
48
+ factory :admin, :traits => [:admin]
49
+
50
+ factory :male_user do
51
+ male
52
+
53
+ factory :child_male_user do
54
+ date_of_birth { Date.parse("1/1/2000") }
55
+ end
56
+ end
57
+
58
+ factory :female, :traits => [:female] do
59
+ trait :admin do
60
+ admin true
61
+ name "Judy"
62
+ end
63
+
64
+ factory :female_admin_judy, :traits => [:admin]
65
+ end
66
+
67
+ factory :female_admin, :traits => [:female, :admin]
68
+ factory :female_after_male_admin, :traits => [:male, :female, :admin]
69
+ factory :male_after_female_admin, :traits => [:female, :male, :admin]
70
+ end
71
+
72
+ trait :email do
73
+ email { "#{name}@example.com" }
74
+ end
75
+
76
+ factory :user_with_email, :class => User, :traits => [:email] do
77
+ name "Bill"
78
+ end
79
+ end
80
+ end
81
+
82
+ context "the parent class" do
83
+ subject { FactoryGirl.create(:user) }
84
+ its(:name) { should == "John" }
85
+ its(:gender) { should be_nil }
86
+ it { should_not be_admin }
87
+ end
88
+
89
+ context "the child class with one trait" do
90
+ subject { FactoryGirl.create(:admin) }
91
+ its(:name) { should == "John" }
92
+ its(:gender) { should be_nil }
93
+ it { should be_admin }
94
+ end
95
+
96
+ context "the other child class with one trait" do
97
+ subject { FactoryGirl.create(:female) }
98
+ its(:name) { should == "Jane" }
99
+ its(:gender) { should == "Female" }
100
+ it { should_not be_admin }
101
+ end
102
+
103
+ context "the child with multiple traits" do
104
+ subject { FactoryGirl.create(:female_admin) }
105
+ its(:name) { should == "Jane" }
106
+ its(:gender) { should == "Female" }
107
+ it { should be_admin }
108
+ end
109
+
110
+ context "the child with multiple traits and overridden attributes" do
111
+ subject { FactoryGirl.create(:female_admin, :name => "Jill", :gender => nil) }
112
+ its(:name) { should == "Jill" }
113
+ its(:gender) { should be_nil }
114
+ it { should be_admin }
115
+ end
116
+
117
+ context "the child with multiple traits who override the same attribute" do
118
+ context "when the male assigns name after female" do
119
+ subject { FactoryGirl.create(:male_after_female_admin) }
120
+ its(:name) { should == "Joe" }
121
+ its(:gender) { should == "Male" }
122
+ it { should be_admin }
123
+ end
124
+
125
+ context "when the female assigns name after male" do
126
+ subject { FactoryGirl.create(:female_after_male_admin) }
127
+ its(:name) { should == "Jane" }
128
+ its(:gender) { should == "Female" }
129
+ it { should be_admin }
130
+ end
131
+ end
132
+
133
+ context "child class with scoped trait and inherited trait" do
134
+ subject { FactoryGirl.create(:female_admin_judy) }
135
+ its(:name) { should == "Judy" }
136
+ its(:gender) { should == "Female" }
137
+ it { should be_admin }
138
+ end
139
+
140
+ context "factory using global trait" do
141
+ subject { FactoryGirl.create(:user_with_email) }
142
+ its(:name) { should == "Bill" }
143
+ its(:email) { should == "Bill@example.com"}
144
+ end
145
+
146
+ context "factory created with alternate syntax for specifying trait" do
147
+ subject { FactoryGirl.create(:male_user) }
148
+ its(:gender) { should == "Male" }
149
+ end
150
+
151
+ context "factory created with alternate syntax where trait name and attribute are the same" do
152
+ subject { FactoryGirl.create(:great_user) }
153
+ its(:great) { should == "GREAT!!!" }
154
+ end
155
+
156
+ context "factory created with alternate syntax where trait name and attribute are the same and attribute is overridden" do
157
+ subject { FactoryGirl.create(:great_user, :great => "SORT OF!!!") }
158
+ its(:great) { should == "SORT OF!!!" }
159
+ end
160
+
161
+ context "child factory created where trait attributes are inherited" do
162
+ subject { FactoryGirl.create(:child_male_user) }
163
+ its(:gender) { should == "Male" }
164
+ its(:date_of_birth) { should == Date.parse("1/1/2000") }
165
+ end
166
+
167
+ context "factory outside of scope" do
168
+ subject { FactoryGirl.create(:user_without_admin_scoping) }
169
+ it { expect { subject }.to raise_error(ArgumentError, "Not registered: admin_trait") }
170
+ end
171
+ end
@@ -0,0 +1,111 @@
1
+ require "spec_helper"
2
+
3
+ describe FactoryGirl::AttributeList, "#define_attribute" do
4
+ let(:static_attribute) { FactoryGirl::Attribute::Static.new(:full_name, "value") }
5
+ let(:dynamic_attribute) { FactoryGirl::Attribute::Dynamic.new(:email, lambda {|u| "#{u.full_name}@example.com" }) }
6
+
7
+ it "maintains a list of attributes" do
8
+ subject.define_attribute(static_attribute)
9
+ subject.to_a.should == [static_attribute]
10
+
11
+ subject.define_attribute(dynamic_attribute)
12
+ subject.to_a.should == [static_attribute, dynamic_attribute]
13
+ end
14
+
15
+ it "raises if an attribute has already been defined" do
16
+ expect {
17
+ 2.times { subject.define_attribute(static_attribute) }
18
+ }.to raise_error(FactoryGirl::AttributeDefinitionError, "Attribute already defined: full_name")
19
+ end
20
+ end
21
+
22
+ describe FactoryGirl::AttributeList, "#attribute_defined?" do
23
+ let(:static_attribute) { FactoryGirl::Attribute::Static.new(:full_name, "value") }
24
+ let(:callback_attribute) { FactoryGirl::Attribute::Callback.new(:after_create, lambda { }) }
25
+ let(:static_attribute_named_after_create) { FactoryGirl::Attribute::Static.new(:after_create, "funky!") }
26
+
27
+ it "knows if an attribute has been defined" do
28
+ subject.attribute_defined?(static_attribute.name).should == false
29
+
30
+ subject.define_attribute(static_attribute)
31
+
32
+ subject.attribute_defined?(static_attribute.name).should == true
33
+ subject.attribute_defined?(:undefined_attribute).should == false
34
+ end
35
+
36
+ it "doesn't reference callbacks" do
37
+ subject.define_attribute(callback_attribute)
38
+
39
+ subject.attribute_defined?(:after_create).should == false
40
+
41
+ subject.define_attribute(static_attribute_named_after_create)
42
+ subject.attribute_defined?(:after_create).should == true
43
+ end
44
+ end
45
+
46
+ describe FactoryGirl::AttributeList, "#add_callback" do
47
+ let(:proxy_class) { mock("klass") }
48
+ let(:proxy) { FactoryGirl::Proxy.new(proxy_class) }
49
+ let(:valid_callback_names) { [:after_create, :after_build, :after_stub] }
50
+ let(:invalid_callback_names) { [:before_create, :before_build, :bogus] }
51
+
52
+
53
+ it "allows for defining adding a callback" do
54
+ subject.add_callback(:after_create) { "Called after_create" }
55
+
56
+ subject.first.name.should == :after_create
57
+
58
+ subject.first.add_to(proxy)
59
+ proxy.callbacks[:after_create].first.call.should == "Called after_create"
60
+ end
61
+
62
+ it "allows valid callback names to be assigned" do
63
+ valid_callback_names.each do |callback_name|
64
+ expect do
65
+ subject.add_callback(callback_name) { "great name!" }
66
+ end.to_not raise_error(FactoryGirl::InvalidCallbackNameError)
67
+ end
68
+ end
69
+
70
+ it "raises if an invalid callback name is assigned" do
71
+ invalid_callback_names.each do |callback_name|
72
+ expect do
73
+ subject.add_callback(callback_name) { "great name!" }
74
+ end.to raise_error(FactoryGirl::InvalidCallbackNameError, "#{callback_name} is not a valid callback name. Valid callback names are [:after_build, :after_create, :after_stub]")
75
+ end
76
+ end
77
+ end
78
+
79
+ describe FactoryGirl::AttributeList, "#apply_attributes" do
80
+ let(:full_name_attribute) { FactoryGirl::Attribute::Static.new(:full_name, "John Adams") }
81
+ let(:city_attribute) { FactoryGirl::Attribute::Static.new(:city, "Boston") }
82
+ let(:email_attribute) { FactoryGirl::Attribute::Dynamic.new(:email, lambda {|model| "#{model.full_name}@example.com" }) }
83
+ let(:login_attribute) { FactoryGirl::Attribute::Dynamic.new(:login, lambda {|model| "username-#{model.full_name}" }) }
84
+
85
+ it "prepends applied attributes" do
86
+ subject.define_attribute(full_name_attribute)
87
+ subject.apply_attributes([city_attribute])
88
+ subject.to_a.should == [city_attribute, full_name_attribute]
89
+ end
90
+
91
+ it "moves non-static attributes to the end of the list" do
92
+ subject.define_attribute(full_name_attribute)
93
+ subject.apply_attributes([city_attribute, email_attribute])
94
+ subject.to_a.should == [city_attribute, full_name_attribute, email_attribute]
95
+ end
96
+
97
+ it "maintains order of non-static attributes" do
98
+ subject.define_attribute(full_name_attribute)
99
+ subject.define_attribute(login_attribute)
100
+ subject.apply_attributes([city_attribute, email_attribute])
101
+ subject.to_a.should == [city_attribute, full_name_attribute, email_attribute, login_attribute]
102
+ end
103
+
104
+ it "overwrites attributes that are already defined" do
105
+ subject.define_attribute(full_name_attribute)
106
+ attribute_with_same_name = FactoryGirl::Attribute::Static.new(:full_name, "Benjamin Franklin")
107
+
108
+ subject.apply_attributes([attribute_with_same_name])
109
+ subject.to_a.should == [attribute_with_same_name]
110
+ end
111
+ end
@@ -130,7 +130,7 @@ describe FactoryGirl::DefinitionProxy do
130
130
  name = :user
131
131
  attr = 'attribute'
132
132
  stub(attr).name { name }
133
- mock(FactoryGirl::Attribute::Implicit).new(name) { attr }
133
+ mock(FactoryGirl::Attribute::Implicit).new(name, factory) { attr }
134
134
  subject.send(name)
135
135
  factory.attributes.should include(attr)
136
136
  end
@@ -25,24 +25,6 @@ describe FactoryGirl::Factory do
25
25
  @factory.default_strategy.should == :create
26
26
  end
27
27
 
28
- it "should not allow the same attribute to be added twice" do
29
- lambda {
30
- 2.times { @factory.define_attribute FactoryGirl::Attribute::Static.new(:name, 'value') }
31
- }.should raise_error(FactoryGirl::AttributeDefinitionError)
32
- end
33
-
34
- it "should add a callback attribute when defining a callback" do
35
- mock(FactoryGirl::Attribute::Callback).new(:after_create, is_a(Proc)) { 'after_create callback' }
36
- @factory.add_callback(:after_create) {}
37
- @factory.attributes.should include('after_create callback')
38
- end
39
-
40
- it "should raise an InvalidCallbackNameError when defining a callback with an invalid name" do
41
- lambda{
42
- @factory.add_callback(:invalid_callback_name) {}
43
- }.should raise_error(FactoryGirl::InvalidCallbackNameError)
44
- end
45
-
46
28
  describe "after adding an attribute" do
47
29
  before do
48
30
  @attribute = "attribute"
@@ -24,6 +24,7 @@ RSpec.configure do |config|
24
24
  config.after do
25
25
  FactoryGirl.factories.clear
26
26
  FactoryGirl.sequences.clear
27
+ FactoryGirl.traits.clear
27
28
  end
28
29
  end
29
30
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: factory_girl
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.3
4
+ version: 2.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,12 +9,12 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-05 00:00:00.000000000 -04:00
12
+ date: 2011-08-12 00:00:00.000000000 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: rcov
17
- requirement: &2152633400 !ruby/object:Gem::Requirement
17
+ requirement: &2152563020 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,10 +22,10 @@ dependencies:
22
22
  version: '0'
23
23
  type: :development
24
24
  prerelease: false
25
- version_requirements: *2152633400
25
+ version_requirements: *2152563020
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: rspec
28
- requirement: &2152632980 !ruby/object:Gem::Requirement
28
+ requirement: &2152562560 !ruby/object:Gem::Requirement
29
29
  none: false
30
30
  requirements:
31
31
  - - ! '>='
@@ -33,10 +33,10 @@ dependencies:
33
33
  version: '0'
34
34
  type: :development
35
35
  prerelease: false
36
- version_requirements: *2152632980
36
+ version_requirements: *2152562560
37
37
  - !ruby/object:Gem::Dependency
38
38
  name: cucumber
39
- requirement: &2152632560 !ruby/object:Gem::Requirement
39
+ requirement: &2152562040 !ruby/object:Gem::Requirement
40
40
  none: false
41
41
  requirements:
42
42
  - - ! '>='
@@ -44,10 +44,10 @@ dependencies:
44
44
  version: '0'
45
45
  type: :development
46
46
  prerelease: false
47
- version_requirements: *2152632560
47
+ version_requirements: *2152562040
48
48
  - !ruby/object:Gem::Dependency
49
49
  name: activerecord
50
- requirement: &2152632040 !ruby/object:Gem::Requirement
50
+ requirement: &2152561440 !ruby/object:Gem::Requirement
51
51
  none: false
52
52
  requirements:
53
53
  - - ~>
@@ -55,10 +55,10 @@ dependencies:
55
55
  version: 2.3.5
56
56
  type: :development
57
57
  prerelease: false
58
- version_requirements: *2152632040
58
+ version_requirements: *2152561440
59
59
  - !ruby/object:Gem::Dependency
60
60
  name: activerecord
61
- requirement: &2152631500 !ruby/object:Gem::Requirement
61
+ requirement: &2152560840 !ruby/object:Gem::Requirement
62
62
  none: false
63
63
  requirements:
64
64
  - - ~>
@@ -66,10 +66,10 @@ dependencies:
66
66
  version: 3.0.0.beta3
67
67
  type: :development
68
68
  prerelease: false
69
- version_requirements: *2152631500
69
+ version_requirements: *2152560840
70
70
  - !ruby/object:Gem::Dependency
71
71
  name: rr
72
- requirement: &2152631080 !ruby/object:Gem::Requirement
72
+ requirement: &2152560400 !ruby/object:Gem::Requirement
73
73
  none: false
74
74
  requirements:
75
75
  - - ! '>='
@@ -77,10 +77,10 @@ dependencies:
77
77
  version: '0'
78
78
  type: :development
79
79
  prerelease: false
80
- version_requirements: *2152631080
80
+ version_requirements: *2152560400
81
81
  - !ruby/object:Gem::Dependency
82
82
  name: sqlite3
83
- requirement: &2152630620 !ruby/object:Gem::Requirement
83
+ requirement: &2152530400 !ruby/object:Gem::Requirement
84
84
  none: false
85
85
  requirements:
86
86
  - - ! '>='
@@ -88,10 +88,10 @@ dependencies:
88
88
  version: '0'
89
89
  type: :development
90
90
  prerelease: false
91
- version_requirements: *2152630620
91
+ version_requirements: *2152530400
92
92
  - !ruby/object:Gem::Dependency
93
93
  name: aruba
94
- requirement: &2152621520 !ruby/object:Gem::Requirement
94
+ requirement: &2152529980 !ruby/object:Gem::Requirement
95
95
  none: false
96
96
  requirements:
97
97
  - - ! '>='
@@ -99,7 +99,7 @@ dependencies:
99
99
  version: '0'
100
100
  type: :development
101
101
  prerelease: false
102
- version_requirements: *2152621520
102
+ version_requirements: *2152529980
103
103
  description: ! "factory_girl provides a framework and DSL for defining and\n using
104
104
  factories - less error-prone, more explicit, and\n all-around
105
105
  easier to work with than fixtures."
@@ -124,7 +124,9 @@ files:
124
124
  - lib/factory_girl/attribute/implicit.rb
125
125
  - lib/factory_girl/attribute/sequence.rb
126
126
  - lib/factory_girl/attribute/static.rb
127
+ - lib/factory_girl/attribute/trait.rb
127
128
  - lib/factory_girl/attribute.rb
129
+ - lib/factory_girl/attribute_list.rb
128
130
  - lib/factory_girl/definition_proxy.rb
129
131
  - lib/factory_girl/deprecated.rb
130
132
  - lib/factory_girl/factory.rb
@@ -146,11 +148,13 @@ files:
146
148
  - lib/factory_girl/syntax/sham.rb
147
149
  - lib/factory_girl/syntax/vintage.rb
148
150
  - lib/factory_girl/syntax.rb
151
+ - lib/factory_girl/trait.rb
149
152
  - lib/factory_girl/version.rb
150
153
  - lib/factory_girl.rb
151
154
  - spec/acceptance/acceptance_helper.rb
152
155
  - spec/acceptance/attribute_aliases_spec.rb
153
156
  - spec/acceptance/attributes_for_spec.rb
157
+ - spec/acceptance/attributes_ordered_spec.rb
154
158
  - spec/acceptance/build_list_spec.rb
155
159
  - spec/acceptance/build_spec.rb
156
160
  - spec/acceptance/build_stubbed_spec.rb
@@ -160,6 +164,7 @@ files:
160
164
  - spec/acceptance/default_strategy_spec.rb
161
165
  - spec/acceptance/definition_spec.rb
162
166
  - spec/acceptance/definition_without_block_spec.rb
167
+ - spec/acceptance/overrides_spec.rb
163
168
  - spec/acceptance/parent_spec.rb
164
169
  - spec/acceptance/sequence_spec.rb
165
170
  - spec/acceptance/syntax/blueprint_spec.rb
@@ -167,6 +172,7 @@ files:
167
172
  - spec/acceptance/syntax/make_spec.rb
168
173
  - spec/acceptance/syntax/sham_spec.rb
169
174
  - spec/acceptance/syntax/vintage_spec.rb
175
+ - spec/acceptance/traits_spec.rb
170
176
  - spec/factory_girl/aliases_spec.rb
171
177
  - spec/factory_girl/attribute/association_spec.rb
172
178
  - spec/factory_girl/attribute/callback_spec.rb
@@ -174,6 +180,7 @@ files:
174
180
  - spec/factory_girl/attribute/implicit_spec.rb
175
181
  - spec/factory_girl/attribute/sequence_spec.rb
176
182
  - spec/factory_girl/attribute/static_spec.rb
183
+ - spec/factory_girl/attribute_list_spec.rb
177
184
  - spec/factory_girl/attribute_spec.rb
178
185
  - spec/factory_girl/definition_proxy_spec.rb
179
186
  - spec/factory_girl/deprecated_spec.rb
@@ -224,6 +231,7 @@ summary: factory_girl provides a framework and DSL for defining and using model
224
231
  test_files:
225
232
  - spec/acceptance/attribute_aliases_spec.rb
226
233
  - spec/acceptance/attributes_for_spec.rb
234
+ - spec/acceptance/attributes_ordered_spec.rb
227
235
  - spec/acceptance/build_list_spec.rb
228
236
  - spec/acceptance/build_spec.rb
229
237
  - spec/acceptance/build_stubbed_spec.rb
@@ -233,6 +241,7 @@ test_files:
233
241
  - spec/acceptance/default_strategy_spec.rb
234
242
  - spec/acceptance/definition_spec.rb
235
243
  - spec/acceptance/definition_without_block_spec.rb
244
+ - spec/acceptance/overrides_spec.rb
236
245
  - spec/acceptance/parent_spec.rb
237
246
  - spec/acceptance/sequence_spec.rb
238
247
  - spec/acceptance/syntax/blueprint_spec.rb
@@ -240,6 +249,7 @@ test_files:
240
249
  - spec/acceptance/syntax/make_spec.rb
241
250
  - spec/acceptance/syntax/sham_spec.rb
242
251
  - spec/acceptance/syntax/vintage_spec.rb
252
+ - spec/acceptance/traits_spec.rb
243
253
  - spec/factory_girl/aliases_spec.rb
244
254
  - spec/factory_girl/attribute/association_spec.rb
245
255
  - spec/factory_girl/attribute/callback_spec.rb
@@ -247,6 +257,7 @@ test_files:
247
257
  - spec/factory_girl/attribute/implicit_spec.rb
248
258
  - spec/factory_girl/attribute/sequence_spec.rb
249
259
  - spec/factory_girl/attribute/static_spec.rb
260
+ - spec/factory_girl/attribute_list_spec.rb
250
261
  - spec/factory_girl/attribute_spec.rb
251
262
  - spec/factory_girl/definition_proxy_spec.rb
252
263
  - spec/factory_girl/deprecated_spec.rb