factory_girl 2.0.3 → 2.0.4

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