pseudo_cleaner 0.0.25
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.
- checksums.yaml +15 -0
- data/.gitignore +18 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +22 -0
- data/README.md +142 -0
- data/Rakefile +1 -0
- data/lib/pseudo_cleaner.rb +12 -0
- data/lib/pseudo_cleaner/configuration.rb +44 -0
- data/lib/pseudo_cleaner/cucumber.rb +44 -0
- data/lib/pseudo_cleaner/logger.rb +9 -0
- data/lib/pseudo_cleaner/master_cleaner.rb +380 -0
- data/lib/pseudo_cleaner/rspec.rb +45 -0
- data/lib/pseudo_cleaner/spinach.rb +38 -0
- data/lib/pseudo_cleaner/table_cleaner.rb +557 -0
- data/lib/pseudo_cleaner/version.rb +3 -0
- data/pseudo_cleaner.gemspec +26 -0
- metadata +116 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
N2YxYjQzYTAzZDJkN2M0YzQ0YjE1ODgwMjA3Y2ZlYWNiNTQzNTk4NQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZGI3ODU3NGEyOTE5NGUxODBhNjhlMjk2MTM3ODA5OGZjZDA3NzU4ZQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
MDUxNTk3ZDNiM2RlZDJmYzM5NGRlZjhhMzQ5Yjg1YWY4OTE2MmQ5ZWY1ZWVj
|
10
|
+
MTM2MGM0MzQ3MTNkYWIxNDQyYjUxZGQ1OTVkYTg5YjJmNTQ1ZTk2MmExNmNj
|
11
|
+
YzY1MWE5ZjdkMWY3YmMxNjQ1ODgxYmQxNzQ0ZGM4OWIyYzMzZmM=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NzBlNGYwNmU4ZTA0MDM4MzA3YTRjYjI4NzY4YTJlZTk1YjJhMTM2ZjA3YjUw
|
14
|
+
MjlmMDUyNzZlNzI2NmE4ZGUyNDQ5ODBlNjM5NDEyNGQwMzMwYzc2ODk1Zjk5
|
15
|
+
NjRhY2VhNTdlM2RmNWNmYjQ2MGZhOGI1NmIyY2EwMWVlMmMwMmM=
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 RealNobody
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
# PseudoCleaner
|
2
|
+
|
3
|
+
The [Database Cleaner gem](https://github.com/DatabaseCleaner/database_cleaner) is a wonderful tool,
|
4
|
+
and I've used it for years. I would highly recommend it. It is a well written and a well used and therefore tested
|
5
|
+
tool that you can rely on.
|
6
|
+
|
7
|
+
However, it is (quite rightly) a very conservative tool. I often run into situations where it doesn't quite fit my
|
8
|
+
needs. there are often times when I cannot use transactions (such as when I am doing Cucumber tests with Capybara to
|
9
|
+
feature test my site), but when truncating my tables just isn't reasonable or practical.
|
10
|
+
|
11
|
+
So, I came up with a compromise that works for a large number of tables and databases that I've worked with. The
|
12
|
+
thing is that this solution is not like DatabaseCleaner in that it isn't conservative,
|
13
|
+
and it doesn't guarantee much. This solution might not clean the database entirely between calls.
|
14
|
+
|
15
|
+
The thing is, the database doesn't have to be entirely clean after every call for most tests,
|
16
|
+
just clean enough is often good enough.
|
17
|
+
|
18
|
+
So, what is it that the PseudoCleaner does and why is it good enough?
|
19
|
+
|
20
|
+
The cleaner relies on the fact that most databases use 2 common defaults in most tables (well,
|
21
|
+
most tables that simple tests that rely on the workings of a cleaner anyway...) Those features are an auto-increment
|
22
|
+
`id` column, and/or a `created_at`/`updated_at` columns.
|
23
|
+
|
24
|
+
Using these, when a test starts the cleaner iterates through the tables and saves the current `MAX(id)`,
|
25
|
+
`MAX(created_at)`, and `MAX(updated_at)` values for a table. When a test ends, the cleaner iterates through the
|
26
|
+
tables again and deletes anything that is new. It will then report (optionally) on any records that have been
|
27
|
+
updated but haven't been cleaned up. In future versions, I have plans for it to also report (optionally) on any
|
28
|
+
referential integrity holes.
|
29
|
+
|
30
|
+
Because the PsuedoCleaner already uses the [SortedSeeder gem](https://github.com/RealNobody/sorted_seeder) to determine what
|
31
|
+
order to delete records in when cleaning up, when the database is truncated, it will re-seed the database using the
|
32
|
+
SortedSeeders seed_all function.
|
33
|
+
|
34
|
+
## Installation
|
35
|
+
|
36
|
+
Add this line to your application's Gemfile in the test group:
|
37
|
+
|
38
|
+
gem 'pseudo_cleaner'
|
39
|
+
|
40
|
+
OR
|
41
|
+
|
42
|
+
gem 'pseudo_cleaner', '~> 0.0.1', :git => "git@github.com/RealNobody/pseudo_cleaner.git"
|
43
|
+
|
44
|
+
And then execute:
|
45
|
+
|
46
|
+
$ bundle
|
47
|
+
|
48
|
+
Or install it yourself as:
|
49
|
+
|
50
|
+
$ gem install pseudo_cleaner
|
51
|
+
|
52
|
+
## Usage
|
53
|
+
|
54
|
+
There are multiple ways to use the PseudoCleaner. The main intended usages are detailed here:
|
55
|
+
|
56
|
+
### Rspec
|
57
|
+
|
58
|
+
Rspect integration is built in to make using the PseudoCleaner simple and straight-forward.
|
59
|
+
To integrate the PseudoCleaner with Rspec, simply add the following lines to `spec_helper.rb`:
|
60
|
+
|
61
|
+
require 'pseudo_cleaner'
|
62
|
+
require 'pseudo_cleaner/rspec'
|
63
|
+
|
64
|
+
All tests will now by default use DatabaseCleaner with the `:transaction` strategy. For most tests, this will wrap
|
65
|
+
the test in a transaction, and roll back the transaction at the end of the test.
|
66
|
+
|
67
|
+
If a test is a feature test which uses Capybara using the `:js` tag, that test will be switched to not use
|
68
|
+
DatabaseCleaner. Instead, the test will use the `:pseudo_delete` strategy which as described will store the state of
|
69
|
+
the tables before the test then delete any new records at the end of the test.
|
70
|
+
|
71
|
+
If you want or need a specific strategy for a single test, you can specify the metadata tag: `:strategy` in the test
|
72
|
+
to change the behavior of the test. This tag accepts the following values:
|
73
|
+
|
74
|
+
* :none - Do not use any cleaning on this test run.
|
75
|
+
* :psedu_delete - Do not use DatabaseCleaner and clean tables individually.
|
76
|
+
* :truncation - Use the :truncation strategy with DatabaseCleaner and re-seed the database after truncation.
|
77
|
+
* :deletion - Use the :deletion strategy with DatabaseCleaner and re-seed the database after deletion.
|
78
|
+
* :transaction - Use the :transaction strategy with DatabaseCleaner.
|
79
|
+
|
80
|
+
Example:
|
81
|
+
|
82
|
+
it "is a test", strategy: :truncation do
|
83
|
+
expect(something).to work
|
84
|
+
end
|
85
|
+
|
86
|
+
### Cucumber
|
87
|
+
|
88
|
+
Cucumber integration similar to Rspec integration is planned, but not implemented yet.
|
89
|
+
|
90
|
+
### Manual
|
91
|
+
|
92
|
+
There are two ways to use the cleaner manually. When you use the cleaner manually, you are only using the
|
93
|
+
PseduoCleaner. You do not get DatabaseCleaner integration like you get automatically with Rspec. This will create
|
94
|
+
table cleaners and any custom defined cleaners and execute them.
|
95
|
+
|
96
|
+
NOTE: When using the tool manually, if the strategy is any strategy other than `:pseudo_delete`, the default
|
97
|
+
cleaners will not do anything. The strategy may still be useful though if you have any custom cleaners.
|
98
|
+
|
99
|
+
*PseudoCleaner::MasterCleaner.clean* This function cleans the code executed inside a block and takes two parameters.
|
100
|
+
The first parameter takes the values `:test` or `:suite`. This is used to determine if the cleaner is wrapped around
|
101
|
+
a set of tests or a single test. The default implementations provided do not distinguish between these, but custom
|
102
|
+
cleaners might. The second parameter is the strategy to use.
|
103
|
+
|
104
|
+
PseudoCleaner::MasterCleaner.clean(:test, :pseudo_delete) do
|
105
|
+
# Your code here
|
106
|
+
end
|
107
|
+
|
108
|
+
*PseudoCleaner::MasterCleaner.start_test* This takes one parameter that is the type of the cleaner this is (`:test` or
|
109
|
+
`:suite`). This creates a cleaner object that can be started and ended around the code to be cleaned. You specify
|
110
|
+
the strategy for the tests when you start the cleaner.
|
111
|
+
|
112
|
+
pseudo_cleaner = PseudoCleaner::MasterCleaner.start_test :pseudo_delete
|
113
|
+
# Your code here
|
114
|
+
pseudo_cleaner.end
|
115
|
+
|
116
|
+
## Custom Cleaners
|
117
|
+
|
118
|
+
This system is built to clean the database after calls.
|
119
|
+
|
120
|
+
If you have additional actions which need to be done either before and/or after tests run to clean up resources, you
|
121
|
+
can create custom cleaners and place them in the `db/cleaners` folder. You can also create a custom cleaner for a
|
122
|
+
specific table by placing the cleaner in the same folder and naming it `<TableName>Cleaner`.
|
123
|
+
|
124
|
+
Cleaners must be instantiated, and will be initialized with the following prototype:
|
125
|
+
`initialize(start_method, end_method, table, options = {})`
|
126
|
+
|
127
|
+
Cleaners must also include one or more of the following funcitons:
|
128
|
+
|
129
|
+
* test_start test_strategy
|
130
|
+
* test_end test_strategy
|
131
|
+
* suite_start test_strategy
|
132
|
+
* suite_end test_strategy
|
133
|
+
|
134
|
+
A Cleaner can adjust when it is called in relation to other cleaners by overriding the instance method `<=>`
|
135
|
+
|
136
|
+
## Contributing
|
137
|
+
|
138
|
+
1. Fork it
|
139
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
140
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
141
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
142
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "sorted_seeder"
|
2
|
+
require "colorize"
|
3
|
+
require "database_cleaner"
|
4
|
+
require "pseudo_cleaner/version"
|
5
|
+
require "pseudo_cleaner/configuration"
|
6
|
+
require "pseudo_cleaner/table_cleaner"
|
7
|
+
require "pseudo_cleaner/master_cleaner"
|
8
|
+
require "pseudo_cleaner/logger"
|
9
|
+
|
10
|
+
module PseudoCleaner
|
11
|
+
# Your code goes here...
|
12
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module PseudoCleaner
|
2
|
+
class Configuration
|
3
|
+
@@current_instance = nil
|
4
|
+
|
5
|
+
# A simple configuration class for the PseudoCleaner
|
6
|
+
#
|
7
|
+
# Configurations:
|
8
|
+
# output_diagnostics - true/false
|
9
|
+
# if true, the system will use puts to output information about what it is doing...
|
10
|
+
attr_accessor :output_diagnostics
|
11
|
+
attr_accessor :clean_database_before_tests
|
12
|
+
attr_accessor :reset_auto_increment
|
13
|
+
attr_accessor :single_cleaner_set
|
14
|
+
attr_accessor :post_transaction_analysis
|
15
|
+
|
16
|
+
def self.current_instance
|
17
|
+
@@current_instance ||= PseudoCleaner::Configuration.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def initialize
|
21
|
+
@output_diagnostics = false # false to keep the noise level down...
|
22
|
+
@clean_database_before_tests = false # false because I think it will annoy developers...
|
23
|
+
@reset_auto_increment = true # true because I think it should be done
|
24
|
+
@single_cleaner_set = true # true because I hope it will improve performance
|
25
|
+
@post_transaction_analysis = false # should only be set true if you are searching for a problem
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.db_connection=(connection)
|
29
|
+
@db_connection = connection
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.db_connection(type)
|
33
|
+
if @db_connection || type.nil?
|
34
|
+
@db_connection
|
35
|
+
else
|
36
|
+
if type == :sequel
|
37
|
+
Sequel::DATABASES[0]
|
38
|
+
else
|
39
|
+
ActiveRecord::Base
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
first_test_run = false
|
2
|
+
|
3
|
+
# turn off Cucumber's default usage of DatabaseCleaner
|
4
|
+
Cucumber::Rails::Database.autorun_database_cleaner = false
|
5
|
+
|
6
|
+
Before do |scenario|
|
7
|
+
unless first_test_run
|
8
|
+
first_test_run = true
|
9
|
+
# before tests run...
|
10
|
+
# We start suite in case a custom cleaner wants/needs to.
|
11
|
+
if PseudoCleaner::Configuration.current_instance.clean_database_before_tests
|
12
|
+
PseudoCleaner::MasterCleaner.reset_database
|
13
|
+
else
|
14
|
+
PseudoCleaner::MasterCleaner.start_suite
|
15
|
+
end
|
16
|
+
|
17
|
+
DatabaseCleaner.strategy = :transaction
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Before("~@truncation", "~@deletion") do |scenario|
|
22
|
+
PseudoCleaner::MasterCleaner.start_example(scenario, :pseudo_delete)
|
23
|
+
end
|
24
|
+
|
25
|
+
Before("@truncation") do |scenario|
|
26
|
+
PseudoCleaner::MasterCleaner.start_example(scenario, :truncation)
|
27
|
+
end
|
28
|
+
|
29
|
+
Before("@deletion", "~@truncation") do |scenario|
|
30
|
+
PseudoCleaner::MasterCleaner.start_example(scenario, :deletion)
|
31
|
+
end
|
32
|
+
|
33
|
+
Before("@none") do |scenario|
|
34
|
+
PseudoCleaner::MasterCleaner.start_example(scenario, :none)
|
35
|
+
end
|
36
|
+
|
37
|
+
After do |scenario|
|
38
|
+
PseudoCleaner::MasterCleaner.end_example(scenario)
|
39
|
+
end
|
40
|
+
|
41
|
+
at_exit do
|
42
|
+
# We end suite in case a custom cleaner wants/needs to.
|
43
|
+
PseudoCleaner::MasterCleaner.end_suite
|
44
|
+
end
|
@@ -0,0 +1,380 @@
|
|
1
|
+
require "sorted_seeder"
|
2
|
+
|
3
|
+
module PseudoCleaner
|
4
|
+
class MasterCleaner
|
5
|
+
@@suite_cleaner = nil
|
6
|
+
@@cleaner_classes = nil
|
7
|
+
@@cleaner_classes_sorted = false
|
8
|
+
|
9
|
+
CLEANING_STRATEGIES = [:transaction, :truncation, :deletion, :pseudo_delete, :none]
|
10
|
+
DB_CLEANER_CLEANING_STRATEGIES =
|
11
|
+
{
|
12
|
+
transaction: :transaction,
|
13
|
+
truncation: :truncation,
|
14
|
+
deletion: :deletion,
|
15
|
+
pseudo_delete: :transaction
|
16
|
+
}
|
17
|
+
VALID_TEST_TYPES = [:suite, :test]
|
18
|
+
|
19
|
+
VALID_START_METHODS = [:test_start, :suite_start]
|
20
|
+
VALID_END_METHODS = [:test_end, :suite_end]
|
21
|
+
VALID_TEST_METHODS = VALID_START_METHODS + VALID_END_METHODS
|
22
|
+
|
23
|
+
class << self
|
24
|
+
def start_suite
|
25
|
+
if @@suite_cleaner
|
26
|
+
@@suite_cleaner.reset_suite
|
27
|
+
end
|
28
|
+
@@suite_cleaner = PseudoCleaner::MasterCleaner.new(:suite)
|
29
|
+
@@suite_cleaner.start :pseudo_delete
|
30
|
+
@@suite_cleaner
|
31
|
+
end
|
32
|
+
|
33
|
+
def start_example(example_class, strategy)
|
34
|
+
pseudo_cleaner_data = {}
|
35
|
+
pseudo_cleaner_data[:test_strategy] = strategy
|
36
|
+
|
37
|
+
unless strategy == :none
|
38
|
+
raise "invalid strategy" unless PseudoCleaner::MasterCleaner::DB_CLEANER_CLEANING_STRATEGIES.has_key? strategy
|
39
|
+
|
40
|
+
DatabaseCleaner.strategy = PseudoCleaner::MasterCleaner::DB_CLEANER_CLEANING_STRATEGIES[strategy]
|
41
|
+
unless [:pseudo_delete].include? strategy
|
42
|
+
if Object.const_defined?("ActiveRecord", false) && ActiveRecord.const_defined?("Base", false)
|
43
|
+
DatabaseCleaner[:active_record, connection: PseudoCleaner::Configuration.db_connection(:active_record)].
|
44
|
+
start
|
45
|
+
elsif Object.const_defined?("Sequel", false) && Sequel.const_defined?("Model", false)
|
46
|
+
DatabaseCleaner[:sequel, connection: PseudoCleaner::Configuration.db_connection(:sequel)].start
|
47
|
+
end
|
48
|
+
# DatabaseCleaner.start
|
49
|
+
end
|
50
|
+
|
51
|
+
pseudo_cleaner_data[:pseudo_state] = PseudoCleaner::MasterCleaner.start_test strategy
|
52
|
+
end
|
53
|
+
|
54
|
+
example_class.instance_variable_set(:@pseudo_cleaner_data, pseudo_cleaner_data)
|
55
|
+
end
|
56
|
+
|
57
|
+
def end_example(example_class)
|
58
|
+
pseudo_cleaner_data = example_class.instance_variable_get(:@pseudo_cleaner_data)
|
59
|
+
|
60
|
+
unless pseudo_cleaner_data[:test_strategy] == :none
|
61
|
+
unless [:pseudo_delete].include? pseudo_cleaner_data[:test_strategy]
|
62
|
+
if Object.const_defined?("ActiveRecord", false) && ActiveRecord.const_defined?("Base", false)
|
63
|
+
DatabaseCleaner[:active_record, connection: PseudoCleaner::Configuration.db_connection(:active_record)].
|
64
|
+
clean
|
65
|
+
elsif Object.const_defined?("Sequel", false) && Sequel.const_defined?("Model", false)
|
66
|
+
DatabaseCleaner[:sequel, connection: PseudoCleaner::Configuration.db_connection(:sequel)].clean
|
67
|
+
end
|
68
|
+
# DatabaseCleaner.clean
|
69
|
+
end
|
70
|
+
|
71
|
+
case pseudo_cleaner_data[:test_strategy]
|
72
|
+
when :deletion, :truncation
|
73
|
+
PseudoCleaner::MasterCleaner.database_reset
|
74
|
+
end
|
75
|
+
|
76
|
+
pseudo_cleaner_data[:pseudo_state].end test_type: :test, test_strategy: pseudo_cleaner_data[:test_strategy]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def end_suite
|
81
|
+
@@suite_cleaner.end test_strategy: :pseudo_delete if @@suite_cleaner
|
82
|
+
end
|
83
|
+
|
84
|
+
def start_test test_strategy
|
85
|
+
raise "Invalid test_strategy \"#{test_strategy}\"" unless CLEANING_STRATEGIES.include? test_strategy
|
86
|
+
|
87
|
+
cleaner = if PseudoCleaner::Configuration.current_instance.single_cleaner_set
|
88
|
+
@@suite_cleaner
|
89
|
+
else
|
90
|
+
PseudoCleaner::MasterCleaner.new(:test)
|
91
|
+
end
|
92
|
+
|
93
|
+
cleaner.start test_strategy, test_type: :test, test_strategy: test_strategy
|
94
|
+
|
95
|
+
cleaner
|
96
|
+
end
|
97
|
+
|
98
|
+
def clean(test_type, test_strategy, &block)
|
99
|
+
raise "Invalid test_type \"#{test_type}\"" unless [:suite, :test].include? test_type
|
100
|
+
raise "Invalid test_strategy \"#{test_strategy}\"" unless CLEANING_STRATEGIES.include? test_strategy
|
101
|
+
|
102
|
+
master_cleaner = PseudoCleaner::MasterCleaner.send "start_#{test_type}", test_strategy
|
103
|
+
|
104
|
+
body_error = nil
|
105
|
+
begin
|
106
|
+
block.yield master_cleaner
|
107
|
+
rescue => error
|
108
|
+
body_error = error
|
109
|
+
end
|
110
|
+
|
111
|
+
master_cleaner.end test_type: test_type, test_strategy: test_strategy
|
112
|
+
|
113
|
+
raise body_error if body_error
|
114
|
+
end
|
115
|
+
|
116
|
+
def reset_database
|
117
|
+
DatabaseCleaner.clean_with(:truncation)
|
118
|
+
|
119
|
+
PseudoCleaner::MasterCleaner.database_reset
|
120
|
+
end
|
121
|
+
|
122
|
+
def database_reset
|
123
|
+
PseudoCleaner::MasterCleaner.seed_data
|
124
|
+
PseudoCleaner::MasterCleaner.start_suite
|
125
|
+
end
|
126
|
+
|
127
|
+
def seed_data
|
128
|
+
PseudoCleaner::Logger.write("Re-seeding database".red.on_light_white)
|
129
|
+
SortedSeeder::Seeder.seed_all(PseudoCleaner::Configuration.db_connection(nil))
|
130
|
+
end
|
131
|
+
|
132
|
+
def process_exception(error)
|
133
|
+
PseudoCleaner::Logger.write(" An exception has occurred:".red.on_light_white)
|
134
|
+
PseudoCleaner::Logger.write("")
|
135
|
+
PseudoCleaner::Logger.write(error.to_s)
|
136
|
+
PseudoCleaner::Logger.write(error.backtrace.join("\n")) if error.backtrace
|
137
|
+
end
|
138
|
+
|
139
|
+
def cleaner_class(table_name)
|
140
|
+
seed_class_name = "#{table_name.to_s.classify}Cleaner"
|
141
|
+
seed_class_base_name = seed_class_name.demodulize
|
142
|
+
base_module = seed_class_name.split("::")[0..-2].join("::")
|
143
|
+
base_module_classes = [Object]
|
144
|
+
|
145
|
+
unless base_module.blank?
|
146
|
+
base_module_classes = base_module_classes.unshift base_module.constantize
|
147
|
+
end
|
148
|
+
|
149
|
+
return_class = nil
|
150
|
+
2.times do
|
151
|
+
base_module_classes.each do |base_class|
|
152
|
+
if (base_class.const_defined?(seed_class_base_name, false))
|
153
|
+
if base_class == Object
|
154
|
+
return_class = seed_class_base_name.constantize
|
155
|
+
else
|
156
|
+
return_class = "#{base_class.name}::#{seed_class_base_name}".constantize
|
157
|
+
end
|
158
|
+
|
159
|
+
break
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
break if return_class
|
164
|
+
|
165
|
+
seeder_file = "db/cleaners/"
|
166
|
+
seeder_file += base_module.split("::").map { |module_name| module_name.underscore }.join("/")
|
167
|
+
seeder_file += "/" unless seeder_file[-1] == "/"
|
168
|
+
seeder_file += seed_class_base_name.underscore
|
169
|
+
seeder_file += ".rb"
|
170
|
+
seeder_file = File.join(Rails.root, seeder_file)
|
171
|
+
|
172
|
+
break unless File.exists?(seeder_file)
|
173
|
+
|
174
|
+
require seeder_file
|
175
|
+
end
|
176
|
+
|
177
|
+
# unless return_class &&
|
178
|
+
# VALID_TEST_METHODS.any? { |test_method| return_class.instance_methods.include?(test_method.to_sym) }
|
179
|
+
# return_class = table
|
180
|
+
# end
|
181
|
+
|
182
|
+
unless return_class &&
|
183
|
+
VALID_TEST_METHODS.any? { |test_method| return_class.instance_methods.include?(test_method.to_sym) }
|
184
|
+
return_class = PseudoCleaner::TableCleaner
|
185
|
+
end
|
186
|
+
|
187
|
+
return_class
|
188
|
+
end
|
189
|
+
|
190
|
+
def cleaner_classes
|
191
|
+
unless @@cleaner_classes
|
192
|
+
@@cleaner_classes = []
|
193
|
+
|
194
|
+
PseudoCleaner::MasterCleaner.create_table_cleaners
|
195
|
+
PseudoCleaner::MasterCleaner.create_custom_cleaners
|
196
|
+
end
|
197
|
+
|
198
|
+
@@cleaner_classes
|
199
|
+
end
|
200
|
+
|
201
|
+
def create_table_cleaners(options = {})
|
202
|
+
SortedSeeder::Seeder.create_order(PseudoCleaner::Configuration.db_connection(nil)).each do |table|
|
203
|
+
cleaner_class = PseudoCleaner::MasterCleaner.cleaner_class(table.name)
|
204
|
+
if cleaner_class
|
205
|
+
PseudoCleaner::MasterCleaner.cleaner_classes << [table, nil, cleaner_class]
|
206
|
+
end
|
207
|
+
end
|
208
|
+
if SortedSeeder::Seeder.respond_to?(:unclassed_tables)
|
209
|
+
SortedSeeder::Seeder.unclassed_tables(PseudoCleaner::Configuration.db_connection(nil)).each do |table_name|
|
210
|
+
cleaner_class = PseudoCleaner::MasterCleaner.cleaner_class(table_name)
|
211
|
+
if cleaner_class
|
212
|
+
PseudoCleaner::MasterCleaner.cleaner_classes << [nil, table_name, cleaner_class]
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def create_custom_cleaners(options = {})
|
219
|
+
if Object.const_defined?("Rails", false)
|
220
|
+
cleaner_root = Rails.root.join("db/cleaners/").to_s
|
221
|
+
cleaner_files = Dir[Rails.root.join("db/cleaners/**/*.rb")]
|
222
|
+
|
223
|
+
cleaner_files.each do |cleaner_file|
|
224
|
+
class_name = File.basename(cleaner_file, ".rb").classify
|
225
|
+
|
226
|
+
check_class, full_module_name = find_file_class(cleaner_file, cleaner_root)
|
227
|
+
unless check_class && check_class.const_defined?(class_name, false)
|
228
|
+
require cleaner_file
|
229
|
+
check_class, full_module_name = find_file_class(cleaner_file, cleaner_root)
|
230
|
+
end
|
231
|
+
|
232
|
+
if check_class
|
233
|
+
full_module_name << class_name
|
234
|
+
if check_class.const_defined?(class_name, false)
|
235
|
+
check_class = full_module_name.join("::").constantize
|
236
|
+
else
|
237
|
+
check_class = nil
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
if check_class &&
|
242
|
+
PseudoCleaner::MasterCleaner::VALID_TEST_METHODS.
|
243
|
+
any? { |test_method| check_class.instance_methods.include?(test_method) }
|
244
|
+
unless PseudoCleaner::MasterCleaner.cleaner_classes.any? { |cleaner| check_class == cleaner[2] }
|
245
|
+
PseudoCleaner::MasterCleaner.cleaner_classes << [nil, nil, check_class]
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def find_file_class(seeder_file, seeder_root)
|
253
|
+
check_class = Object
|
254
|
+
full_module_name = []
|
255
|
+
|
256
|
+
File.dirname(seeder_file.to_s[seeder_root.length..-1]).split("/").map do |module_element|
|
257
|
+
if (module_element != ".")
|
258
|
+
full_module_name << module_element.classify
|
259
|
+
if check_class.const_defined?(full_module_name[-1], false)
|
260
|
+
check_class = full_module_name.join("::").constantize
|
261
|
+
else
|
262
|
+
check_class = nil
|
263
|
+
break
|
264
|
+
end
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
return check_class, full_module_name
|
269
|
+
end
|
270
|
+
|
271
|
+
def review_rows(&block)
|
272
|
+
@@suite_cleaner.review_rows &block
|
273
|
+
end
|
274
|
+
end
|
275
|
+
|
276
|
+
def initialize(test_type)
|
277
|
+
raise "Invalid test type. Must be one of: #{VALID_TEST_TYPES}" unless VALID_TEST_TYPES.include?(test_type)
|
278
|
+
|
279
|
+
@test_type = test_type
|
280
|
+
end
|
281
|
+
|
282
|
+
def start(test_strategy, options = {})
|
283
|
+
test_type = options[:test_type] || @test_type
|
284
|
+
|
285
|
+
unless @cleaners
|
286
|
+
@cleaners = []
|
287
|
+
@test_strategy = test_strategy
|
288
|
+
|
289
|
+
start_method = "#{test_type}_start".to_sym
|
290
|
+
end_method = "#{test_type}_end".to_sym
|
291
|
+
|
292
|
+
PseudoCleaner::MasterCleaner.cleaner_classes.each do |clean_class|
|
293
|
+
table = clean_class[0]
|
294
|
+
table ||= clean_class[1]
|
295
|
+
|
296
|
+
begin
|
297
|
+
@cleaners << clean_class[2].new(start_method, end_method, table, options)
|
298
|
+
rescue Exception => error
|
299
|
+
puts error.to_s
|
300
|
+
raise error
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
unless @@cleaner_classes_sorted
|
305
|
+
seed_sorts = @cleaners.map { |cleaner| SortedSeeder::Seeder::SeederSorter.new(cleaner) }
|
306
|
+
seed_sorts.sort!
|
307
|
+
|
308
|
+
@cleaners = seed_sorts.map(&:seed_base_object)
|
309
|
+
|
310
|
+
sorted_classes = []
|
311
|
+
@cleaners.each do |cleaner|
|
312
|
+
cleaner_class = PseudoCleaner::MasterCleaner.cleaner_classes.detect do |unsorted_cleaner|
|
313
|
+
if cleaner.class == unsorted_cleaner[2]
|
314
|
+
if unsorted_cleaner[2] == PseudoCleaner::TableCleaner
|
315
|
+
cleaner.table == unsorted_cleaner[0] || cleaner.table == unsorted_cleaner[1]
|
316
|
+
else
|
317
|
+
true
|
318
|
+
end
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
sorted_classes << cleaner_class
|
323
|
+
end
|
324
|
+
|
325
|
+
@@cleaner_classes = sorted_classes
|
326
|
+
@@cleaner_classes_sorted = true
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
start_all_cleaners options
|
331
|
+
end
|
332
|
+
|
333
|
+
def end(options = {})
|
334
|
+
test_type = options[:test_type] || @test_type
|
335
|
+
if PseudoCleaner::Configuration.current_instance.output_diagnostics ||
|
336
|
+
PseudoCleaner::Configuration.current_instance.post_transaction_analysis
|
337
|
+
PseudoCleaner::Logger.write("Cleaning #{test_type}")
|
338
|
+
end
|
339
|
+
end_all_cleaners options
|
340
|
+
end
|
341
|
+
|
342
|
+
def start_all_cleaners(options)
|
343
|
+
test_type = options[:test_type] || @test_type
|
344
|
+
test_strategy = options[:test_strategy] || @test_strategy
|
345
|
+
run_all_cleaners("#{test_type}_start".to_sym, @cleaners, test_strategy)
|
346
|
+
end
|
347
|
+
|
348
|
+
def end_all_cleaners(options)
|
349
|
+
test_type = options[:test_type] || @test_type
|
350
|
+
test_strategy = options[:test_strategy] || @test_strategy
|
351
|
+
run_all_cleaners("#{test_type}_end".to_sym, @cleaners.reverse, test_strategy)
|
352
|
+
end
|
353
|
+
|
354
|
+
def reset_suite
|
355
|
+
run_all_cleaners(:reset_suite, @cleaners.reverse)
|
356
|
+
end
|
357
|
+
|
358
|
+
def run_all_cleaners(cleaner_function, cleaners, *args, &block)
|
359
|
+
last_error = nil
|
360
|
+
|
361
|
+
cleaners.each do |cleaner|
|
362
|
+
begin
|
363
|
+
if cleaner.respond_to?(cleaner_function)
|
364
|
+
cleaner.send(cleaner_function, *args, &block)
|
365
|
+
end
|
366
|
+
rescue => error
|
367
|
+
PseudoCleaner::MasterCleaner.process_exception(last_error) if last_error
|
368
|
+
|
369
|
+
last_error = error
|
370
|
+
end
|
371
|
+
end
|
372
|
+
|
373
|
+
raise last_error if last_error
|
374
|
+
end
|
375
|
+
|
376
|
+
def review_rows(&block)
|
377
|
+
run_all_cleaners(:review_rows, @cleaners, &block)
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|