myronmarston-factory_data_preloader 0.3.1 → 0.3.2

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.
@@ -1,4 +1,4 @@
1
1
  ---
2
2
  :major: 0
3
3
  :minor: 3
4
- :patch: 1
4
+ :patch: 2
@@ -8,21 +8,20 @@ module FactoryDataPreloader
8
8
  @@preloaded_cache = nil
9
9
  @@preloaded_data_deleted = nil
10
10
  @@single_test_cache = {}
11
- @@preloaders = []
12
-
11
+
13
12
  class << self
14
13
  # An Array of strings specifying locations that should be searched for
15
14
  # factory_data definitions. By default, factory_data_preloader will attempt to require
16
15
  # "factory_data," "test/factory_data," and "spec/factory_data." Only the first
17
16
  # existing file will be loaded.
18
17
  attr_accessor :definition_file_paths
19
-
18
+
20
19
  def preload(model_type, options = {}, &proc)
21
- raise PreloaderAlreadyDefinedError.new, "You have already defined the preloader for #{model_type.to_s}" if @@preloaders.map(&:model_type).include?(model_type)
22
-
20
+ raise PreloaderAlreadyDefinedError.new, "You have already defined the preloader for #{model_type.to_s}" if PreloaderCollection.instance.map(&:model_type).include?(model_type)
21
+
23
22
  model_class = options[:model_class] || model_type.to_s.singularize.classify.constantize
24
23
  depends_on = [options[:depends_on]].compact.flatten
25
- @@preloaders << FactoryDataPreloader::Preloader.new(model_type, model_class, proc, @@preloaders.size, depends_on)
24
+ FactoryDataPreloader::Preloader.new(model_type, model_class, proc, depends_on)
26
25
 
27
26
  class << self; self; end.class_eval do
28
27
  define_method model_type do |key|
@@ -34,21 +33,20 @@ module FactoryDataPreloader
34
33
  def delete_preload_data!
35
34
  # make sure this only runs once...
36
35
  return unless @@preloaded_data_deleted.nil?
37
-
38
- # the preloaders are listed in the parent -> child table order,
39
- # so we need to delete them in reverse.
40
- @@preloaders.sort.reverse.each do |preloader|
36
+
37
+ # Delete them in the reverse order of the dependencies, to handle foreign keys
38
+ PreloaderCollection.instance.dependency_order.reverse.each do |preloader|
41
39
  preloader.model_class.delete_all
42
40
  end
43
-
41
+
44
42
  @@preloaded_data_deleted = true
45
43
  end
46
44
 
47
45
  def preload_data!
48
46
  return unless @@preloaded_cache.nil? # make sure the data is only preloaded once.
49
47
  @@preloaded_cache = {}
50
-
51
- @@preloaders.sort.each do |preloader|
48
+
49
+ PreloaderCollection.instance.dependency_order.each do |preloader|
52
50
  cache = @@preloaded_cache[preloader.model_type] ||= {}
53
51
  preloader.data.each do |key, record|
54
52
  if record.new_record? && !record.save
@@ -57,16 +55,16 @@ module FactoryDataPreloader
57
55
  puts "\n\n"
58
56
  next
59
57
  end
60
-
58
+
61
59
  cache[key] = record.id
62
60
  end
63
61
  end
64
62
  end
65
-
63
+
66
64
  def reset_cache!
67
65
  @@single_test_cache = {}
68
66
  end
69
-
67
+
70
68
  def find_definitions
71
69
  definition_file_paths.each do |path|
72
70
  require("#{path}.rb") if File.exists?("#{path}.rb")
@@ -89,7 +87,7 @@ module FactoryDataPreloader
89
87
  record
90
88
  end
91
89
  end
92
-
90
+
93
91
  # Borrowed from shoulda: http://github.com/thoughtbot/shoulda/blob/e02228d45a879ff92cb72b84f5fccc6a5f856a65/lib/shoulda/active_record/helpers.rb#L4-9
94
92
  def pretty_error_messages(obj)
95
93
  obj.errors.map do |a, m|
@@ -98,7 +96,7 @@ module FactoryDataPreloader
98
96
  end
99
97
  end
100
98
  end
101
-
99
+
102
100
  self.definition_file_paths = %w(factory_data test/factory_data spec/factory_data)
103
101
  end
104
102
  end
@@ -1,11 +1,14 @@
1
1
  module FactoryDataPreloader
2
+ class PreloaderNotDefinedError < StandardError; end
3
+
2
4
  class Preloader
3
- attr_accessor :model_type, :model_class, :proc, :defined_index, :depends_on
4
-
5
- def initialize(model_type, model_class, proc, defined_index, depends_on)
6
- @model_type, @model_class, @proc, @defined_index, @depends_on = model_type, model_class, proc, defined_index, depends_on || []
5
+ attr_accessor :model_type, :model_class, :proc, :depends_on
6
+
7
+ def initialize(model_type, model_class, proc, depends_on)
8
+ @model_type, @model_class, @proc, @depends_on = model_type, model_class, proc, depends_on || []
9
+ PreloaderCollection.instance << self
7
10
  end
8
-
11
+
9
12
  def data
10
13
  @data ||= begin
11
14
  data = {}
@@ -13,15 +16,34 @@ module FactoryDataPreloader
13
16
  data
14
17
  end
15
18
  end
16
-
17
- def <=>(preloader)
18
- if self.depends_on.include?(preloader.model_type)
19
- 1
20
- elsif preloader.depends_on.include?(self.model_type)
21
- -1
22
- else
23
- self.defined_index <=> preloader.defined_index
19
+
20
+ def dependencies
21
+ @dependencies ||= begin
22
+ self.depends_on.collect do |dependency|
23
+ preloader = PreloaderCollection.instance.detect { |p| p.model_type == dependency }
24
+ raise PreloaderNotDefinedError, "The preloader for :#{dependency} has not been defined." unless preloader
25
+ preloader
26
+ end
24
27
  end
25
28
  end
26
29
  end
30
+
31
+ class PreloaderCollection < Array
32
+ include Singleton
33
+
34
+ def dependency_order
35
+ unordered_preloaders = Array.new(self) # rather than using self.dup since singleton doesn't allow duping.
36
+ ordered_preloaders = []
37
+
38
+ until unordered_preloaders.empty?
39
+ unordered_preloaders.each do |preloader|
40
+ if preloader.dependencies.all? { |dependency| ordered_preloaders.include?(dependency) }
41
+ ordered_preloaders << unordered_preloaders.delete(preloader)
42
+ end
43
+ end
44
+ end
45
+
46
+ ordered_preloaders
47
+ end
48
+ end
27
49
  end
@@ -6,9 +6,23 @@ end
6
6
 
7
7
  class Post < ActiveRecord::Base
8
8
  belongs_to :user
9
+ has_many :comments
10
+ has_many :post_images
9
11
  end
10
12
 
11
13
  class Comment < ActiveRecord::Base
12
14
  belongs_to :post
13
15
  belongs_to :user
16
+ end
17
+
18
+ class PostImage < ActiveRecord::Base
19
+ belongs_to :post
20
+ has_many :post_image_ratings
21
+ end
22
+
23
+ class PostImageRating < ActiveRecord::Base
24
+ belongs_to :post_image
25
+ end
26
+
27
+ class IpAddress < ActiveRecord::Base
14
28
  end
@@ -24,4 +24,25 @@ OutputCapturer.capture do
24
24
  t.timestamps
25
25
  end
26
26
  end
27
+
28
+ ActiveRecord::Schema.define do
29
+ create_table :post_images, :force => true do |t|
30
+ t.references :post
31
+ t.timestamps
32
+ end
33
+ end
34
+
35
+ ActiveRecord::Schema.define do
36
+ create_table :post_image_ratings, :force => true do |t|
37
+ t.references :post_image
38
+ t.timestamps
39
+ end
40
+ end
41
+
42
+ ActiveRecord::Schema.define do
43
+ create_table :ip_addresses, :force => true do |t|
44
+ t.string :ip_address
45
+ t.timestamps
46
+ end
47
+ end
27
48
  end
@@ -1,43 +1,65 @@
1
1
  require File.dirname(__FILE__) + '/test_helper'
2
2
 
3
- class FactoryDataTest < Test::Unit::TestCase
3
+ class PreloaderTest < Test::Unit::TestCase
4
+ def setup
5
+ FactoryDataPreloader::PreloaderCollection.instance.clear
6
+ end
7
+
4
8
  context 'A new preloader' do
5
9
  setup do
6
- proc = lambda { |data|
10
+ proc = lambda { |data|
7
11
  data[:thom] = User.create(:first_name => 'Thom', :last_name => 'York')
8
12
  data[:john] = User.create(:first_name => 'John', :last_name => 'Doe')
9
13
  }
10
- @preloader = FactoryDataPreloader::Preloader.new(:users, User, proc, 0, [])
14
+ @preloader = FactoryDataPreloader::Preloader.new(:users, User, proc, [])
11
15
  end
12
-
16
+
13
17
  should 'return the preloaded data for #data' do
14
18
  data = @preloader.data
15
19
  assert_equal 'York', data[:thom].last_name
16
20
  assert_equal 'Doe', data[:john].last_name
17
21
  end
22
+
23
+ should 'be automatically added to the PreloaderCollection' do
24
+ assert_equal [@preloader], FactoryDataPreloader::PreloaderCollection.instance
25
+ end
18
26
  end
19
-
20
- context 'Post and user preloaders, where post depends on users' do
27
+
28
+ context 'A preloader with dependencies' do
21
29
  setup do
22
- @posts = FactoryDataPreloader::Preloader.new(:posts, Post, nil, 0, [:users])
23
- @users = FactoryDataPreloader::Preloader.new(:users, User, nil, 1, [])
30
+ @comments = FactoryDataPreloader::Preloader.new(:comments, Comment, nil, [:users, :posts])
24
31
  end
25
-
26
- should 'return a comparison value indicating that users is less than posts' do
27
- assert_equal -1, @users <=> @posts
28
- assert_equal 1, @posts <=> @users
32
+
33
+ should 'raise PreloaderNotDefinedError for #dependencies if the preloader it depends on are not defined' do
34
+ assert_raise FactoryDataPreloader::PreloaderNotDefinedError do
35
+ @comments.dependencies
36
+ end
37
+ end
38
+
39
+ context 'when the dependency preloaders have also been defined' do
40
+ setup do
41
+ @posts = FactoryDataPreloader::Preloader.new(:posts, Post, nil, [:users])
42
+ @users = FactoryDataPreloader::Preloader.new(:users, User, nil, [])
43
+ end
44
+
45
+ should 'return the preloader objects for #dependencies' do
46
+ assert_equal [@users, @posts], @comments.dependencies
47
+ end
29
48
  end
30
49
  end
31
-
32
- context 'Post and user preloaders, where neither post depends on the other' do
50
+
51
+ context 'A series of preloaders, with dependencies,' do
33
52
  setup do
34
- @posts = FactoryDataPreloader::Preloader.new(:posts, Post, nil, 0, [])
35
- @users = FactoryDataPreloader::Preloader.new(:users, User, nil, 1, [])
53
+ @post_image_ratings = FactoryDataPreloader::Preloader.new(:post_image_ratings, PostImageRating, nil, [:post_images])
54
+ @post_images = FactoryDataPreloader::Preloader.new(:post_images, PostImage, nil, [:posts])
55
+ @ip_addresses = FactoryDataPreloader::Preloader.new(:ip_addresses, IpAddress, nil, [])
56
+ @posts = FactoryDataPreloader::Preloader.new(:posts, Post, nil, [:users])
57
+ @users = FactoryDataPreloader::Preloader.new(:users, User, nil, [])
36
58
  end
37
-
38
- should 'return a comparison value based on their defined index' do
39
- assert_equal 1, @users <=> @posts
40
- assert_equal -1, @posts <=> @users
59
+
60
+ should 'sort correctly for PreloaderCollection.instance.dependency_order' do
61
+ expected = [@ip_addresses, @users, @posts, @post_images, @post_image_ratings]
62
+ assert_equal expected.map(&:model_type), FactoryDataPreloader::PreloaderCollection.instance.dependency_order.map(&:model_type)
41
63
  end
42
64
  end
43
65
  end
@@ -41,20 +41,20 @@ end
41
41
  class FactoryDataPreloader::FactoryData
42
42
  # helper method to reset the factory data between test runs.
43
43
  def self.reset!
44
- @@preloaders.reverse.each do |preloader|
45
- class << self; self; end.class_eval do
46
- remove_method(preloader.model_type)
44
+ FactoryDataPreloader::PreloaderCollection.instance.dependency_order.reverse.each do |preloader|
45
+ class << self; self; end.class_eval do
46
+ remove_method(preloader.model_type)
47
47
  end
48
-
48
+
49
49
  unless @@preloaded_cache.nil?
50
50
  preloader.model_class.delete_all(:id => (@@preloaded_cache[preloader.model_type] || {}).values)
51
51
  end
52
52
  end
53
-
53
+
54
54
  @@preloaded_cache = nil
55
55
  @@preloaded_data_deleted = nil
56
56
  @@single_test_cache = {}
57
- @@preloaders = []
57
+ FactoryDataPreloader::PreloaderCollection.instance.clear
58
58
  end
59
59
  end
60
60
 
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.3.1
4
+ version: 0.3.2
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-03-30 00:00:00 -07:00
12
+ date: 2009-04-07 00:00:00 -07:00
13
13
  default_executable:
14
14
  dependencies: []
15
15