myronmarston-factory_data_preloader 0.4.3 → 0.5.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.
data/CHANGELOG.rdoc ADDED
@@ -0,0 +1,41 @@
1
+ == 0.5.0 / 2009-07-07
2
+
3
+ * Added better error handling. The data[:key] = record form is deprecated in favor of data.add(:key) { record }.
4
+ * Allow preloaders to be redefined. This should make this more compatible with autotest.
5
+
6
+ == 0.4.3 / 2009-06-05
7
+
8
+ * Added shoulda and mocha development dependencies.
9
+
10
+ == 0.4.2 / 2009-06-02
11
+
12
+ * Raise an appropriate error when the developer tries to get a record for a preloader that was never run.
13
+
14
+ == 0.4.1 / 2009-06-01
15
+
16
+ * Updated documentation (Forgot to for the 0.4.0 release).
17
+
18
+ == 0.4.0 / 2009-06-01
19
+
20
+ * Added ability to only preload some of the types.
21
+ * During preloading, print to the console to indicate the records being preloaded and a benchmark.
22
+
23
+ == 0.3.2 / 2009-04-07
24
+
25
+ * Fixed a bug with the ordering of the dependent preloaders.
26
+
27
+ == 0.3.1 / 2009-03-30
28
+
29
+ * Updated documentation. (Forgot to for 0.3.0 release)
30
+
31
+ == 0.3.0 / 2009-03-30
32
+
33
+ * Added logic to auto load the factory_data files.
34
+
35
+ == 0.2.0 / 2009-03-30
36
+
37
+ * Added :depends_on option to the preloader, to force the preloaders to load in the correct order based on your foreign keys and table dependencies.
38
+
39
+ == 0.1.0 / 2009-03-30
40
+
41
+ * Initial release
data/README.rdoc CHANGED
@@ -34,23 +34,23 @@ Define your preloaded data. FactoryData will automatically require test/factory
34
34
  Define your data in these files like this:
35
35
 
36
36
  FactoryData.preload(:users) do |data|
37
- data[:thom] = User.create(:first_name => 'Thom', :last_name => 'York')
38
- data[:john] = User.create(:first_name => 'John', :last_name => 'Doe')
37
+ data.add(:thom) { User.create(:first_name => 'Thom', :last_name => 'York') }
38
+ data.add(:john) { User.create(:first_name => 'John', :last_name => 'Doe') }
39
39
  end
40
40
 
41
41
  FactoryData.preload(:posts, :depends_on => :users) do |data|
42
42
  # note the use of the :depends_on option to force the users to be loaded first.
43
- data[:tour] = FactoryData.users(:thom).posts.create(:title => 'Tour!', :body => 'Radiohead will tour soon.')
43
+ data.add(:tour) { FactoryData.users(:thom).posts.create(:title => 'Tour!', :body => 'Radiohead will tour soon.') }
44
44
  end
45
45
 
46
46
  FactoryData.preload(:some_other_posts, :model_class => Post, :depends_on => :users) do |data|
47
47
  # note the use of the :model_class option when the model class cannot be inferred from the symbol.
48
- data[:another_post] = Post.create(:user => FactoryData.users(:john), :title => 'Life is good')
48
+ data.add(:another_post) { Post.create(:user => FactoryData.users(:john), :title => 'Life is good') }
49
49
  end
50
50
 
51
51
  FactoryData.preload(:comments, :depends_on => [:users, :posts]) do |data|
52
52
  # :depends_on lets you pass an array
53
- data[:woohoo] = FactoryData.users(:john).comments.create(:post => FactoryData.posts(:tour), :comment => "I can't wait!")
53
+ data.add(:woohoo) { FactoryData.users(:john).comments.create(:post => FactoryData.posts(:tour), :comment => "I can't wait!") }
54
54
  end
55
55
 
56
56
  Finally, use this preloaded data in your tests:
@@ -80,19 +80,19 @@ at the start of each test run. If you want finer grain control over which prelo
80
80
  # test/user_test.rb
81
81
  class UserTest < ActiveSupport::TestCase
82
82
  preload_factory_data :users # multiple types can be listed as necessary
83
- # test go here...
83
+ # tests go here...
84
84
  end
85
85
 
86
86
  # test/post_test.rb
87
87
  class PostTest < ActiveSupport::TestCase
88
88
  preload_factory_data :posts # dependencies are taken into account, so users will automatically be preloaded as well.
89
- # test go here...
89
+ # tests go here...
90
90
  end
91
91
 
92
92
  == Notes, etc.
93
93
 
94
94
  * This gem has been tested with Rails 2.2.2 and Rails 2.3.2.
95
- * You can create the data using any gem or plugin you want. In this contrived example, I just used ActiveRecord's
95
+ * You can create the data using any fixture replacement you want. In this contrived example, I just used ActiveRecord's
96
96
  built in methods for simplicity's sake.
97
97
  * FactoryData#preload does not actually preload the data. It simply defines the data that will be automatically preloaded
98
98
  at the appropriate time (namely, at the same time when rails loads the fixtures).
@@ -106,6 +106,26 @@ at the start of each test run. If you want finer grain control over which prelo
106
106
  * The preloader will also delete all records from the database, before any preloading begins. This is done at the same
107
107
  time that rails deletes records for test fixtures. The tables are cleared using the reverse of the order defined by
108
108
  your :depends_on options, so be sure to use :depends_on if you have foreign key constraints.
109
+ * Errors that occur during preloading are handled smartly: a warning is printed when one occurs, and an exception is raised
110
+ when the record that had the error is accessed. This should allow the rest of the data to be preloaded, and cause only
111
+ the tests that access the records with errors to fail.
112
+ * The syntax used before the 0.5.0 release is still allowed but has been deprecated. Instead of this:
113
+
114
+ FactoryData.preload(:users) do |data|
115
+ data[:thom] = User.create(:first_name => 'Thom', :last_name => 'York')
116
+ data[:john] = User.create(:first_name => 'John', :last_name => 'Doe') }
117
+ end
118
+
119
+ Use this:
120
+
121
+ FactoryData.preload(:users) do |data|
122
+ data.add(:thom) { User.create(:first_name => 'Thom', :last_name => 'York') }
123
+ data.add(:john) { User.create(:first_name => 'John', :last_name => 'Doe') }
124
+ end
125
+
126
+ == Known Issues
127
+
128
+ * This gem doesn't play nice with autotest.
109
129
 
110
130
  == Copyright
111
131
 
data/VERSION.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
- :minor: 4
4
- :patch: 3
3
+ :minor: 5
4
+ :patch: 0
@@ -14,6 +14,7 @@ require 'active_record/fixtures'
14
14
  require 'factory_data_preloader/core_ext'
15
15
  require 'factory_data_preloader/preloader'
16
16
  require 'factory_data_preloader/preloader_collection'
17
+ require 'factory_data_preloader/preloaded_data_hash'
17
18
  require 'factory_data_preloader/factory_data'
18
19
  require 'factory_data_preloader/rails_core_ext'
19
20
 
@@ -4,12 +4,16 @@ module FactoryDataPreloader
4
4
  class PreloaderAlreadyDefinedError < StandardError; end
5
5
  class PreloadedRecordNotFound < StandardError; end
6
6
  class DefinedPreloaderNotRunError < StandardError; end
7
+ class ErrorWhilePreloadingRecord < StandardError; end
8
+
9
+ module DataMethods
10
+ end
7
11
 
8
12
  class FactoryData
9
- @@preloaded_cache = nil
10
- @@preloaded_data_deleted = nil
11
13
  @@single_test_cache = {}
12
14
 
15
+ extend DataMethods
16
+
13
17
  class << self
14
18
  # An Array of strings specifying locations that should be searched for
15
19
  # factory_data definitions. By default, factory_data_preloader will attempt to require
@@ -18,47 +22,29 @@ module FactoryDataPreloader
18
22
  attr_accessor :definition_file_paths
19
23
 
20
24
  def preload(model_type, options = {}, &proc)
21
- raise PreloaderAlreadyDefinedError.new, "You have already defined the preloader for #{model_type.to_s}" if AllPreloaders.instance.map(&:model_type).include?(model_type)
25
+ if existing_preloader = AllPreloaders.instance.from_symbol(model_type, false)
26
+ existing_preloader.remove!
27
+ end
22
28
 
23
- model_class = options[:model_class] || model_type.to_s.singularize.classify.constantize
24
- depends_on = [options[:depends_on]].compact.flatten
25
- FactoryDataPreloader::Preloader.new(model_type, model_class, proc, depends_on)
29
+ FactoryDataPreloader::Preloader.new(model_type, options[:model_class], proc, options[:depends_on])
26
30
 
27
- class << self; self; end.class_eval do
31
+ DataMethods.class_eval do
28
32
  define_method model_type do |key|
29
- get_record(model_type, model_class, key)
33
+ FactoryData.send(:get_record, model_type, key)
30
34
  end
31
35
  end
32
36
  end
33
37
 
34
38
  def delete_preload_data!
35
- # make sure this only runs once...
36
- return unless @@preloaded_data_deleted.nil?
37
-
38
39
  # Delete them in the reverse order of the dependencies, to handle foreign keys
39
- FactoryDataPreloader.requested_preloaders.reverse.each do |preloader|
40
+ FactoryDataPreloader.requested_preloaders.dependency_order.reverse.each do |preloader|
40
41
  preloader.model_class.delete_all
41
42
  end
42
-
43
- @@preloaded_data_deleted = true
44
43
  end
45
44
 
46
45
  def preload_data!
47
- return unless @@preloaded_cache.nil? # make sure the data is only preloaded once.
48
- @@preloaded_cache = {}
49
-
50
46
  FactoryDataPreloader.requested_preloaders.dependency_order.each do |preloader|
51
- cache = @@preloaded_cache[preloader.model_type] ||= {}
52
- preloader.data.each do |key, record|
53
- if record.new_record? && !record.save
54
- puts "\nError preloading factory data. #{preloader.model_class.to_s} :#{key.to_s} could not be saved. Errors: "
55
- puts pretty_error_messages(record)
56
- puts "\n\n"
57
- next
58
- end
59
-
60
- cache[key] = record.id
61
- end
47
+ preloader.preload!
62
48
  end
63
49
  end
64
50
 
@@ -80,25 +66,10 @@ module FactoryDataPreloader
80
66
 
81
67
  private
82
68
 
83
- def get_record(type, model_class, key)
84
- if @@preloaded_cache[type].nil?
85
- raise DefinedPreloaderNotRunError, "The :#{type} preloader has never been run. Did you forget to add the 'preload_factory_data :#{type}' declaration to your test case? You'll need this at the top of your test case class if you want to use the factory data defined by this preloader."
86
- end
87
-
88
- @@single_test_cache[type] ||= {}
89
- @@single_test_cache[type][key] ||= begin
90
- record = model_class.find_by_id(@@preloaded_cache[type][key])
91
- raise PreloadedRecordNotFound.new, "Could not find a record for FactoryData.#{type}(:#{key})." unless record
92
- record
93
- end
94
- end
95
-
96
- # Borrowed from shoulda: http://github.com/thoughtbot/shoulda/blob/e02228d45a879ff92cb72b84f5fccc6a5f856a65/lib/shoulda/active_record/helpers.rb#L4-9
97
- def pretty_error_messages(obj)
98
- obj.errors.map do |a, m|
99
- msg = "#{a} #{m}"
100
- msg << " (#{obj.send(a).inspect})" unless a.to_sym == :base
101
- end
69
+ def get_record(type, key)
70
+ preloader = AllPreloaders.instance.from_symbol(type)
71
+ @@single_test_cache[type] ||= {}
72
+ @@single_test_cache[type][key] ||= preloader.get_record(key)
102
73
  end
103
74
  end
104
75
 
@@ -0,0 +1,53 @@
1
+ class PreloadedDataHash
2
+ attr_reader :preloader
3
+
4
+ def initialize(preloader)
5
+ @preloader, @backing_hash = preloader, {}
6
+ end
7
+
8
+ def []=(key, record)
9
+ puts "DEPRECATION WARNING: Instead of 'data[:#{key}] = record' please use 'data.add(:#{key}) { record }'"
10
+ add_to_backing_hash(key, record)
11
+ end
12
+
13
+ def [](key)
14
+ @backing_hash[key]
15
+ end
16
+
17
+ def record_ids
18
+ @backing_hash.values.select { |value| value.is_a?(Fixnum) }
19
+ end
20
+
21
+ def add(key)
22
+ raise "You must pass a block to PreloaderDataHash#add. You forgot to use the block in your #{preloader.model_type} prelodaer for the #{key.inspect} record." unless block_given?
23
+ begin
24
+ add_to_backing_hash(key, yield)
25
+ rescue => e
26
+ puts "WARNING: an error occurred while preloading the #{preloader.model_type.to_s}(:#{key}) record: #{e.class.to_s}: #{e.message}"
27
+ add_to_backing_hash(key, nil, e)
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def add_to_backing_hash(key, record, error = nil)
34
+ print '.'
35
+ if record
36
+ if record.new_record? && !record.save
37
+ raise StandardError.new("Error preloading factory data. #{preloader.model_class.to_s} :#{key.to_s} could not be saved. Errors: #{pretty_error_messages(record)}")
38
+ else
39
+ @backing_hash[key] = record.id
40
+ end
41
+ else
42
+ @backing_hash[key] = error
43
+ end
44
+ end
45
+
46
+ # Borrowed from shoulda: http://github.com/thoughtbot/shoulda/blob/e02228d45a879ff92cb72b84f5fccc6a5f856a65/lib/shoulda/active_record/helpers.rb#L4-9
47
+ def pretty_error_messages(obj)
48
+ obj.errors.map do |a, m|
49
+ msg = "#{a} #{m}"
50
+ msg << " (#{obj.send(a).inspect})" unless a.to_sym == :base
51
+ end
52
+ end
53
+ end
@@ -1,3 +1,5 @@
1
+ require 'active_support/deprecation'
2
+
1
3
  module FactoryDataPreloader
2
4
  class PreloaderNotDefinedError < StandardError; end
3
5
 
@@ -11,50 +13,81 @@ module FactoryDataPreloader
11
13
  alias :preload_all? :preload_all
12
14
 
13
15
  def requested_preloaders
14
- @requested_preloaders ||= begin
15
- if preload_all?
16
- AllPreloaders.instance
17
- else
18
- preloaders = self.preload_types.collect { |type| AllPreloaders.instance.from_symbol(type) }
19
- preloaders += (preloaders.collect { |p| p.all_dependencies }).flatten
20
- preloaders.uniq!
21
- PreloaderCollection.new(preloaders)
22
- end
16
+ if preload_all?
17
+ AllPreloaders.instance
18
+ else
19
+ preloaders = self.preload_types.collect { |type| AllPreloaders.instance.from_symbol(type) }
20
+ preloaders += (preloaders.collect { |p| p.all_dependencies }).flatten
21
+ preloaders.uniq!
22
+ PreloaderCollection.new(preloaders)
23
23
  end
24
24
  end
25
25
  end
26
26
 
27
27
  class Preloader
28
28
  attr_accessor :model_type, :model_class, :proc, :depends_on
29
+ attr_reader :data
29
30
 
30
31
  def initialize(model_type, model_class, proc, depends_on)
31
- @model_type, @model_class, @proc, @depends_on = model_type, model_class, proc, depends_on || []
32
+ model_class ||= model_type.to_s.pluralize.classify.constantize
33
+
34
+ @model_type, @model_class, @proc, @depends_on = model_type, model_class, proc, [depends_on].compact.flatten
32
35
  AllPreloaders.instance << self
33
- end
34
36
 
35
- def data
36
- @data ||= begin
37
- data = PreloaderDataHash.new
38
- print "Preloading #{model_type}:"
39
- benchmark_measurement = Benchmark.measure { self.proc.try(:call, data) }
40
- print "(#{format('%.3f', benchmark_measurement.real)} secs)\n"
41
- data
37
+ DataMethods.class_eval do
38
+ define_method model_type do |key|
39
+ FactoryData.send(:get_record, model_type, key)
40
+ end
42
41
  end
43
42
  end
44
43
 
44
+ def preload!
45
+ @data = PreloadedDataHash.new(self)
46
+ print "Preloading #{model_type}:"
47
+ benchmark_measurement = Benchmark.measure { self.proc.try(:call, @data) }
48
+ print "(#{format('%.3f', benchmark_measurement.real)} secs)\n"
49
+ end
50
+
51
+ def preloaded?
52
+ !@data.nil?
53
+ end
54
+
45
55
  def dependencies
46
- @dependencies ||= self.depends_on.collect { |dependency| AllPreloaders.instance.from_symbol(dependency) }
56
+ self.depends_on.collect { |dependency| AllPreloaders.instance.from_symbol(dependency) }
47
57
  end
48
58
 
49
59
  def all_dependencies
50
- @all_dependencies ||= (self.dependencies + (self.dependencies.collect { |d| d.all_dependencies }).flatten).uniq
60
+ (self.dependencies + (self.dependencies.collect { |d| d.all_dependencies }).flatten).uniq
51
61
  end
52
- end
53
62
 
54
- class PreloaderDataHash < Hash
55
- def []=(key, value)
56
- print "."
57
- super
63
+ def get_record(key)
64
+ unless self.preloaded?
65
+ raise DefinedPreloaderNotRunError.new, "The :#{self.model_type} preloader has never been run. Did you forget to add the 'preload_factory_data :#{self.model_type}' declaration to your test case? You'll need this at the top of your test case class if you want to use the factory data defined by this preloader."
66
+ end
67
+
68
+ unless record_id_or_error = self.data[key]
69
+ raise PreloadedRecordNotFound.new, "Could not find a preloaded record #{self.model_type} recore for :#{key}. Did you mispell :#{key}?"
70
+ end
71
+
72
+ if record_id_or_error.is_a?(Exception)
73
+ raise ErrorWhilePreloadingRecord.new, "An error occurred while preloading #{self.model_type}(:#{key}): #{record_id_or_error.class.to_s}: #{record_id_or_error.message}\n\nBacktrace:\n\n#{record_id_or_error.backtrace}"
74
+ end
75
+
76
+ self.model_class.find_by_id(record_id_or_error)
77
+ end
78
+
79
+ def remove!
80
+ preloader = self
81
+ DataMethods.class_eval do
82
+ remove_method(preloader.model_type) if method_defined?(preloader.model_type)
83
+ end
84
+
85
+ if @data
86
+ self.model_class.delete_all(:id => @data.record_ids)
87
+ @data = nil
88
+ end
89
+
90
+ AllPreloaders.instance.delete(self)
58
91
  end
59
92
  end
60
93
 
@@ -15,9 +15,9 @@ module FactoryDataPreloader
15
15
  ordered_preloaders
16
16
  end
17
17
 
18
- def from_symbol(symbol)
18
+ def from_symbol(symbol, raise_error = true)
19
19
  unless preloader = self.detect { |p| p.model_type == symbol }
20
- raise PreloaderNotDefinedError, "The preloader for :#{symbol} has not been defined."
20
+ raise PreloaderNotDefinedError, "The preloader for :#{symbol} has not been defined." if raise_error
21
21
  end
22
22
  preloader
23
23
  end
@@ -8,42 +8,52 @@ class FactoryDataTest < Test::Unit::TestCase
8
8
  context 'Calling FactoryData.preload(:users)' do
9
9
  setup do
10
10
  FactoryData.preload(:users) do |data|
11
- data[:thom] = User.create(:first_name => 'Thom', :last_name => 'York')
11
+ data.add(:thom) { User.create(:first_name => 'Thom', :last_name => 'York') }
12
12
  end
13
13
  end
14
14
 
15
15
  should_not_change 'User.count'
16
16
  should_change "FactoryData.methods.include?('users')", :from => false, :to => true
17
17
 
18
- should 'not allow it to be called again' do
19
- assert_raise FactoryDataPreloader::PreloaderAlreadyDefinedError do
20
- FactoryData.preload(:users)
21
- end
22
- end
23
-
24
18
  context 'when there was a previous user record in the database' do
25
19
  setup { User.create(:first_name => 'Barack', :last_name => 'Obama') }
26
20
 
27
21
  context 'and calling FactoryData.delete_preload_data!' do
28
22
  setup { FactoryData.delete_preload_data! }
29
23
  should_change 'User.count', :to => 0
30
-
31
- context 'and there is another record in the database' do
32
- setup { User.create(:first_name => 'George', :last_name => 'Bush') }
33
-
34
- context 'and FactoryData.delete_preload_data! is called again' do
35
- setup { FactoryData.delete_preload_data! }
36
- should_not_change 'User.count'
37
- end
38
- end
39
24
  end
40
25
  end
41
26
 
42
27
  context 'and later calling FactoryData.preload_data!' do
43
- setup { FactoryData.preload_data! }
28
+ setup do
29
+ @out, @err = OutputCapturer.capture do
30
+ FactoryData.preload_data!
31
+ end
32
+ end
44
33
 
45
34
  should_change 'User.count', :by => 1
46
35
 
36
+ context 'and later re-defining the preloaders' do
37
+ setup do
38
+ FactoryData.preload(:users) do |data|
39
+ data.add(:thom) { User.create(:first_name => 'Thom', :last_name => 'York') }
40
+ data.add(:john) { User.create(:first_name => 'John', :last_name => 'Doe') }
41
+ end
42
+ end
43
+
44
+ should_change 'User.count', :by => -1
45
+
46
+ context 'and preloading the re-defined preloader' do
47
+ setup do
48
+ @out, @err = OutputCapturer.capture do
49
+ FactoryData.preload_data!
50
+ end
51
+ end
52
+
53
+ should_change 'User.count', :by => 2
54
+ end
55
+ end
56
+
47
57
  context 'and later calling FactoryData.users(key)' do
48
58
  setup { @user = FactoryData.users(:thom) }
49
59
 
@@ -77,47 +87,14 @@ class FactoryDataTest < Test::Unit::TestCase
77
87
  end
78
88
  end
79
89
 
80
- context 'Preloading a record that has not been saved' do
81
- setup do
82
- @unsaved_user = User.new(:first_name => 'George', :last_name => 'Washington')
83
- assert @unsaved_user.new_record?
84
-
85
- FactoryData.preload(:users) do |data|
86
- data[:george] = @unsaved_user
87
- end
88
- end
89
-
90
- should 'save the record wen preload_data! is called' do
91
- FactoryData.preload_data!
92
- assert !@unsaved_user.new_record?
93
- end
94
- end
95
-
96
- context 'Preloading a record that cannot be saved to the database' do
97
- setup do
98
- @invalid_user = User.new(:first_name => 'Bob')
99
- assert !@invalid_user.valid?
100
-
101
- FactoryData.preload(:users) do |data|
102
- data[:bob] = @invalid_user
103
- end
104
- end
105
-
106
- should 'print an appropriate error message when preload_data! is called' do
107
- out, err = OutputCapturer.capture do
108
- FactoryData.preload_data!
109
- end
110
-
111
- assert_match /Error preloading factory data\.\s+User :bob could not be saved\.\s+Errors:\s+last_name can't be blank/im, out
112
- end
113
- end
114
-
115
90
  context 'Preloading with an explicit :model_class option' do
116
91
  setup do
117
92
  FactoryData.preload(:posts, :model_class => User) do |data|
118
- data[:george] = User.create(:first_name => 'George', :last_name => 'Washington')
93
+ data.add(:george) { User.create(:first_name => 'George', :last_name => 'Washington') }
94
+ end
95
+ @out, @err = OutputCapturer.capture do
96
+ FactoryData.preload_data!
119
97
  end
120
- FactoryData.preload_data!
121
98
  end
122
99
 
123
100
  should 'use the passed model_class rather than inferring the class from the symbol' do
@@ -128,23 +105,26 @@ class FactoryDataTest < Test::Unit::TestCase
128
105
  context 'Preloading multiple record types, with dependencies' do
129
106
  setup do
130
107
  FactoryData.preload(:comments, :depends_on => [:users, :posts]) do |data|
131
- data[:woohoo] = FactoryData.users(:john).comments.create(:post => FactoryData.posts(:tour), :comment => "I can't wait!")
108
+ data.add(:woohoo) { FactoryData.users(:john).comments.create(:post => FactoryData.posts(:tour), :comment => "I can't wait!") }
132
109
  end
133
110
 
134
111
  FactoryData.preload(:posts, :depends_on => :users) do |data|
135
- data[:tour] = FactoryData.users(:thom).posts.create(:title => 'Tour!', :body => 'Radiohead will tour soon.')
112
+ data.add(:tour) { FactoryData.users(:thom).posts.create(:title => 'Tour!', :body => 'Radiohead will tour soon.') }
136
113
  end
137
114
 
138
115
  FactoryData.preload(:users) do |data|
139
- data[:thom] = User.create(:first_name => 'Thom', :last_name => 'York')
140
- data[:john] = User.create(:first_name => 'John', :last_name => 'Doe')
116
+ data.add(:thom) { User.create(:first_name => 'Thom', :last_name => 'York') }
117
+ data.add(:john) { User.create(:first_name => 'John', :last_name => 'Doe') }
141
118
  end
142
119
  end
143
120
 
144
121
  should "raise the appropriate error when a developer tries to access a record that wasn't preloaded" do
145
122
  FactoryDataPreloader.preload_all = false
146
123
  FactoryDataPreloader.preload_types << :users
147
- FactoryData.preload_data!
124
+
125
+ @out, @err = OutputCapturer.capture do
126
+ FactoryData.preload_data!
127
+ end
148
128
 
149
129
  assert FactoryData.users(:thom)
150
130
  assert_raise FactoryDataPreloader::DefinedPreloaderNotRunError do
@@ -153,7 +133,10 @@ class FactoryDataTest < Test::Unit::TestCase
153
133
  end
154
134
 
155
135
  should 'preload them in the proper order, allowing you to use the dependencies' do
156
- FactoryData.preload_data!
136
+ @out, @err = OutputCapturer.capture do
137
+ FactoryData.preload_data!
138
+ end
139
+
157
140
  assert_equal 'Thom', FactoryData.users(:thom).first_name
158
141
  assert_equal 'John', FactoryData.users(:john).first_name
159
142
 
data/test/lib/models.rb CHANGED
@@ -1,3 +1,6 @@
1
+ class EmailAddress < ActiveRecord::Base
2
+ end
3
+
1
4
  class User < ActiveRecord::Base
2
5
  has_many :posts
3
6
  has_many :comments
data/test/lib/schema.rb CHANGED
@@ -1,4 +1,10 @@
1
1
  OutputCapturer.capture do
2
+ ActiveRecord::Schema.define do
3
+ create_table :email_addresses, :force => true do |t|
4
+ t.string :address
5
+ end
6
+ end
7
+
2
8
  ActiveRecord::Schema.define do
3
9
  create_table :users, :force => true do |t|
4
10
  t.string :first_name
@@ -0,0 +1,88 @@
1
+ require File.dirname(__FILE__) + '/test_helper'
2
+
3
+ class PreloadedDataHashTest < Test::Unit::TestCase
4
+ def setup
5
+ FactoryDataPreloader.reset!
6
+ end
7
+
8
+ def self.test_add_valid_record(desc, &block)
9
+ context desc do
10
+ setup do
11
+ @out, @err = OutputCapturer.capture do
12
+ @preloaded_data_hash.add(:record) { @user = instance_eval(&block) }
13
+ end
14
+ end
15
+
16
+ should 'print a dot' do
17
+ assert_equal '', @err
18
+ assert_equal '.', @out
19
+ end
20
+
21
+ should 'add the record id to the hash' do
22
+ assert_equal @user.id, @preloaded_data_hash[:record]
23
+ end
24
+ end
25
+ end
26
+
27
+ def self.test_add_invalid_record(desc, error_msg_regex, &block)
28
+ context desc do
29
+ setup do
30
+ @out, @err = OutputCapturer.capture do
31
+ @preloaded_data_hash.add(:record) { instance_eval(&block) }
32
+ end
33
+ end
34
+
35
+ should 'print a warning message' do
36
+ assert_equal '', @err
37
+ assert_match /WARNING: an error occurred while preloading/, @out
38
+ end
39
+
40
+ should 'add an error message to the hash' do
41
+ assert @preloaded_data_hash[:record].is_a?(Exception)
42
+ assert_match error_msg_regex, @preloaded_data_hash[:record].message
43
+ end
44
+ end
45
+ end
46
+
47
+ context 'For a new preloader data hash' do
48
+ setup do
49
+ @preloaded_data_hash = PreloadedDataHash.new(stub_everything(:model_class => User))
50
+ end
51
+
52
+ test_add_valid_record('when a saved record is added') { User.create!(:first_name => 'Barack', :last_name => 'Obama') }
53
+ test_add_valid_record('when a valid unsaved record is added') { User.new(:first_name => 'Barack', :last_name => 'Obama') }
54
+ test_add_invalid_record('when an invalid unsaved record is added', /Error preloading factory data.*could not be saved/) { User.new }
55
+ test_add_invalid_record('when an error occurs while adding the preloading a record', /This is an error/) { raise 'This is an error' }
56
+
57
+ context 'adding a record through the deprecated []= method' do
58
+ setup do
59
+ @out, @err = OutputCapturer.capture do
60
+ @preloaded_data_hash[:record] = (@user = User.create!(:first_name => 'Barack', :last_name => 'Obama'))
61
+ end
62
+ end
63
+
64
+ should 'print a deprecation warning' do
65
+ assert_equal '', @err
66
+ assert_match /DEPRECATION WARNING: Instead of .* please use .*/, @out
67
+ end
68
+
69
+ should 'add the record id to the hash' do
70
+ assert_equal @user.id, @preloaded_data_hash[:record]
71
+ end
72
+ end
73
+
74
+ context 'when multiple records and errors have been added' do
75
+ setup do
76
+ @out, @err = OutputCapturer.capture do
77
+ @preloaded_data_hash.add(:barack) { @barack = User.create!(:first_name => 'Barack', :last_name => 'Obama') }
78
+ @preloaded_data_hash.add(:error) { raise 'An error' }
79
+ @preloaded_data_hash.add(:george) { @george = User.create!(:first_name => 'George', :last_name => 'Washington') }
80
+ end
81
+ end
82
+
83
+ should 'return the record ids for #record_ids' do
84
+ assert_same_elements [@barack.id, @george.id], @preloaded_data_hash.record_ids
85
+ end
86
+ end
87
+ end
88
+ end
@@ -8,21 +8,66 @@ class PreloaderTest < Test::Unit::TestCase
8
8
  context 'A new preloader' do
9
9
  setup do
10
10
  proc = lambda { |data|
11
- data[:thom] = User.create(:first_name => 'Thom', :last_name => 'York')
12
- data[:john] = User.create(:first_name => 'John', :last_name => 'Doe')
11
+ data.add(:thom) { User.create(:first_name => 'Thom', :last_name => 'York') }
12
+ data.add(:john) { User.create(:first_name => 'John', :last_name => 'Doe') }
13
13
  }
14
14
  @preloader = FactoryDataPreloader::Preloader.new(:users, User, proc, [])
15
15
  end
16
16
 
17
- should 'return the preloaded data for #data' do
18
- data = @preloader.data
19
- assert_equal 'York', data[:thom].last_name
20
- assert_equal 'Doe', data[:john].last_name
21
- end
22
-
23
17
  should 'be automatically added to the PreloaderCollection' do
24
18
  assert_equal [@preloader], FactoryDataPreloader::AllPreloaders.instance
25
19
  end
20
+
21
+ context 'when preloaded' do
22
+ setup do
23
+ @out, @err = OutputCapturer.capture do
24
+ @preloader.preload!
25
+ end
26
+ end
27
+
28
+ should 'return the preloaded data when #get_record is called' do
29
+ assert_equal 'York', @preloader.get_record(:thom).last_name
30
+ assert_equal 'Doe', @preloader.get_record(:john).last_name
31
+ end
32
+
33
+ should 'print out a preloader message, a dot for each record and a benchmark' do
34
+ assert_equal '', @err
35
+ assert_match /Preloading users:\.\.\([\d\.]+ secs\)/, @out
36
+ end
37
+ end
38
+ end
39
+
40
+ context 'A new preloader for email_addresses' do
41
+ setup do
42
+ @preloader = FactoryDataPreloader::Preloader.new(:email_addresses, nil, lambda { }, [])
43
+ end
44
+
45
+ should 'infer the model class' do
46
+ assert_equal EmailAddress, @preloader.model_class
47
+ end
48
+ end
49
+
50
+ context 'A preloader with errors' do
51
+ setup do
52
+ proc = lambda { |data|
53
+ data.add(:thom) { raise StandardError('Error for thom') }
54
+ data.add(:john) { @john = User.create(:first_name => 'John', :last_name => 'Doe') }
55
+ }
56
+ @preloader = FactoryDataPreloader::Preloader.new(:users, User, proc, [])
57
+ @out, @err = OutputCapturer.capture do
58
+ @preloader.preload!
59
+ end
60
+ end
61
+
62
+ should 'raise an exception when the record with the error is accessed' do
63
+ assert_raise FactoryDataPreloader::ErrorWhilePreloadingRecord do
64
+ @preloader.get_record(:thom)
65
+ end
66
+ end
67
+
68
+ should 'allow the error-free records to be accessed, even when they were created after the error record' do
69
+ assert_equal @john, @preloader.get_record(:john)
70
+ end
26
71
  end
27
72
 
28
73
  context 'A preloader with dependencies' do
data/test/test_helper.rb CHANGED
@@ -42,28 +42,13 @@ module FactoryDataPreloader
42
42
  def self.reset!
43
43
  self.preload_all = true
44
44
  self.preload_types = []
45
- @requested_preloaders = nil
46
- FactoryData.reset!
47
- end
48
-
49
- class FactoryData
50
- # helper method to reset the factory data between test runs.
51
- def self.reset!
52
- FactoryDataPreloader::AllPreloaders.instance.each do |preloader|
53
- class << self; self; end.class_eval do
54
- remove_method(preloader.model_type) if method_defined?(preloader.model_type)
55
- end
56
45
 
57
- unless @@preloaded_cache.nil?
58
- preloader.model_class.delete_all(:id => (@@preloaded_cache[preloader.model_type] || {}).values)
59
- end
60
- end
61
-
62
- @@preloaded_cache = nil
63
- @@preloaded_data_deleted = nil
64
- @@single_test_cache = {}
65
- FactoryDataPreloader::AllPreloaders.instance.clear
46
+ preloaders = Array.new(FactoryDataPreloader::AllPreloaders.instance)
47
+ preloaders.each do |preloader|
48
+ preloader.remove!
66
49
  end
50
+
51
+ FactoryData.reset_cache!
67
52
  end
68
53
  end
69
54
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: myronmarston-factory_data_preloader
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.3
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Myron Marston
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-06-05 00:00:00 -07:00
12
+ date: 2009-07-07 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -42,11 +42,13 @@ extra_rdoc_files:
42
42
  - README.rdoc
43
43
  - LICENSE
44
44
  files:
45
+ - CHANGELOG.rdoc
45
46
  - README.rdoc
46
47
  - VERSION.yml
47
48
  - lib/factory_data_preloader
48
49
  - lib/factory_data_preloader/core_ext.rb
49
50
  - lib/factory_data_preloader/factory_data.rb
51
+ - lib/factory_data_preloader/preloaded_data_hash.rb
50
52
  - lib/factory_data_preloader/preloader.rb
51
53
  - lib/factory_data_preloader/preloader_collection.rb
52
54
  - lib/factory_data_preloader/rails_core_ext.rb
@@ -55,6 +57,7 @@ files:
55
57
  - test/lib
56
58
  - test/lib/models.rb
57
59
  - test/lib/schema.rb
60
+ - test/preloaded_data_hash_test.rb
58
61
  - test/preloader_test.rb
59
62
  - test/test_helper.rb
60
63
  - LICENSE