factory_data_preloader 0.5.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.
- 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
|
+
|