factory_data_preloader 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +49 -0
- data/LICENSE +20 -0
- data/README.rdoc +132 -0
- data/VERSION.yml +4 -0
- data/lib/factory_data_preloader.rb +31 -0
- data/lib/factory_data_preloader/core_ext.rb +23 -0
- data/lib/factory_data_preloader/factory_data.rb +81 -0
- data/lib/factory_data_preloader/preloaded_data_hash.rb +53 -0
- data/lib/factory_data_preloader/preloader.rb +102 -0
- data/lib/factory_data_preloader/preloader_collection.rb +30 -0
- data/lib/factory_data_preloader/rails_core_ext.rb +39 -0
- data/test/factory_data_test.rb +149 -0
- data/test/lib/models.rb +31 -0
- data/test/lib/schema.rb +54 -0
- data/test/preloaded_data_hash_test.rb +88 -0
- data/test/preloader_test.rb +180 -0
- data/test/test_helper.rb +56 -0
- metadata +92 -0
data/CHANGELOG.rdoc
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
== 0.5.2 / 2009-07-09
|
2
|
+
|
3
|
+
* Improved backtrace output.
|
4
|
+
|
5
|
+
== 0.5.1 / 2009-07-07
|
6
|
+
|
7
|
+
* Fixed a couple of bugs that caused the data to be preloaded multiple times or deleted multiple times.
|
8
|
+
|
9
|
+
== 0.5.0 / 2009-07-07
|
10
|
+
|
11
|
+
* Added better error handling. The data[:key] = record form is deprecated in favor of data.add(:key) { record }.
|
12
|
+
* Allow preloaders to be redefined. This should make this more compatible with autotest.
|
13
|
+
|
14
|
+
== 0.4.3 / 2009-06-05
|
15
|
+
|
16
|
+
* Added shoulda and mocha development dependencies.
|
17
|
+
|
18
|
+
== 0.4.2 / 2009-06-02
|
19
|
+
|
20
|
+
* Raise an appropriate error when the developer tries to get a record for a preloader that was never run.
|
21
|
+
|
22
|
+
== 0.4.1 / 2009-06-01
|
23
|
+
|
24
|
+
* Updated documentation (Forgot to for the 0.4.0 release).
|
25
|
+
|
26
|
+
== 0.4.0 / 2009-06-01
|
27
|
+
|
28
|
+
* Added ability to only preload some of the types.
|
29
|
+
* During preloading, print to the console to indicate the records being preloaded and a benchmark.
|
30
|
+
|
31
|
+
== 0.3.2 / 2009-04-07
|
32
|
+
|
33
|
+
* Fixed a bug with the ordering of the dependent preloaders.
|
34
|
+
|
35
|
+
== 0.3.1 / 2009-03-30
|
36
|
+
|
37
|
+
* Updated documentation. (Forgot to for 0.3.0 release)
|
38
|
+
|
39
|
+
== 0.3.0 / 2009-03-30
|
40
|
+
|
41
|
+
* Added logic to auto load the factory_data files.
|
42
|
+
|
43
|
+
== 0.2.0 / 2009-03-30
|
44
|
+
|
45
|
+
* 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.
|
46
|
+
|
47
|
+
== 0.1.0 / 2009-03-30
|
48
|
+
|
49
|
+
* Initial release
|
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Myron Marston, Kashless.org
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
= factory_data_preloader
|
2
|
+
|
3
|
+
If you're like me, you really dislike using rails test fixtures. On the rails projects I've worked on that
|
4
|
+
have used test fixtures, we've had a number of tests that passed as false positives because the test
|
5
|
+
fixtures don't represent "real data". Fixtures just dump the data you've defined directly into the database,
|
6
|
+
without going through the model--meaning you don't get any of the validations or before/after save callback
|
7
|
+
behavior defined on your models.
|
8
|
+
|
9
|
+
There are multiple gems and plugins that address this; my personal favorite is {factory girl}[http://github.com/thoughtbot/factory_girl/].
|
10
|
+
|
11
|
+
However, none of the solutions I've tried are as fast as using test fixtures. When you use test fixtures,
|
12
|
+
rails rolls back the database transaction used in each test and allows you to re-use your fixture data.
|
13
|
+
This is much much quicker then creating the data you will use directly in each test.
|
14
|
+
|
15
|
+
This gem attempts to solve this issue, and give you the best of both worlds: create your test data using
|
16
|
+
factory girl, the models themselves, or any other solution you want, while also being able to pre-load it
|
17
|
+
and re-use it in each test when rails rolls back the transaction.
|
18
|
+
|
19
|
+
== Download
|
20
|
+
|
21
|
+
Github: http://github.com/myronmarston/factory_data_preloader/tree/master
|
22
|
+
|
23
|
+
Gem:
|
24
|
+
gem install myronmarston-factory_data_preloader --source http://gems.github.com
|
25
|
+
|
26
|
+
== Usage
|
27
|
+
|
28
|
+
Load the gem using Rails' 2.1+ gem support, in either config/environment.rb, or config/environments/test.rb:
|
29
|
+
config.gem 'myronmarston-factory_data_preloader',
|
30
|
+
:lib => 'factory_data_preloader',
|
31
|
+
:source => 'http://gems.github.com'
|
32
|
+
|
33
|
+
Define your preloaded data. FactoryData will automatically require test/factory_data.rb or test/factory_data/*.rb.
|
34
|
+
Define your data in these files like this:
|
35
|
+
|
36
|
+
FactoryData.preload(:users) do |data|
|
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
|
+
end
|
40
|
+
|
41
|
+
FactoryData.preload(:posts, :depends_on => :users) do |data|
|
42
|
+
# note the use of the :depends_on option to force the users to be loaded first.
|
43
|
+
data.add(:tour) { FactoryData.users(:thom).posts.create(:title => 'Tour!', :body => 'Radiohead will tour soon.') }
|
44
|
+
end
|
45
|
+
|
46
|
+
FactoryData.preload(:some_other_posts, :model_class => Post, :depends_on => :users) do |data|
|
47
|
+
# note the use of the :model_class option when the model class cannot be inferred from the symbol.
|
48
|
+
data.add(:another_post) { Post.create(:user => FactoryData.users(:john), :title => 'Life is good') }
|
49
|
+
end
|
50
|
+
|
51
|
+
FactoryData.preload(:comments, :depends_on => [:users, :posts]) do |data|
|
52
|
+
# :depends_on lets you pass an array
|
53
|
+
data.add(:woohoo) { FactoryData.users(:john).comments.create(:post => FactoryData.posts(:tour), :comment => "I can't wait!") }
|
54
|
+
end
|
55
|
+
|
56
|
+
Finally, use this preloaded data in your tests:
|
57
|
+
|
58
|
+
# test/user_test.rb
|
59
|
+
class UserTest < ActiveSupport::TestCase
|
60
|
+
def test_thom_has_last_name
|
61
|
+
user = FactoryData.users(:thom)
|
62
|
+
assert_equal 'York', user.last_name
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# test/post_test.rb
|
67
|
+
class PostTest < ActiveSupport::TestCase
|
68
|
+
def test_post_has_body
|
69
|
+
post = FactoryData.posts(:tour)
|
70
|
+
assert_not_nil post.body
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
All factory data is automatically preloaded for all tests. In a large rails application, this can incur a significant performance penalty
|
75
|
+
at the start of each test run. If you want finer grain control over which preloaders run, you can configure it like so:
|
76
|
+
|
77
|
+
# in test_helper.rb
|
78
|
+
FactoryDataPreloader.preload_all = false
|
79
|
+
|
80
|
+
# test/user_test.rb
|
81
|
+
class UserTest < ActiveSupport::TestCase
|
82
|
+
preload_factory_data :users # multiple types can be listed as necessary
|
83
|
+
# tests go here...
|
84
|
+
end
|
85
|
+
|
86
|
+
# test/post_test.rb
|
87
|
+
class PostTest < ActiveSupport::TestCase
|
88
|
+
preload_factory_data :posts # dependencies are taken into account, so users will automatically be preloaded as well.
|
89
|
+
# tests go here...
|
90
|
+
end
|
91
|
+
|
92
|
+
== Notes, etc.
|
93
|
+
|
94
|
+
* This gem has been tested with Rails 2.2.2 and Rails 2.3.2.
|
95
|
+
* You can create the data using any fixture replacement you want. In this contrived example, I just used ActiveRecord's
|
96
|
+
built in methods for simplicity's sake.
|
97
|
+
* FactoryData#preload does not actually preload the data. It simply defines the data that will be automatically preloaded
|
98
|
+
at the appropriate time (namely, at the same time when rails loads the fixtures).
|
99
|
+
* FactoryData#preload defines a new method on FactoryData using the same name as the symbol.
|
100
|
+
* This can be mixed-n-matched with fixtures. You may want to keep using fixtures in an existing code base,
|
101
|
+
or migrate over to this slowly.
|
102
|
+
* If you have dependencies between your preloaded data, you can use the :depends_on option to force some records to be preloaded
|
103
|
+
before others. Where no dependencies exist, the preloaders are run in the order they are defined.
|
104
|
+
* FactoryData#preload attempts to infer the appropriate model class from the symbol you pass. If your symbol doesn't
|
105
|
+
match the model class, pass the model class using the :model_class option.
|
106
|
+
* The preloader will also delete all records from the database, before any preloading begins. This is done at the same
|
107
|
+
time that rails deletes records for test fixtures. The tables are cleared using the reverse of the order defined by
|
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.
|
129
|
+
|
130
|
+
== Copyright
|
131
|
+
|
132
|
+
Copyright (c) 2009 Myron Marston, Kashless.org. See LICENSE for details.
|
data/VERSION.yml
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
require 'rubygems'
|
3
|
+
|
4
|
+
if ENV['RAILS_VERSION']
|
5
|
+
puts "loading Rails version #{ENV['RAILS_VERSION']}"
|
6
|
+
gem "activesupport", "= #{ENV['RAILS_VERSION']}"
|
7
|
+
gem "activerecord", "= #{ENV['RAILS_VERSION']}"
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'active_support'
|
11
|
+
require 'active_record'
|
12
|
+
require 'active_record/fixtures'
|
13
|
+
|
14
|
+
require 'factory_data_preloader/core_ext'
|
15
|
+
require 'factory_data_preloader/preloader'
|
16
|
+
require 'factory_data_preloader/preloader_collection'
|
17
|
+
require 'factory_data_preloader/preloaded_data_hash'
|
18
|
+
require 'factory_data_preloader/factory_data'
|
19
|
+
require 'factory_data_preloader/rails_core_ext'
|
20
|
+
|
21
|
+
if defined? Rails.configuration
|
22
|
+
Rails.configuration.after_initialize do
|
23
|
+
FactoryData.definition_file_paths = [
|
24
|
+
File.join(RAILS_ROOT, 'test', 'factory_data'),
|
25
|
+
File.join(RAILS_ROOT, 'spec', 'factory_data')
|
26
|
+
]
|
27
|
+
FactoryData.find_definitions
|
28
|
+
end
|
29
|
+
else
|
30
|
+
FactoryData.find_definitions
|
31
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# http://github.com/rails/rails/tree/823b623fe2de8846c37aa13250010809ac940b57/activesupport/lib/active_support/core_ext/object/misc.rb
|
2
|
+
|
3
|
+
unless Object.respond_to?(:try) # Object#try is in Rails 2.3 but not in 2.2.
|
4
|
+
class Object
|
5
|
+
# Tries to send the method only if object responds to it. Return +nil+ otherwise.
|
6
|
+
# It will also forward any arguments and/or block like Object#send does.
|
7
|
+
#
|
8
|
+
# ==== Example :
|
9
|
+
#
|
10
|
+
# # Without try
|
11
|
+
# @person ? @person.name : nil
|
12
|
+
#
|
13
|
+
# With try
|
14
|
+
# @person.try(:name)
|
15
|
+
#
|
16
|
+
# # try also accepts arguments/blocks for the method it is trying
|
17
|
+
# Person.try(:find, 1)
|
18
|
+
# @people.try(:map) {|p| p.name}
|
19
|
+
def try(method, *args, &block)
|
20
|
+
send(method, *args, &block) if respond_to?(method, true)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module FactoryDataPreloader
|
4
|
+
class PreloaderAlreadyDefinedError < StandardError; end
|
5
|
+
class PreloadedRecordNotFound < StandardError; end
|
6
|
+
class DefinedPreloaderNotRunError < StandardError; end
|
7
|
+
class ErrorWhilePreloadingRecord < StandardError; end
|
8
|
+
|
9
|
+
module DataMethods
|
10
|
+
end
|
11
|
+
|
12
|
+
class FactoryData
|
13
|
+
@@single_test_cache = {}
|
14
|
+
|
15
|
+
extend DataMethods
|
16
|
+
|
17
|
+
class << self
|
18
|
+
# An Array of strings specifying locations that should be searched for
|
19
|
+
# factory_data definitions. By default, factory_data_preloader will attempt to require
|
20
|
+
# "factory_data," "test/factory_data," and "spec/factory_data." Only the first
|
21
|
+
# existing file will be loaded.
|
22
|
+
attr_accessor :definition_file_paths
|
23
|
+
|
24
|
+
def preload(model_type, options = {}, &proc)
|
25
|
+
if existing_preloader = AllPreloaders.instance.from_symbol(model_type, false)
|
26
|
+
existing_preloader.remove!
|
27
|
+
end
|
28
|
+
|
29
|
+
FactoryDataPreloader::Preloader.new(model_type, options[:model_class], proc, options[:depends_on])
|
30
|
+
|
31
|
+
DataMethods.class_eval do
|
32
|
+
define_method model_type do |key|
|
33
|
+
FactoryData.send(:get_record, model_type, key)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def delete_preload_data!
|
39
|
+
# Delete them in the reverse order of the dependencies, to handle foreign keys
|
40
|
+
FactoryDataPreloader.requested_preloaders.dependency_order.reverse.each do |preloader|
|
41
|
+
preloader.delete_table_data!
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def preload_data!
|
46
|
+
FactoryDataPreloader.requested_preloaders.dependency_order.each do |preloader|
|
47
|
+
preloader.preload!
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def reset_cache!
|
52
|
+
@@single_test_cache = {}
|
53
|
+
end
|
54
|
+
|
55
|
+
def find_definitions
|
56
|
+
definition_file_paths.each do |path|
|
57
|
+
require("#{path}.rb") if File.exists?("#{path}.rb")
|
58
|
+
|
59
|
+
if File.directory? path
|
60
|
+
Dir[File.join(path, '*.rb')].each do |file|
|
61
|
+
require file
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
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)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
self.definition_file_paths = %w(factory_data test/factory_data spec/factory_data)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
# alias this class so that apps that use it don't have to use the fully qualified name.
|
81
|
+
FactoryData = FactoryDataPreloader::FactoryData
|
@@ -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
|
@@ -0,0 +1,102 @@
|
|
1
|
+
require 'active_support/deprecation'
|
2
|
+
|
3
|
+
module FactoryDataPreloader
|
4
|
+
class PreloaderNotDefinedError < StandardError; end
|
5
|
+
|
6
|
+
mattr_accessor :preload_all
|
7
|
+
self.preload_all = true
|
8
|
+
|
9
|
+
mattr_accessor :preload_types
|
10
|
+
self.preload_types = []
|
11
|
+
|
12
|
+
class << self
|
13
|
+
alias :preload_all? :preload_all
|
14
|
+
|
15
|
+
def requested_preloaders
|
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
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Preloader
|
28
|
+
attr_accessor :model_type, :model_class, :proc, :depends_on
|
29
|
+
attr_reader :data
|
30
|
+
|
31
|
+
def initialize(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
|
35
|
+
AllPreloaders.instance << self
|
36
|
+
|
37
|
+
DataMethods.class_eval do
|
38
|
+
define_method model_type do |key|
|
39
|
+
FactoryData.send(:get_record, model_type, key)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def preload!
|
45
|
+
return if preloaded?
|
46
|
+
@data = PreloadedDataHash.new(self)
|
47
|
+
print "Preloading #{model_type}:"
|
48
|
+
benchmark_measurement = Benchmark.measure { self.proc.try(:call, @data) }
|
49
|
+
print "(#{format('%.3f', benchmark_measurement.real)} secs)\n"
|
50
|
+
end
|
51
|
+
|
52
|
+
def preloaded?
|
53
|
+
!@data.nil?
|
54
|
+
end
|
55
|
+
|
56
|
+
def delete_table_data!
|
57
|
+
unless @table_data_deleted
|
58
|
+
self.model_class.delete_all
|
59
|
+
@table_data_deleted = true
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def dependencies
|
64
|
+
self.depends_on.collect { |dependency| AllPreloaders.instance.from_symbol(dependency) }
|
65
|
+
end
|
66
|
+
|
67
|
+
def all_dependencies
|
68
|
+
(self.dependencies + (self.dependencies.collect { |d| d.all_dependencies }).flatten).uniq
|
69
|
+
end
|
70
|
+
|
71
|
+
def get_record(key)
|
72
|
+
unless self.preloaded?
|
73
|
+
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."
|
74
|
+
end
|
75
|
+
|
76
|
+
unless record_id_or_error = self.data[key]
|
77
|
+
raise PreloadedRecordNotFound.new, "Could not find a preloaded record #{self.model_type} record for :#{key}. Did you mispell :#{key}?"
|
78
|
+
end
|
79
|
+
|
80
|
+
if record_id_or_error.is_a?(Exception)
|
81
|
+
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.join("\n ")}\n"
|
82
|
+
end
|
83
|
+
|
84
|
+
self.model_class.find_by_id(record_id_or_error)
|
85
|
+
end
|
86
|
+
|
87
|
+
def remove!
|
88
|
+
preloader = self
|
89
|
+
DataMethods.class_eval do
|
90
|
+
remove_method(preloader.model_type) if method_defined?(preloader.model_type)
|
91
|
+
end
|
92
|
+
|
93
|
+
if @data
|
94
|
+
self.model_class.delete_all(:id => @data.record_ids)
|
95
|
+
@data = nil
|
96
|
+
end
|
97
|
+
|
98
|
+
AllPreloaders.instance.delete(self)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module FactoryDataPreloader
|
2
|
+
class PreloaderCollection < Array
|
3
|
+
def dependency_order
|
4
|
+
unordered_preloaders = Array.new(self) # rather than using self.dup since singleton doesn't allow duping.
|
5
|
+
ordered_preloaders = []
|
6
|
+
|
7
|
+
until unordered_preloaders.empty?
|
8
|
+
unordered_preloaders.each do |preloader|
|
9
|
+
if preloader.dependencies.all? { |dependency| ordered_preloaders.include?(dependency) }
|
10
|
+
ordered_preloaders << unordered_preloaders.delete(preloader)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
ordered_preloaders
|
16
|
+
end
|
17
|
+
|
18
|
+
def from_symbol(symbol, raise_error = true)
|
19
|
+
unless preloader = self.detect { |p| p.model_type == symbol }
|
20
|
+
raise PreloaderNotDefinedError, "The preloader for :#{symbol} has not been defined." if raise_error
|
21
|
+
end
|
22
|
+
preloader
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class AllPreloaders < PreloaderCollection
|
27
|
+
include Singleton
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# TODO: add tests for this. I've manually tested this in a Rails 2.2 and 2.3 app, but haven't found a way to add
|
2
|
+
# a test for this to our test suite. It's difficult to test this since it just modifies what happens before the
|
3
|
+
# tests are run.
|
4
|
+
|
5
|
+
# Between Rails 2.2 and 2.3, the fixture loading code was moved from
|
6
|
+
# Test::Unit::TestCase to ActiveRecord::TestFixtures. See this commit:
|
7
|
+
# http://github.com/rails/rails/commit/b0ee1bdf2650d7a8380d4e9be58bba8d9c5bd40e
|
8
|
+
patch_module = defined?(ActiveRecord::TestFixtures) ? ActiveRecord::TestFixtures : Test::Unit::TestCase
|
9
|
+
|
10
|
+
patch_module.class_eval do
|
11
|
+
def load_fixtures_with_preloaded_factory_data
|
12
|
+
val = load_fixtures_without_preloaded_factory_data
|
13
|
+
FactoryData.preload_data!
|
14
|
+
val
|
15
|
+
end
|
16
|
+
|
17
|
+
def teardown_fixtures_with_preloaded_factory_data
|
18
|
+
FactoryData.reset_cache!
|
19
|
+
teardown_fixtures_without_preloaded_factory_data
|
20
|
+
end
|
21
|
+
|
22
|
+
alias_method_chain :load_fixtures, :preloaded_factory_data
|
23
|
+
alias_method_chain :teardown_fixtures, :preloaded_factory_data
|
24
|
+
end
|
25
|
+
|
26
|
+
class Fixtures
|
27
|
+
def delete_existing_fixtures_with_preloaded_factory_data
|
28
|
+
delete_existing_fixtures_without_preloaded_factory_data
|
29
|
+
FactoryData.delete_preload_data!
|
30
|
+
end
|
31
|
+
|
32
|
+
alias_method_chain :delete_existing_fixtures, :preloaded_factory_data
|
33
|
+
end
|
34
|
+
|
35
|
+
class ActiveSupport::TestCase
|
36
|
+
def self.preload_factory_data(*types)
|
37
|
+
types.each { |t| FactoryDataPreloader.preload_types << t }
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class FactoryDataTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
FactoryDataPreloader.reset!
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'Calling FactoryData.preload(:users)' do
|
9
|
+
setup do
|
10
|
+
FactoryData.preload(:users) do |data|
|
11
|
+
data.add(:thom) { User.create(:first_name => 'Thom', :last_name => 'York') }
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
should_not_change 'User.count'
|
16
|
+
should_change "FactoryData.methods.include?('users')", :from => false, :to => true
|
17
|
+
|
18
|
+
context 'when there was a previous user record in the database' do
|
19
|
+
setup { User.create(:first_name => 'Barack', :last_name => 'Obama') }
|
20
|
+
|
21
|
+
context 'and calling FactoryData.delete_preload_data!' do
|
22
|
+
setup { FactoryData.delete_preload_data! }
|
23
|
+
should_change 'User.count', :to => 0
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'and later calling FactoryData.preload_data!' do
|
28
|
+
setup do
|
29
|
+
@out, @err = OutputCapturer.capture do
|
30
|
+
FactoryData.preload_data!
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
should_change 'User.count', :by => 1
|
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
|
+
|
57
|
+
context 'and later calling FactoryData.users(key)' do
|
58
|
+
setup { @user = FactoryData.users(:thom) }
|
59
|
+
|
60
|
+
should 'retrieve the correct user' do
|
61
|
+
assert_equal 'Thom', @user.first_name
|
62
|
+
assert_equal 'York', @user.last_name
|
63
|
+
assert !@user.new_record?
|
64
|
+
end
|
65
|
+
|
66
|
+
should 'raise the appropriate error for a non-existant key' do
|
67
|
+
assert_raise FactoryDataPreloader::PreloadedRecordNotFound do
|
68
|
+
FactoryData.users(:not_a_user)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
should 'cache the record so as not to use User.find more than necessary' do
|
73
|
+
User.expects(:find).never
|
74
|
+
user2 = FactoryData.users(:thom)
|
75
|
+
assert_equal @user.object_id, user2.object_id
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'and later calling FactoryData.reset_cache!' do
|
79
|
+
setup { FactoryData.reset_cache! }
|
80
|
+
|
81
|
+
should 'reload the record from the database the next time FactoryData.users(key) is called' do
|
82
|
+
User.expects(:find).once.returns(@user)
|
83
|
+
FactoryData.users(:thom)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
context 'Preloading with an explicit :model_class option' do
|
91
|
+
setup do
|
92
|
+
FactoryData.preload(:posts, :model_class => User) do |data|
|
93
|
+
data.add(:george) { User.create(:first_name => 'George', :last_name => 'Washington') }
|
94
|
+
end
|
95
|
+
@out, @err = OutputCapturer.capture do
|
96
|
+
FactoryData.preload_data!
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
should 'use the passed model_class rather than inferring the class from the symbol' do
|
101
|
+
assert_equal User, FactoryData.posts(:george).class
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'Preloading multiple record types, with dependencies' do
|
106
|
+
setup do
|
107
|
+
FactoryData.preload(:comments, :depends_on => [:users, :posts]) do |data|
|
108
|
+
data.add(:woohoo) { FactoryData.users(:john).comments.create(:post => FactoryData.posts(:tour), :comment => "I can't wait!") }
|
109
|
+
end
|
110
|
+
|
111
|
+
FactoryData.preload(:posts, :depends_on => :users) do |data|
|
112
|
+
data.add(:tour) { FactoryData.users(:thom).posts.create(:title => 'Tour!', :body => 'Radiohead will tour soon.') }
|
113
|
+
end
|
114
|
+
|
115
|
+
FactoryData.preload(:users) do |data|
|
116
|
+
data.add(:thom) { User.create(:first_name => 'Thom', :last_name => 'York') }
|
117
|
+
data.add(:john) { User.create(:first_name => 'John', :last_name => 'Doe') }
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
should "raise the appropriate error when a developer tries to access a record that wasn't preloaded" do
|
122
|
+
FactoryDataPreloader.preload_all = false
|
123
|
+
FactoryDataPreloader.preload_types << :users
|
124
|
+
|
125
|
+
@out, @err = OutputCapturer.capture do
|
126
|
+
FactoryData.preload_data!
|
127
|
+
end
|
128
|
+
|
129
|
+
assert FactoryData.users(:thom)
|
130
|
+
assert_raise FactoryDataPreloader::DefinedPreloaderNotRunError do
|
131
|
+
FactoryData.posts(:tour)
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
should 'preload them in the proper order, allowing you to use the dependencies' do
|
136
|
+
@out, @err = OutputCapturer.capture do
|
137
|
+
FactoryData.preload_data!
|
138
|
+
end
|
139
|
+
|
140
|
+
assert_equal 'Thom', FactoryData.users(:thom).first_name
|
141
|
+
assert_equal 'John', FactoryData.users(:john).first_name
|
142
|
+
|
143
|
+
assert_equal FactoryData.users(:thom), FactoryData.posts(:tour).user
|
144
|
+
|
145
|
+
assert_equal FactoryData.users(:john), FactoryData.comments(:woohoo).user
|
146
|
+
assert_equal FactoryData.posts(:tour), FactoryData.comments(:woohoo).post
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
data/test/lib/models.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
class EmailAddress < ActiveRecord::Base
|
2
|
+
end
|
3
|
+
|
4
|
+
class User < ActiveRecord::Base
|
5
|
+
has_many :posts
|
6
|
+
has_many :comments
|
7
|
+
validates_presence_of :last_name
|
8
|
+
end
|
9
|
+
|
10
|
+
class Post < ActiveRecord::Base
|
11
|
+
belongs_to :user
|
12
|
+
has_many :comments
|
13
|
+
has_many :post_images
|
14
|
+
end
|
15
|
+
|
16
|
+
class Comment < ActiveRecord::Base
|
17
|
+
belongs_to :post
|
18
|
+
belongs_to :user
|
19
|
+
end
|
20
|
+
|
21
|
+
class PostImage < ActiveRecord::Base
|
22
|
+
belongs_to :post
|
23
|
+
has_many :post_image_ratings
|
24
|
+
end
|
25
|
+
|
26
|
+
class PostImageRating < ActiveRecord::Base
|
27
|
+
belongs_to :post_image
|
28
|
+
end
|
29
|
+
|
30
|
+
class IpAddress < ActiveRecord::Base
|
31
|
+
end
|
data/test/lib/schema.rb
ADDED
@@ -0,0 +1,54 @@
|
|
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
|
+
|
8
|
+
ActiveRecord::Schema.define do
|
9
|
+
create_table :users, :force => true do |t|
|
10
|
+
t.string :first_name
|
11
|
+
t.string :last_name
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
ActiveRecord::Schema.define do
|
17
|
+
create_table :posts, :force => true do |t|
|
18
|
+
t.references :user
|
19
|
+
t.string :title
|
20
|
+
t.string :body
|
21
|
+
t.timestamps
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
ActiveRecord::Schema.define do
|
26
|
+
create_table :comments, :force => true do |t|
|
27
|
+
t.references :user
|
28
|
+
t.references :post
|
29
|
+
t.string :comment
|
30
|
+
t.timestamps
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
ActiveRecord::Schema.define do
|
35
|
+
create_table :post_images, :force => true do |t|
|
36
|
+
t.references :post
|
37
|
+
t.timestamps
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
ActiveRecord::Schema.define do
|
42
|
+
create_table :post_image_ratings, :force => true do |t|
|
43
|
+
t.references :post_image
|
44
|
+
t.timestamps
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
ActiveRecord::Schema.define do
|
49
|
+
create_table :ip_addresses, :force => true do |t|
|
50
|
+
t.string :ip_address
|
51
|
+
t.timestamps
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -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
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/test_helper'
|
2
|
+
|
3
|
+
class PreloaderTest < Test::Unit::TestCase
|
4
|
+
def setup
|
5
|
+
FactoryDataPreloader.reset!
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'A new preloader' do
|
9
|
+
setup do
|
10
|
+
proc = lambda { |data|
|
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
|
+
}
|
14
|
+
@preloader = FactoryDataPreloader::Preloader.new(:users, User, proc, [])
|
15
|
+
end
|
16
|
+
|
17
|
+
should 'be automatically added to the PreloaderCollection' do
|
18
|
+
assert_equal [@preloader], FactoryDataPreloader::AllPreloaders.instance
|
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_change 'User.count', :by => 2
|
29
|
+
|
30
|
+
should 'return the preloaded data when #get_record is called' do
|
31
|
+
assert_equal 'York', @preloader.get_record(:thom).last_name
|
32
|
+
assert_equal 'Doe', @preloader.get_record(:john).last_name
|
33
|
+
end
|
34
|
+
|
35
|
+
should 'print out a preloader message, a dot for each record and a benchmark' do
|
36
|
+
assert_equal '', @err
|
37
|
+
assert_match /Preloading users:\.\.\([\d\.]+ secs\)/, @out
|
38
|
+
end
|
39
|
+
|
40
|
+
context 'when preloaded again' do
|
41
|
+
setup do
|
42
|
+
@out, @err = OutputCapturer.capture do
|
43
|
+
@preloader.preload!
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
should 'print nothing' do
|
48
|
+
assert_equal '', @err
|
49
|
+
assert_equal '', @out
|
50
|
+
end
|
51
|
+
|
52
|
+
should_not_change 'User.count'
|
53
|
+
end
|
54
|
+
|
55
|
+
should 'issue a delete statement if #delete_table_data! is called' do
|
56
|
+
User.expects(:delete_all).once
|
57
|
+
@preloader.delete_table_data!
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'when #delete_table_data! is called' do
|
61
|
+
setup do
|
62
|
+
@preloader.delete_table_data!
|
63
|
+
end
|
64
|
+
|
65
|
+
should 'not issue another delete statement if #delete_table_data! is later called on the same preloader' do
|
66
|
+
User.expects(:delete_all).never
|
67
|
+
@preloader.delete_table_data!
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context 'A new preloader for email_addresses' do
|
74
|
+
setup do
|
75
|
+
@preloader = FactoryDataPreloader::Preloader.new(:email_addresses, nil, lambda { }, [])
|
76
|
+
end
|
77
|
+
|
78
|
+
should 'infer the model class' do
|
79
|
+
assert_equal EmailAddress, @preloader.model_class
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'A preloader with errors' do
|
84
|
+
setup do
|
85
|
+
proc = lambda { |data|
|
86
|
+
data.add(:thom) { raise StandardError('Error for thom') }
|
87
|
+
data.add(:john) { @john = User.create(:first_name => 'John', :last_name => 'Doe') }
|
88
|
+
}
|
89
|
+
@preloader = FactoryDataPreloader::Preloader.new(:users, User, proc, [])
|
90
|
+
@out, @err = OutputCapturer.capture do
|
91
|
+
@preloader.preload!
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
should 'raise an exception when the record with the error is accessed' do
|
96
|
+
assert_raise FactoryDataPreloader::ErrorWhilePreloadingRecord do
|
97
|
+
@preloader.get_record(:thom)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
should 'allow the error-free records to be accessed, even when they were created after the error record' do
|
102
|
+
assert_equal @john, @preloader.get_record(:john)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context 'A preloader with dependencies' do
|
107
|
+
setup do
|
108
|
+
@comments = FactoryDataPreloader::Preloader.new(:comments, Comment, nil, [:users, :posts])
|
109
|
+
end
|
110
|
+
|
111
|
+
should 'raise PreloaderNotDefinedError for #dependencies if the preloader it depends on are not defined' do
|
112
|
+
assert_raise FactoryDataPreloader::PreloaderNotDefinedError do
|
113
|
+
@comments.dependencies
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
context 'when the dependency preloaders have also been defined' do
|
118
|
+
setup do
|
119
|
+
@posts = FactoryDataPreloader::Preloader.new(:posts, Post, nil, [:users])
|
120
|
+
@users = FactoryDataPreloader::Preloader.new(:users, User, nil, [])
|
121
|
+
end
|
122
|
+
|
123
|
+
should 'return the preloader objects for #dependencies' do
|
124
|
+
assert_equal [@users, @posts], @comments.dependencies
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
context 'A series of preloaders, with dependencies,' do
|
130
|
+
setup do
|
131
|
+
@post_image_ratings = FactoryDataPreloader::Preloader.new(:post_image_ratings, PostImageRating, nil, [:post_images])
|
132
|
+
@post_images = FactoryDataPreloader::Preloader.new(:post_images, PostImage, nil, [:posts])
|
133
|
+
@ip_addresses = FactoryDataPreloader::Preloader.new(:ip_addresses, IpAddress, nil, [])
|
134
|
+
@posts = FactoryDataPreloader::Preloader.new(:posts, Post, nil, [:users])
|
135
|
+
@users = FactoryDataPreloader::Preloader.new(:users, User, nil, [])
|
136
|
+
end
|
137
|
+
|
138
|
+
should 'sort correctly for PreloaderCollection.instance.dependency_order' do
|
139
|
+
expected = [@ip_addresses, @users, @posts, @post_images, @post_image_ratings]
|
140
|
+
assert_equal expected.map(&:model_type), FactoryDataPreloader::AllPreloaders.instance.dependency_order.map(&:model_type)
|
141
|
+
end
|
142
|
+
|
143
|
+
should 'return the correct preloader objects for #all_dependencies' do
|
144
|
+
assert_same_elements [@post_images, @posts, @users], @post_image_ratings.all_dependencies
|
145
|
+
assert_same_elements [@posts, @users], @post_images.all_dependencies
|
146
|
+
assert_same_elements [], @ip_addresses.all_dependencies
|
147
|
+
assert_same_elements [@users], @posts.all_dependencies
|
148
|
+
assert_same_elements [], @users.all_dependencies
|
149
|
+
end
|
150
|
+
|
151
|
+
context 'when FactoryDataPreloader.preload_all = true' do
|
152
|
+
setup do
|
153
|
+
FactoryDataPreloader.preload_all = true
|
154
|
+
end
|
155
|
+
|
156
|
+
should 'return all preloaders for FactoryDataPreloader.requested_preloaders' do
|
157
|
+
expected = [@ip_addresses, @users, @posts, @post_images, @post_image_ratings]
|
158
|
+
assert_equal expected.map(&:model_type), FactoryDataPreloader.requested_preloaders.dependency_order.map(&:model_type)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context 'when FactoryDataPreloader.preload_all = false' do
|
163
|
+
setup do
|
164
|
+
FactoryDataPreloader.preload_all = false
|
165
|
+
end
|
166
|
+
|
167
|
+
should 'return no preloaders when for FactoryDataPreloader.requested_preloaders when preload_types is empty' do
|
168
|
+
assert_equal [], FactoryDataPreloader.preload_types
|
169
|
+
assert_equal [], FactoryDataPreloader.requested_preloaders
|
170
|
+
end
|
171
|
+
|
172
|
+
should 'return just the requested preloaders for FactoryDataPreloader.requested_preloaders' do
|
173
|
+
FactoryDataPreloader.preload_types << :post_images
|
174
|
+
FactoryDataPreloader.preload_types << :ip_addresses
|
175
|
+
expected = [@ip_addresses, @users, @posts, @post_images]
|
176
|
+
assert_equal expected.map(&:model_type), FactoryDataPreloader.requested_preloaders.dependency_order.map(&:model_type)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'shoulda'
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'ruby-debug'
|
7
|
+
Debugger.start
|
8
|
+
Debugger.settings[:autoeval] = true if Debugger.respond_to?(:settings)
|
9
|
+
rescue LoadError
|
10
|
+
# ruby-debug wasn't available so neither can the debugging be
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
15
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
16
|
+
|
17
|
+
require 'factory_data_preloader'
|
18
|
+
|
19
|
+
ActiveRecord::Base.establish_connection({ :database => ":memory:", :adapter => 'sqlite3', :timeout => 500 })
|
20
|
+
|
21
|
+
module OutputCapturer
|
22
|
+
# borrowed from zentest assertions...
|
23
|
+
def self.capture
|
24
|
+
require 'stringio'
|
25
|
+
orig_stdout = $stdout.dup
|
26
|
+
orig_stderr = $stderr.dup
|
27
|
+
captured_stdout = StringIO.new
|
28
|
+
captured_stderr = StringIO.new
|
29
|
+
$stdout = captured_stdout
|
30
|
+
$stderr = captured_stderr
|
31
|
+
yield
|
32
|
+
captured_stdout.rewind
|
33
|
+
captured_stderr.rewind
|
34
|
+
return captured_stdout.string, captured_stderr.string
|
35
|
+
ensure
|
36
|
+
$stdout = orig_stdout
|
37
|
+
$stderr = orig_stderr
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
module FactoryDataPreloader
|
42
|
+
def self.reset!
|
43
|
+
self.preload_all = true
|
44
|
+
self.preload_types = []
|
45
|
+
|
46
|
+
preloaders = Array.new(FactoryDataPreloader::AllPreloaders.instance)
|
47
|
+
preloaders.each do |preloader|
|
48
|
+
preloader.remove!
|
49
|
+
end
|
50
|
+
|
51
|
+
FactoryData.reset_cache!
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
require 'lib/schema'
|
56
|
+
require 'lib/models'
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: factory_data_preloader
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.5.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Myron Marston
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-09 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: Shoulda
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "0"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: mocha
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
description:
|
36
|
+
email: myron.marston@gmail.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README.rdoc
|
43
|
+
- LICENSE
|
44
|
+
files:
|
45
|
+
- CHANGELOG.rdoc
|
46
|
+
- README.rdoc
|
47
|
+
- VERSION.yml
|
48
|
+
- lib/factory_data_preloader/core_ext.rb
|
49
|
+
- lib/factory_data_preloader/factory_data.rb
|
50
|
+
- lib/factory_data_preloader/preloaded_data_hash.rb
|
51
|
+
- lib/factory_data_preloader/preloader.rb
|
52
|
+
- lib/factory_data_preloader/preloader_collection.rb
|
53
|
+
- lib/factory_data_preloader/rails_core_ext.rb
|
54
|
+
- lib/factory_data_preloader.rb
|
55
|
+
- test/factory_data_test.rb
|
56
|
+
- test/lib/models.rb
|
57
|
+
- test/lib/schema.rb
|
58
|
+
- test/preloaded_data_hash_test.rb
|
59
|
+
- test/preloader_test.rb
|
60
|
+
- test/test_helper.rb
|
61
|
+
- LICENSE
|
62
|
+
has_rdoc: true
|
63
|
+
homepage: http://github.com/myronmarston/factory_data_preloader
|
64
|
+
licenses: []
|
65
|
+
|
66
|
+
post_install_message:
|
67
|
+
rdoc_options:
|
68
|
+
- --inline-source
|
69
|
+
- --charset=UTF-8
|
70
|
+
require_paths:
|
71
|
+
- lib
|
72
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: "0"
|
77
|
+
version:
|
78
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: "0"
|
83
|
+
version:
|
84
|
+
requirements: []
|
85
|
+
|
86
|
+
rubyforge_project:
|
87
|
+
rubygems_version: 1.3.5
|
88
|
+
signing_key:
|
89
|
+
specification_version: 2
|
90
|
+
summary: A library for preloading test data in rails applications.
|
91
|
+
test_files: []
|
92
|
+
|