factory_girl 3.1.1 → 3.2.0

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.
Files changed (59) hide show
  1. data/.simplecov +4 -0
  2. data/GETTING_STARTED.md +147 -3
  3. data/Gemfile.lock +1 -1
  4. data/NEWS +8 -0
  5. data/features/support/env.rb +0 -1
  6. data/gemfiles/3.0.gemfile.lock +1 -1
  7. data/gemfiles/3.1.gemfile.lock +1 -1
  8. data/gemfiles/3.2.gemfile.lock +1 -1
  9. data/lib/factory_girl.rb +50 -7
  10. data/lib/factory_girl/attribute.rb +1 -1
  11. data/lib/factory_girl/attribute/association.rb +1 -1
  12. data/lib/factory_girl/attribute/dynamic.rb +1 -1
  13. data/lib/factory_girl/attribute/sequence.rb +1 -1
  14. data/lib/factory_girl/attribute/static.rb +1 -1
  15. data/lib/factory_girl/attribute_assigner.rb +12 -3
  16. data/lib/factory_girl/callback.rb +4 -1
  17. data/lib/factory_girl/{callback_runner.rb → callbacks_observer.rb} +1 -1
  18. data/lib/factory_girl/definition.rb +1 -1
  19. data/lib/factory_girl/definition_proxy.rb +4 -0
  20. data/lib/factory_girl/disallows_duplicates_registry.rb +17 -0
  21. data/lib/factory_girl/evaluator.rb +14 -11
  22. data/lib/factory_girl/factory.rb +5 -5
  23. data/lib/factory_girl/null_object.rb +14 -2
  24. data/lib/factory_girl/registry.rb +15 -23
  25. data/lib/factory_girl/reload.rb +2 -0
  26. data/lib/factory_girl/strategy_calculator.rb +1 -5
  27. data/lib/factory_girl/syntax.rb +1 -0
  28. data/lib/factory_girl/syntax/blueprint.rb +1 -0
  29. data/lib/factory_girl/syntax/generate.rb +6 -3
  30. data/lib/factory_girl/syntax/make.rb +4 -2
  31. data/lib/factory_girl/syntax/methods.rb +0 -81
  32. data/lib/factory_girl/syntax/sham.rb +1 -0
  33. data/lib/factory_girl/syntax/vintage.rb +0 -2
  34. data/lib/factory_girl/syntax_runner.rb +5 -0
  35. data/lib/factory_girl/version.rb +1 -1
  36. data/spec/acceptance/activesupport_instrumentation_spec.rb +49 -0
  37. data/spec/acceptance/build_stubbed_spec.rb +6 -6
  38. data/spec/acceptance/initialize_with_spec.rb +26 -0
  39. data/spec/acceptance/modify_factories_spec.rb +2 -2
  40. data/spec/acceptance/register_strategies_spec.rb +120 -0
  41. data/spec/acceptance/skip_create_spec.rb +19 -0
  42. data/spec/acceptance/syntax/blueprint_spec.rb +2 -0
  43. data/spec/acceptance/syntax/generate_spec.rb +2 -0
  44. data/spec/acceptance/syntax/make_spec.rb +2 -0
  45. data/spec/acceptance/syntax/vintage_spec.rb +2 -2
  46. data/spec/acceptance/syntax_methods_within_dynamic_attributes_spec.rb +41 -0
  47. data/spec/factory_girl/attribute/dynamic_spec.rb +6 -6
  48. data/spec/factory_girl/attribute_list_spec.rb +4 -4
  49. data/spec/factory_girl/callback_spec.rb +7 -7
  50. data/spec/factory_girl/definition_proxy_spec.rb +6 -6
  51. data/spec/factory_girl/disallows_duplicates_registry_spec.rb +44 -0
  52. data/spec/factory_girl/evaluator_class_definer_spec.rb +5 -5
  53. data/spec/factory_girl/factory_spec.rb +3 -3
  54. data/spec/factory_girl/null_object_spec.rb +18 -4
  55. data/spec/factory_girl/registry_spec.rb +30 -72
  56. data/spec/factory_girl/sequence_spec.rb +3 -2
  57. data/spec/factory_girl/strategy_calculator_spec.rb +1 -1
  58. data/spec/spec_helper.rb +0 -1
  59. metadata +41 -28
data/.simplecov ADDED
@@ -0,0 +1,4 @@
1
+ SimpleCov.start do
2
+ add_filter "/spec/"
3
+ add_filter "/tmp/"
4
+ end
data/GETTING_STARTED.md CHANGED
@@ -130,6 +130,24 @@ factory :user do
130
130
  end
131
131
  ```
132
132
 
133
+ In addition to running other methods dynamically, you can use FactoryGirl's
134
+ syntax methods (like `build`, `create`, and `generate`) within dynamic
135
+ attributes without having to prefix the call with `FactoryGirl.`. This allows
136
+ you to do:
137
+
138
+ ```ruby
139
+ sequence(:random_string) {|n| LoremIpsum.generate }
140
+
141
+ factory :post do
142
+ title { generate(:random_string) } # instead of FactoryGirl.generate(:random_string)
143
+ end
144
+
145
+ factory :comment do
146
+ post
147
+ body { generate(:random_string) } # instead of FactoryGirl.generate(:random_string)
148
+ end
149
+ ```
150
+
133
151
  Aliases
134
152
  -------
135
153
 
@@ -372,7 +390,7 @@ Or in lazy attributes:
372
390
 
373
391
  ```ruby
374
392
  factory :invite do
375
- invitee { FactoryGirl.generate(:email) }
393
+ invitee { generate(:email) }
376
394
  end
377
395
  ```
378
396
 
@@ -684,7 +702,7 @@ Custom Construction
684
702
  If you want to use factory_girl to construct an object where some attributes
685
703
  are passed to `initialize` or if you want to do something other than simply
686
704
  calling `new` on your build class, you can override the default behavior by
687
- defining `to_initialize` on your factory. Example:
705
+ defining `initialize_with` on your factory. Example:
688
706
 
689
707
  ```ruby
690
708
  # user.rb
@@ -705,7 +723,7 @@ factory :user do
705
723
  end
706
724
 
707
725
  email
708
- initialize_with { User.new(name) }
726
+ initialize_with { new(name) }
709
727
  end
710
728
 
711
729
  FactoryGirl.build(:user).name # Bob Hope
@@ -727,6 +745,132 @@ You can override the initializer in order to:
727
745
  * Use a method other than `new` to instantiate the instance
728
746
  * Do crazy things like decorate the instance after it's built
729
747
 
748
+ When using `initialize_with`, you don't have to declare the class itself when
749
+ calling `new`; however, any other class methods you want to call will have to
750
+ be called on the class explicitly.
751
+
752
+ For example:
753
+
754
+ ```ruby
755
+ factory :user do
756
+ ignore do
757
+ name { Faker::Name.name }
758
+ end
759
+
760
+ initialize_with { User.build_with_name(name) }
761
+ end
762
+ ```
763
+
764
+ Custom Strategies
765
+ -----------------
766
+
767
+ There are times where you may want to extend behavior of factory\_girl by
768
+ adding a custom build strategy.
769
+
770
+ Strategies define two methods: `association` and `result`. `association`
771
+ receives a `FactoryGirl::FactoryRunner` instance, upon which you can call
772
+ `run`, overriding the strategy if you want. The second method, `result`,
773
+ receives a `FactoryGirl::Evaluation` instance. It provides a way to trigger
774
+ callbacks (with `notify`), `object` or `hash` (to get the result instance or a
775
+ hash based on the attributes defined in the factory), and `create`, which
776
+ executes the `to_create` callback defined on the factory.
777
+
778
+ To understand how factory\_girl uses strategies internally, it's probably
779
+ easiest to just view the source for each of the four default strategies.
780
+
781
+ Inheritance can occasionally be useful; here's an example of inheriting from
782
+ `FactoryGirl::Strategy::Create` to build a JSON representation of your model.
783
+
784
+ ```ruby
785
+ class JsonStrategy
786
+ def initialize
787
+ @strategy = FactoryGirl.strategy_by_name(:create).new
788
+ end
789
+
790
+ delegate :association, to: :@strategy
791
+
792
+ def result(evaluation)
793
+ @strategy.result(evaluation).to_json
794
+ end
795
+ end
796
+ ```
797
+
798
+ For factory\_girl to recognize the new strategy, you can register it:
799
+
800
+ ```ruby
801
+ FactoryGirl.register_strategy(:json, JsonStrategy)
802
+ ```
803
+
804
+ This allows you to call
805
+
806
+ ```ruby
807
+ FactoryGirl.json(:user)
808
+ ```
809
+
810
+ Finally, you can override factory\_girl's own strategies if you'd like by
811
+ registering a new object in place of the strategies.
812
+
813
+ Custom Methods to Persist Objects
814
+ ---------------------------------
815
+
816
+ By default, creating a record will call `save!` on the instance; since this
817
+ may not always be ideal, you can override that behavior by defining
818
+ `to_create` on the factory:
819
+
820
+ ```ruby
821
+ factory :different_orm_model do
822
+ to_create {|instance| instance.persist! }
823
+ end
824
+ ```
825
+
826
+ To disable the persistence method altogether on create, you can `skip_create`
827
+ for that factory:
828
+
829
+ ```ruby
830
+ factory :user_without_database do
831
+ skip_create
832
+ end
833
+ ```
834
+
835
+ ActiveSupport Instrumentation
836
+ -----------------------------
837
+
838
+ In order to track what factories are created (and with what build strategy),
839
+ `ActiveSupport::Notifications` are included to provide a way to subscribe to
840
+ factories being run. One example would be to track factories based on a
841
+ threshold of execution time.
842
+
843
+ ```ruby
844
+ ActiveSupport::Notifications.subscribe("factory_girl.run_factory") do |name, start, finish, id, payload|
845
+ execution_time_in_seconds = finish - start
846
+
847
+ if execution_time_in_seconds >= 0.5
848
+ $stderr.puts "Slow factory: #{payload[:name]} using strategy #{payload[:strategy]}"
849
+ end
850
+ end
851
+ ```
852
+
853
+ Another example would be tracking all factories and how they're used
854
+ throughout your test suite. If you're using RSpec, it's as simple as adding a
855
+ `before(:suite)` and `after(:suite)`:
856
+
857
+ ```ruby
858
+ config.before(:suite) do
859
+ @factory_girl_results = {}
860
+ ActiveSupport::Notifications.subscribe("factory_girl.run_factory") do |name, start, finish, id, payload|
861
+ factory_name = payload[:name]
862
+ strategy_name = payload[:strategy]
863
+ @factory_girl_results[factory_name] ||= {}
864
+ @factory_girl_results[factory_name][strategy_name] ||= 0
865
+ @factory_girl_results[factory_name][strategy_name] += 1
866
+ end
867
+ end
868
+
869
+ config.after(:suite) do
870
+ puts @factory_girl_results
871
+ end
872
+ ```
873
+
730
874
  Cucumber Integration
731
875
  --------------------
732
876
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- factory_girl (3.1.1)
4
+ factory_girl (3.2.0)
5
5
  activesupport (>= 3.0.0)
6
6
 
7
7
  GEM
data/NEWS CHANGED
@@ -1,3 +1,11 @@
1
+ 3.2.0 (April 24, 2012)
2
+ Use AS::Notifications for pub/sub to track running factories
3
+ Call new within initialize_with implicitly on the build class
4
+ Skip to_create with skip_create
5
+ Allow registration of custom strategies
6
+ Deprecate alternate syntaxes
7
+ Implicitly call factory_girl's syntax methods from dynamic attributes
8
+
1
9
  3.1.0 (April 6, 2012)
2
10
  Sequences support aliases, which reference the same block
3
11
  Update documentation
@@ -1,7 +1,6 @@
1
1
  PROJECT_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..', '..'))
2
2
 
3
3
  require "simplecov"
4
- SimpleCov.start
5
4
 
6
5
  $: << File.join(PROJECT_ROOT, 'lib')
7
6
 
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/joshuaclayton/dev/gems/factory_girl
3
3
  specs:
4
- factory_girl (3.1.1)
4
+ factory_girl (3.2.0)
5
5
  activesupport (>= 3.0.0)
6
6
 
7
7
  GEM
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/joshuaclayton/dev/gems/factory_girl
3
3
  specs:
4
- factory_girl (3.1.1)
4
+ factory_girl (3.2.0)
5
5
  activesupport (>= 3.0.0)
6
6
 
7
7
  GEM
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/joshuaclayton/dev/gems/factory_girl
3
3
  specs:
4
- factory_girl (3.1.1)
4
+ factory_girl (3.2.0)
5
5
  activesupport (>= 3.0.0)
6
6
 
7
7
  GEM
data/lib/factory_girl.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "active_support/core_ext/module/delegation"
2
+ require "active_support/notifications"
2
3
 
3
4
  require 'factory_girl/errors'
4
5
  require 'factory_girl/factory_runner'
@@ -8,6 +9,7 @@ require "factory_girl/strategy/create"
8
9
  require "factory_girl/strategy/attributes_for"
9
10
  require "factory_girl/strategy/stub"
10
11
  require "factory_girl/strategy/null"
12
+ require 'factory_girl/disallows_duplicates_registry'
11
13
  require 'factory_girl/registry'
12
14
  require 'factory_girl/null_factory'
13
15
  require 'factory_girl/null_object'
@@ -18,7 +20,7 @@ require 'factory_girl/evaluator'
18
20
  require 'factory_girl/evaluator_class_definer'
19
21
  require 'factory_girl/attribute'
20
22
  require 'factory_girl/callback'
21
- require 'factory_girl/callback_runner'
23
+ require 'factory_girl/callbacks_observer'
22
24
  require 'factory_girl/declaration_list'
23
25
  require 'factory_girl/declaration'
24
26
  require 'factory_girl/sequence'
@@ -28,17 +30,21 @@ require 'factory_girl/aliases'
28
30
  require 'factory_girl/definition'
29
31
  require 'factory_girl/definition_proxy'
30
32
  require 'factory_girl/syntax'
33
+ require 'factory_girl/syntax_runner'
31
34
  require 'factory_girl/find_definitions'
32
35
  require 'factory_girl/reload'
33
36
  require 'factory_girl/version'
34
37
 
35
38
  module FactoryGirl
36
39
  def self.factories
37
- @factories ||= Registry.new("Factory")
40
+ @factories ||= DisallowsDuplicatesRegistry.new(Registry.new("Factory"))
38
41
  end
39
42
 
40
43
  def self.register_factory(factory)
41
- factories.add(factory)
44
+ factory.names.each do |name|
45
+ factories.register(name, factory)
46
+ end
47
+ factory
42
48
  end
43
49
 
44
50
  def self.factory_by_name(name)
@@ -46,11 +52,14 @@ module FactoryGirl
46
52
  end
47
53
 
48
54
  def self.sequences
49
- @sequences ||= Registry.new("Sequence")
55
+ @sequences ||= DisallowsDuplicatesRegistry.new(Registry.new("Sequence"))
50
56
  end
51
57
 
52
58
  def self.register_sequence(sequence)
53
- sequences.add(sequence)
59
+ sequence.names.each do |name|
60
+ sequences.register(name, sequence)
61
+ end
62
+ sequence
54
63
  end
55
64
 
56
65
  def self.sequence_by_name(name)
@@ -58,18 +67,52 @@ module FactoryGirl
58
67
  end
59
68
 
60
69
  def self.traits
61
- @traits ||= Registry.new("Trait")
70
+ @traits ||= DisallowsDuplicatesRegistry.new(Registry.new("Trait"))
62
71
  end
63
72
 
64
73
  def self.register_trait(trait)
65
- traits.add(trait)
74
+ trait.names.each do |name|
75
+ traits.register(name, trait)
76
+ end
77
+ trait
66
78
  end
67
79
 
68
80
  def self.trait_by_name(name)
69
81
  traits.find(name)
70
82
  end
71
83
 
84
+ def self.strategies
85
+ @strategies ||= Registry.new("Strategy")
86
+ end
87
+
88
+ def self.register_strategy(strategy_name, strategy_class)
89
+ strategies.register(strategy_name, strategy_class)
90
+
91
+ FactoryGirl::Syntax::Methods.module_exec do
92
+ define_method(strategy_name) do |name, *traits_and_overrides, &block|
93
+ instrumentation_payload = { name: name, strategy: strategy_name }
94
+
95
+ ActiveSupport::Notifications.instrument("factory_girl.run_factory", instrumentation_payload) do
96
+ FactoryRunner.new(name, strategy_class, traits_and_overrides).run(&block)
97
+ end
98
+ end
99
+ end
100
+ end
101
+
102
+ def self.strategy_by_name(name)
103
+ strategies.find(name)
104
+ end
105
+
106
+ def self.register_default_strategies
107
+ FactoryGirl.register_strategy(:build, FactoryGirl::Strategy::Build)
108
+ FactoryGirl.register_strategy(:create, FactoryGirl::Strategy::Create)
109
+ FactoryGirl.register_strategy(:attributes_for, FactoryGirl::Strategy::AttributesFor)
110
+ FactoryGirl.register_strategy(:build_stubbed, FactoryGirl::Strategy::Stub)
111
+ end
112
+
72
113
  def self.callback_names
73
114
  [:after_build, :after_create, :after_stub, :before_create].freeze
74
115
  end
75
116
  end
117
+
118
+ FactoryGirl.register_default_strategies
@@ -15,7 +15,7 @@ module FactoryGirl
15
15
  end
16
16
 
17
17
  def to_proc
18
- lambda { }
18
+ -> { }
19
19
  end
20
20
 
21
21
  def association?
@@ -12,7 +12,7 @@ module FactoryGirl
12
12
  def to_proc
13
13
  factory = @factory
14
14
  overrides = @overrides
15
- lambda { association(factory, overrides) }
15
+ -> { association(factory, overrides) }
16
16
  end
17
17
 
18
18
  def association?
@@ -9,7 +9,7 @@ module FactoryGirl
9
9
  def to_proc
10
10
  block = @block
11
11
 
12
- lambda {
12
+ -> {
13
13
  value = block.arity == 1 ? block.call(self) : instance_exec(&block)
14
14
  raise SequenceAbuseError if FactoryGirl::Sequence === value
15
15
  value
@@ -9,7 +9,7 @@ module FactoryGirl
9
9
 
10
10
  def to_proc
11
11
  sequence = @sequence
12
- lambda { FactoryGirl.generate(sequence) }
12
+ -> { FactoryGirl.generate(sequence) }
13
13
  end
14
14
  end
15
15
 
@@ -8,7 +8,7 @@ module FactoryGirl
8
8
 
9
9
  def to_proc
10
10
  value = @value
11
- lambda { value }
11
+ -> { value }
12
12
  end
13
13
  end
14
14
  end
@@ -1,6 +1,7 @@
1
1
  module FactoryGirl
2
2
  class AttributeAssigner
3
- def initialize(evaluator, &instance_builder)
3
+ def initialize(evaluator, build_class, &instance_builder)
4
+ @build_class = build_class
4
5
  @instance_builder = instance_builder
5
6
  @evaluator = evaluator
6
7
  @attribute_list = evaluator.class.attribute_list
@@ -18,7 +19,7 @@ module FactoryGirl
18
19
  end
19
20
 
20
21
  def hash
21
- @evaluator.instance = NullObject.new
22
+ @evaluator.instance = build_hash
22
23
 
23
24
  attributes_to_set_on_hash.inject({}) do |result, attribute|
24
25
  result[attribute] = get(attribute)
@@ -32,6 +33,10 @@ module FactoryGirl
32
33
  @build_class_instance ||= @evaluator.instance_exec(&@instance_builder)
33
34
  end
34
35
 
36
+ def build_hash
37
+ @build_hash ||= NullObject.new(hash_instance_methods_to_respond_to)
38
+ end
39
+
35
40
  def get(attribute_name)
36
41
  @evaluator.send(attribute_name)
37
42
  end
@@ -61,7 +66,11 @@ module FactoryGirl
61
66
  end
62
67
 
63
68
  def override_names
64
- @evaluator.__overrides.keys
69
+ @evaluator.__override_names__
70
+ end
71
+
72
+ def hash_instance_methods_to_respond_to
73
+ @attribute_list.map(&:name) + override_names + @build_class.instance_methods
65
74
  end
66
75
 
67
76
  def alias_names_to_ignore