Class: PseudoCleaner::MasterCleaner

Inherits:
Object
  • Object
show all
Defined in:
lib/pseudo_cleaner/master_cleaner.rb

Constant Summary collapse

CLEANING_STRATEGIES =
[:transaction, :truncation, :deletion, :pseudo_delete, :none]
DB_CLEANER_CLEANING_STRATEGIES =
{
    transaction:   :transaction,
    truncation:    :truncation,
    deletion:      :deletion,
    pseudo_delete: :transaction
}
VALID_TEST_TYPES =
[:suite, :test]
VALID_START_METHODS =
[:test_start, :suite_start]
VALID_END_METHODS =
[:test_end, :suite_end]
VALID_TEST_METHODS =
VALID_START_METHODS + VALID_END_METHODS
@@suite_cleaner =
nil
@@cleaner_classes =
nil
@@cleaner_classes_sorted =

@@redis_classes = nil

false
@@report_table =
nil
@@report_error =
false

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(test_type) ⇒ MasterCleaner

Returns a new instance of MasterCleaner.



438
439
440
441
442
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 438

def initialize(test_type)
  raise "Invalid test type.  Must be one of: #{VALID_TEST_TYPES}" unless VALID_TEST_TYPES.include?(test_type)

  @test_type = test_type
end

Class Method Details

.clean(test_type, test_strategy, &block) ⇒ Object



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 179

def clean(test_type, test_strategy, &block)
  raise "Invalid test_type \"#{test_type}\"" unless [:suite, :test].include? test_type
  raise "Invalid test_strategy \"#{test_strategy}\"" unless CLEANING_STRATEGIES.include? test_strategy

  master_cleaner = PseudoCleaner::MasterCleaner.send "start_#{test_type}", test_strategy

  body_error = nil
  begin
    block.yield master_cleaner
  rescue => error
    body_error = error
  end

  master_cleaner.end test_type: test_type, test_strategy: test_strategy

  raise body_error if body_error
end

.cleaner_class(table_name) ⇒ Object



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 229

def cleaner_class(table_name)
  seed_class_name      = "#{table_name.to_s.classify}Cleaner"
  seed_class_base_name = seed_class_name.demodulize
  base_module          = seed_class_name.split("::")[0..-2].join("::")
  base_module_classes  = [Object]

  unless base_module.blank?
    base_module_classes = base_module_classes.unshift base_module.constantize
  end

  return_class = nil
  2.times do
    base_module_classes.each do |base_class|
      if (base_class.const_defined?(seed_class_base_name, false))
        if base_class == Object
          return_class = seed_class_base_name.constantize
        else
          return_class = "#{base_class.name}::#{seed_class_base_name}".constantize
        end

        break
      end
    end

    break if return_class

    seeder_file = "db/cleaners/"
    seeder_file += base_module.split("::").map { |module_name| module_name.underscore }.join("/")
    seeder_file += "/" unless seeder_file[-1] == "/"
    seeder_file += seed_class_base_name.underscore
    seeder_file += ".rb"
    seeder_file = File.join(Rails.root, seeder_file)

    break unless File.exists?(seeder_file)

    require seeder_file
  end

  # unless return_class &&
  #     VALID_TEST_METHODS.any? { |test_method| return_class.instance_methods.include?(test_method.to_sym) }
  #   return_class = table
  # end

  unless return_class &&
      VALID_TEST_METHODS.any? { |test_method| return_class.instance_methods.include?(test_method.to_sym) }
    return_class = PseudoCleaner::TableCleaner
  end

  return_class
end

.cleaner_classesObject



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 280

def cleaner_classes
  unless @@cleaner_classes
    @@cleaner_classes = []

    time = Benchmark.measure do
      puts "    Gathering cleaners" if PseudoCleaner::Configuration.instance.benchmark

      PseudoCleaner::MasterCleaner.create_table_cleaners
      PseudoCleaner::MasterCleaner.create_custom_cleaners
      # PseudoCleaner::MasterCleaner.create_redis_cleaners
    end
    puts "    Gathering cleaners time: #{time}" if PseudoCleaner::Configuration.instance.benchmark
  end

  @@cleaner_classes
end

.create_custom_cleaners(options = {}) ⇒ Object



314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 314

def create_custom_cleaners(options = {})
  if Object.const_defined?("Rails", false)
    cleaner_root  = Rails.root.join("db/cleaners/").to_s
    cleaner_files = Dir[Rails.root.join("db/cleaners/**/*.rb")]

    cleaner_files.each do |cleaner_file|
      class_name = File.basename(cleaner_file, ".rb").classify

      check_class, full_module_name = find_file_class(cleaner_file, cleaner_root)
      unless check_class && check_class.const_defined?(class_name, false)
        require cleaner_file
        check_class, full_module_name = find_file_class(cleaner_file, cleaner_root)
      end

      if check_class
        full_module_name << class_name
        if check_class.const_defined?(class_name, false)
          check_class = full_module_name.join("::").constantize
        else
          check_class = nil
        end
      end

      if check_class &&
          PseudoCleaner::MasterCleaner::VALID_TEST_METHODS.
              any? { |test_method| check_class.instance_methods.include?(test_method) }
        unless PseudoCleaner::MasterCleaner.cleaner_classes.any? { |cleaner| check_class == cleaner[2] }
          PseudoCleaner::MasterCleaner.cleaner_classes << [nil, nil, check_class]
        end
      end
    end
  end
end

.create_table_cleaners(options = {}) ⇒ Object



297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 297

def create_table_cleaners(options = {})
  SortedSeeder::Seeder.create_order(PseudoCleaner::Configuration.db_connection(nil)).each do |table|
    cleaner_class = PseudoCleaner::MasterCleaner.cleaner_class(table.name)
    if cleaner_class
      PseudoCleaner::MasterCleaner.cleaner_classes << [table, nil, cleaner_class]
    end
  end
  if SortedSeeder::Seeder.respond_to?(:unclassed_tables)
    SortedSeeder::Seeder.unclassed_tables(PseudoCleaner::Configuration.db_connection(nil)).each do |table_name|
      cleaner_class = PseudoCleaner::MasterCleaner.cleaner_class(table_name)
      if cleaner_class
        PseudoCleaner::MasterCleaner.cleaner_classes << [nil, table_name, cleaner_class]
      end
    end
  end
end

.database_cleanerObject

def clean_redis(redis)

@@redis_classes ||= []
@@redis_classes << redis

end



89
90
91
92
93
94
95
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 89

def database_cleaner
  if Object.const_defined?("ActiveRecord", false) && ActiveRecord.const_defined?("Base", false)
    DatabaseCleaner[:active_record, connection: PseudoCleaner::Configuration.db_connection(:active_record)]
  elsif Object.const_defined?("Sequel", false) && Sequel.const_defined?("Model", false)
    DatabaseCleaner[:sequel, connection: PseudoCleaner::Configuration.db_connection(:sequel)]
  end
end

.database_resetObject



212
213
214
215
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 212

def database_reset
  PseudoCleaner::MasterCleaner.seed_data
  PseudoCleaner::MasterCleaner.start_suite
end

.end_example(example_class, options = {}) ⇒ Object



125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 125

def end_example(example_class, options = {})
  options[:description] ||= "PseudoCleaner::MasterCleaner.end_example"

  time = Benchmark.measure do
    puts options[:description] if PseudoCleaner::Configuration.instance.benchmark

    within_report_block(options) do
      pseudo_cleaner_data = example_class.instance_variable_get(:@pseudo_cleaner_data)

      unless pseudo_cleaner_data[:test_strategy] == :none
        unless [:pseudo_delete].include? pseudo_cleaner_data[:test_strategy]
          PseudoCleaner::MasterCleaner.database_cleaner.clean
        end

        case pseudo_cleaner_data[:test_strategy]
          when :deletion, :truncation
            PseudoCleaner::MasterCleaner.database_reset
        end

        pseudo_cleaner_data[:pseudo_state].end test_type: :test, test_strategy: pseudo_cleaner_data[:test_strategy]
      end
    end
  end
  puts "#{options[:description]} time: #{time}" if PseudoCleaner::Configuration.instance.benchmark
end

.end_suite(description = nil) ⇒ Object



151
152
153
154
155
156
157
158
159
160
161
162
163
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 151

def end_suite(description = nil)
  description ||= "PseudoCleaner::MasterCleaner.end_suite"

  time = Benchmark.measure do
    puts description if PseudoCleaner::Configuration.instance.benchmark

    within_report_block(description: description) do
      @@suite_cleaner.end test_strategy: :pseudo_delete if @@suite_cleaner
    end
  end

  puts "#{description} time: #{time}" if PseudoCleaner::Configuration.instance.benchmark
end

.find_file_class(seeder_file, seeder_root) ⇒ Object

def create_redis_cleaners

if @@redis_classes
  @@redis_classes.each do |redis|
    PseudoCleaner::MasterCleaner.cleaner_classes << [redis, nil, PseudoCleaner::RedisMonitorCleaner]
  end
end

end



356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 356

def find_file_class(seeder_file, seeder_root)
  check_class      = Object
  full_module_name = []

  File.dirname(seeder_file.to_s[seeder_root.length..-1]).split("/").map do |module_element|
    if (module_element != ".")
      full_module_name << module_element.classify
      if check_class.const_defined?(full_module_name[-1], false)
        check_class = full_module_name.join("::").constantize
      else
        check_class = nil
        break
      end
    end
  end

  return check_class, full_module_name
end

.peek_data_inline(options = {}) ⇒ Object



379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 379

def peek_data_inline(options = {})
  @@report_error = false
  @@report_table = nil

  options[:description] ||= "PseudoCleaner::MasterCleaner.peek_data"

  time = Benchmark.measure do
    puts options[:description] if PseudoCleaner::Configuration.instance.benchmark

    if Object.const_defined?("Cornucopia", false) &&
        Cornucopia.const_defined?("Util", false) &&
        Cornucopia::Util.const_defined?("ReportBuilder", false) &&
        !PseudoCleaner::Configuration.current_instance.disable_cornucopia_output
      Cornucopia::Util::ReportBuilder.current_report.within_section(options[:description]) do |report|
        report.within_hidden_table do |outer_report_table|
          Cornucopia::Util::ReportTable.new(
              nested_table:         outer_report_table,
              suppress_blank_table: true) do |report_table|
            # redundant, but I like it because it is consistent.
            if options[:location]
              report_table.write_stats "location", options[:location]
            end

            @@report_table = report_table

            peek_values
          end
        end
      end

      @@report_table = nil
    else
      PseudoCleaner::Logger.write(options[:description])

      peek_values
    end
  end

  puts "#{options[:description]} time: #{time}" if PseudoCleaner::Configuration.instance.benchmark
end

.peek_data_new_test(options = {}) ⇒ Object



420
421
422
423
424
425
426
427
428
429
430
431
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 420

def peek_data_new_test(options = {})
  options[:description] ||= "PseudoCleaner::MasterCleaner.peek_data"

  time = Benchmark.measure do
    puts options[:description] if PseudoCleaner::Configuration.instance.benchmark

    within_report_block(options) do
      peek_values
    end
  end
  puts "#{options[:description]} time: #{time}" if PseudoCleaner::Configuration.instance.benchmark
end

.peek_valuesObject



433
434
435
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 433

def peek_values
  @@suite_cleaner.peek_values
end

.process_exception(error) ⇒ Object



222
223
224
225
226
227
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 222

def process_exception(error)
  PseudoCleaner::Logger.write("    An exception has occurred:".red.on_light_white)
  PseudoCleaner::Logger.write("")
  PseudoCleaner::Logger.write(error.to_s)
  PseudoCleaner::Logger.write(error.backtrace.join("\n")) if error.backtrace
end

.report_errorObject



31
32
33
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 31

def report_error
  @@report_error = true
end

.report_tableObject



27
28
29
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 27

def report_table
  @@report_table
end

.reset_database(description = nil) ⇒ Object



197
198
199
200
201
202
203
204
205
206
207
208
209
210
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 197

def reset_database(description = nil)
  description ||= "PseudoCleaner::MasterCleaner.reset_database"

  time = Benchmark.measure do
    puts description if PseudoCleaner::Configuration.instance.benchmark

    within_report_block(description: description) do
      PseudoCleaner::MasterCleaner.database_cleaner.clean_with(:truncation)

      PseudoCleaner::MasterCleaner.database_reset
    end
  end
  puts "#{description} time: #{time}" if PseudoCleaner::Configuration.instance.benchmark
end

.review_rows(&block) ⇒ Object



375
376
377
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 375

def review_rows(&block)
  @@suite_cleaner.review_rows(&block)
end

.seed_dataObject



217
218
219
220
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 217

def seed_data
  PseudoCleaner::Logger.write("Re-seeding database".red.on_light_white)
  SortedSeeder::Seeder.seed_all(PseudoCleaner::Configuration.db_connection(nil))
end

.start_example(example_class, strategy, options = {}) ⇒ Object



97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 97

def start_example(example_class, strategy, options = {})
  options[:description] ||= "PseudoCleaner::MasterCleaner.start_example"

  time = Benchmark.measure do
    puts options[:description] if PseudoCleaner::Configuration.instance.benchmark

    within_report_block(options) do
      pseudo_cleaner_data                 = {}
      pseudo_cleaner_data[:test_strategy] = strategy

      unless strategy == :none
        raise "invalid strategy" unless PseudoCleaner::MasterCleaner::DB_CLEANER_CLEANING_STRATEGIES.has_key? strategy

        DatabaseCleaner.strategy = PseudoCleaner::MasterCleaner::DB_CLEANER_CLEANING_STRATEGIES[strategy]
        unless [:pseudo_delete].include? strategy
          PseudoCleaner::MasterCleaner.database_cleaner.start
        end

        pseudo_cleaner_data[:pseudo_state] = PseudoCleaner::MasterCleaner.start_test strategy
      end

      example_class.instance_variable_set(:@pseudo_cleaner_data, pseudo_cleaner_data)
    end
  end

  puts "#{options[:description]} time: #{time}" if PseudoCleaner::Configuration.instance.benchmark
end

.start_suite(description = nil) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 66

def start_suite(description = nil)
  description ||= "PseudoCleaner::MasterCleaner.start_suite"

  time = Benchmark.measure do
    puts description if PseudoCleaner::Configuration.instance.benchmark

    within_report_block(description: description) do
      if @@suite_cleaner
        @@suite_cleaner.reset_suite
      end
      @@suite_cleaner = PseudoCleaner::MasterCleaner.new(:suite)
      @@suite_cleaner.start :pseudo_delete
      @@suite_cleaner
    end
  end
  puts "#{description} time: #{time}" if PseudoCleaner::Configuration.instance.benchmark
end

.start_test(test_strategy) ⇒ Object



165
166
167
168
169
170
171
172
173
174
175
176
177
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 165

def start_test test_strategy
  raise "Invalid test_strategy \"#{test_strategy}\"" unless CLEANING_STRATEGIES.include? test_strategy

  cleaner = if PseudoCleaner::Configuration.current_instance.single_cleaner_set
              @@suite_cleaner
            else
              PseudoCleaner::MasterCleaner.new(:test)
            end

  cleaner.start test_strategy, test_type: :test, test_strategy: test_strategy

  cleaner
end

.within_report_block(options, &block) ⇒ Object



35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 35

def within_report_block(options, &block)
  @@report_error = false
  @@report_table = nil

  if Object.const_defined?("Cornucopia", false) &&
      Cornucopia.const_defined?("Util", false) &&
      Cornucopia::Util.const_defined?("ReportBuilder", false)
    Cornucopia::Util::ReportBuilder.current_report.within_test(options[:description]) do
      Cornucopia::Util::ReportBuilder.current_report.within_section(options[:description]) do |report|
        report.within_table do |report_table|
          if options[:location]
            report_table.write_stats "location", options[:location]
          end

          @@report_table = report_table

          block.yield
        end
      end

      unless @@report_error
        Cornucopia::Util::ReportBuilder.current_report.test_succeeded
      end
    end

    @@report_table = nil
  else
    block.yield
  end
end

Instance Method Details

#end(options = {}) ⇒ Object



500
501
502
503
504
505
506
507
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 500

def end(options = {})
  test_type = options[:test_type] || @test_type
  if PseudoCleaner::Configuration.current_instance.output_diagnostics ||
      PseudoCleaner::Configuration.current_instance.post_transaction_analysis
    PseudoCleaner::Logger.write("Cleaning #{test_type}")
  end
  end_all_cleaners options
end

#end_all_cleaners(options) ⇒ Object



515
516
517
518
519
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 515

def end_all_cleaners(options)
  test_type     = options[:test_type] || @test_type
  test_strategy = options[:test_strategy] || @test_strategy
  run_all_cleaners("#{test_type}_end".to_sym, @cleaners.reverse, test_strategy)
end

#peek_valuesObject



549
550
551
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 549

def peek_values
  run_all_cleaners(:peek_values, @cleaners)
end

#reset_suiteObject



521
522
523
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 521

def reset_suite
  run_all_cleaners(:reset_suite, @cleaners.reverse)
end

#review_rows(&block) ⇒ Object



545
546
547
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 545

def review_rows(&block)
  run_all_cleaners(:review_rows, @cleaners, &block)
end

#run_all_cleaners(cleaner_function, cleaners, *args, &block) ⇒ Object



525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 525

def run_all_cleaners(cleaner_function, cleaners, *args, &block)
  last_error = nil

  if cleaners
    cleaners.each do |cleaner|
      begin
        if cleaner.respond_to?(cleaner_function)
          cleaner.send(cleaner_function, *args, &block)
        end
      rescue => error
        PseudoCleaner::MasterCleaner.process_exception(last_error) if last_error

        last_error = error
      end
    end
  end

  raise last_error if last_error
end

#start(test_strategy, options = {}) ⇒ Object



444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 444

def start(test_strategy, options = {})
  test_type = options[:test_type] || @test_type

  unless @cleaners
    @cleaners      = []
    @test_strategy = test_strategy

    time = Benchmark.measure do
      puts "  Building cleaners" if PseudoCleaner::Configuration.instance.benchmark

      start_method = "#{test_type}_start".to_sym
      end_method   = "#{test_type}_end".to_sym

      PseudoCleaner::MasterCleaner.cleaner_classes.each do |clean_class|
        table = clean_class[0]
        table ||= clean_class[1]

        begin
          @cleaners << clean_class[2].new(start_method, end_method, table, options)
        rescue Exception => error
          puts error.to_s
          raise error
        end
      end

      unless @@cleaner_classes_sorted
        seed_sorts = @cleaners.map { |cleaner| SortedSeeder::Seeder::SeederSorter.new(cleaner) }
        seed_sorts.sort!

        @cleaners = seed_sorts.map(&:seed_base_object)

        sorted_classes = []
        @cleaners.each do |cleaner|
          cleaner_class = PseudoCleaner::MasterCleaner.cleaner_classes.detect do |unsorted_cleaner|
            if cleaner.class == unsorted_cleaner[2]
              if unsorted_cleaner[2] == PseudoCleaner::TableCleaner
                cleaner.table == unsorted_cleaner[0] || cleaner.table == unsorted_cleaner[1]
              else
                true
              end
            end
          end

          sorted_classes << cleaner_class
        end

        @@cleaner_classes        = sorted_classes
        @@cleaner_classes_sorted = true
      end
    end
    puts "  Building cleaners time: #{time}" if PseudoCleaner::Configuration.instance.benchmark
  end

  start_all_cleaners options
end

#start_all_cleaners(options) ⇒ Object



509
510
511
512
513
# File 'lib/pseudo_cleaner/master_cleaner.rb', line 509

def start_all_cleaners(options)
  test_type     = options[:test_type] || @test_type
  test_strategy = options[:test_strategy] || @test_strategy
  run_all_cleaners("#{test_type}_start".to_sym, @cleaners, test_strategy)
end