fleiss 0.1.3 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 45bf4b9f2a7a9d88cbdd4feb8fb2992d6545554695c791a1f327a5a26da3e53d
4
- data.tar.gz: 4434eac634c36952365be272f5f8327462577db2cc338c97b1f242f2f59d14bf
3
+ metadata.gz: 2d7f27ee73ed5c0434f79ec98f0c249f4f41ece528807cabc3efd29f8ebf79c0
4
+ data.tar.gz: e597168cfb8931240110ad45b4ee49937128bdb4dce826d142954253ce29dc9d
5
5
  SHA512:
6
- metadata.gz: d008669212582e7e5190ee0901024555c5031d0295b934d08dd91508d0b83251ea7790540c0e4d8339cae15b6aa9c6c043d5fc39eaa18bc5a696a30e23bc4a0f
7
- data.tar.gz: fc3f5555a6290abebd712e545172fea30eb4be0074da2eb86bc1cb136216af563054111d45599f2e9ddb3560a94c00256e7a8d00b3f7b69e849d39264911131d
6
+ metadata.gz: 737815c833f1ff98e4c18c54709b7671ecd26cabb8500e5f93375a4ac528b4a8344381fe906db2e27726e2217de036a406aa83263514e2475798e5a15831f037
7
+ data.tar.gz: dae7da18743673e38465150031a48734e7911d7894c0131a8d9469d472650f30ea53a73d6217bcc6c4f237c93fa8dfd147ad9df4ac3542e1f015e75dbbda4c4c
data/.gitignore CHANGED
@@ -1,2 +1,3 @@
1
1
  .rubocop-*
2
2
  pkg/
3
+ spec/tmp
@@ -1,5 +1,8 @@
1
1
  inherit_from:
2
- - https://bitbucket.org/bsm/misc/raw/master/rubocop/default.yml
2
+ - https://gitlab.com/bsm/misc/raw/master/rubocop/default.yml
3
+
4
+ AllCops:
5
+ TargetRubyVersion: "2.6"
3
6
 
4
7
  Security/Open:
5
8
  Exclude:
@@ -1,8 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.5
4
- - 2.4
3
+ - 2.7
4
+ - 2.6
5
5
  before_install:
6
+ - gem install bundler
6
7
  - mysql -e 'CREATE DATABASE fleiss_test;'
7
8
  - psql -c 'CREATE DATABASE fleiss_test;' -U postgres
8
9
  services:
@@ -1,73 +1,74 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- fleiss (0.1.3)
5
- activejob (>= 5.0)
6
- activerecord (>= 5.0)
4
+ fleiss (0.4.0)
5
+ activejob (>= 6.0)
6
+ activerecord (>= 6.0)
7
7
  concurrent-ruby
8
8
 
9
9
  GEM
10
10
  remote: https://rubygems.org/
11
11
  specs:
12
- activejob (5.2.2)
13
- activesupport (= 5.2.2)
12
+ activejob (6.0.3.1)
13
+ activesupport (= 6.0.3.1)
14
14
  globalid (>= 0.3.6)
15
- activemodel (5.2.2)
16
- activesupport (= 5.2.2)
17
- activerecord (5.2.2)
18
- activemodel (= 5.2.2)
19
- activesupport (= 5.2.2)
20
- arel (>= 9.0)
21
- activesupport (5.2.2)
15
+ activemodel (6.0.3.1)
16
+ activesupport (= 6.0.3.1)
17
+ activerecord (6.0.3.1)
18
+ activemodel (= 6.0.3.1)
19
+ activesupport (= 6.0.3.1)
20
+ activesupport (6.0.3.1)
22
21
  concurrent-ruby (~> 1.0, >= 1.0.2)
23
22
  i18n (>= 0.7, < 2)
24
23
  minitest (~> 5.1)
25
24
  tzinfo (~> 1.1)
26
- arel (9.0.0)
25
+ zeitwerk (~> 2.2, >= 2.2.2)
27
26
  ast (2.4.0)
28
- concurrent-ruby (1.1.3)
27
+ concurrent-ruby (1.1.6)
29
28
  diff-lcs (1.3)
30
- globalid (0.4.1)
29
+ globalid (0.4.2)
31
30
  activesupport (>= 4.2.0)
32
- i18n (1.2.0)
31
+ i18n (1.8.2)
33
32
  concurrent-ruby (~> 1.0)
34
- jaro_winkler (1.5.1)
35
- minitest (5.11.3)
36
- mysql2 (0.5.2)
37
- parallel (1.12.1)
38
- parser (2.5.3.0)
33
+ minitest (5.14.1)
34
+ mysql2 (0.5.3)
35
+ parallel (1.19.1)
36
+ parser (2.7.1.3)
39
37
  ast (~> 2.4.0)
40
- pg (1.1.3)
41
- powerpack (0.1.2)
38
+ pg (1.2.3)
42
39
  rainbow (3.0.0)
43
- rake (12.3.2)
44
- rspec (3.8.0)
45
- rspec-core (~> 3.8.0)
46
- rspec-expectations (~> 3.8.0)
47
- rspec-mocks (~> 3.8.0)
48
- rspec-core (3.8.0)
49
- rspec-support (~> 3.8.0)
50
- rspec-expectations (3.8.2)
40
+ rake (13.0.1)
41
+ rexml (3.2.4)
42
+ rspec (3.9.0)
43
+ rspec-core (~> 3.9.0)
44
+ rspec-expectations (~> 3.9.0)
45
+ rspec-mocks (~> 3.9.0)
46
+ rspec-core (3.9.2)
47
+ rspec-support (~> 3.9.3)
48
+ rspec-expectations (3.9.2)
51
49
  diff-lcs (>= 1.2.0, < 2.0)
52
- rspec-support (~> 3.8.0)
53
- rspec-mocks (3.8.0)
50
+ rspec-support (~> 3.9.0)
51
+ rspec-mocks (3.9.1)
54
52
  diff-lcs (>= 1.2.0, < 2.0)
55
- rspec-support (~> 3.8.0)
56
- rspec-support (3.8.0)
57
- rubocop (0.61.1)
58
- jaro_winkler (~> 1.5.1)
53
+ rspec-support (~> 3.9.0)
54
+ rspec-support (3.9.3)
55
+ rubocop (0.84.0)
59
56
  parallel (~> 1.10)
60
- parser (>= 2.5, != 2.5.1.1)
61
- powerpack (~> 0.1)
57
+ parser (>= 2.7.0.1)
62
58
  rainbow (>= 2.2.2, < 4.0)
59
+ rexml
60
+ rubocop-ast (>= 0.0.3)
63
61
  ruby-progressbar (~> 1.7)
64
- unicode-display_width (~> 1.4.0)
65
- ruby-progressbar (1.10.0)
66
- sqlite3 (1.3.13)
62
+ unicode-display_width (>= 1.4.0, < 2.0)
63
+ rubocop-ast (0.0.3)
64
+ parser (>= 2.7.0.1)
65
+ ruby-progressbar (1.10.1)
66
+ sqlite3 (1.4.2)
67
67
  thread_safe (0.3.6)
68
- tzinfo (1.2.5)
68
+ tzinfo (1.2.7)
69
69
  thread_safe (~> 0.1)
70
- unicode-display_width (1.4.0)
70
+ unicode-display_width (1.7.0)
71
+ zeitwerk (2.3.0)
71
72
 
72
73
  PLATFORMS
73
74
  ruby
@@ -83,4 +84,4 @@ DEPENDENCIES
83
84
  sqlite3
84
85
 
85
86
  BUNDLED WITH
86
- 1.16.4
87
+ 2.1.4
data/bin/fleiss CHANGED
@@ -5,17 +5,20 @@ $LOAD_PATH.push(lib_dir) unless $LOAD_PATH.include?(lib_dir)
5
5
 
6
6
  require 'fleiss/cli'
7
7
 
8
+ STDOUT.sync = true
9
+ STDERR.sync = true
10
+
8
11
  cli = Fleiss::CLI.instance
9
12
  begin
10
13
  cli.parse!
11
14
  cli.run!
12
15
  rescue ArgumentError => e
13
- STDERR.puts " ! #{e.message}\n"
16
+ warn " ! #{e.message}\n"
14
17
  STDERR.puts
15
- STDERR.puts cli.parser
18
+ warn cli.parser
16
19
  exit 1
17
20
  rescue StandardError => e
18
- STDERR.puts e.message
19
- STDERR.puts e.backtrace.join("\n")
21
+ warn e.message
22
+ warn e.backtrace.join("\n")
20
23
  exit 1
21
24
  end
@@ -1,6 +1,6 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = 'fleiss'
3
- s.version = '0.1.3'
3
+ s.version = '0.4.0'
4
4
  s.authors = ['Black Square Media Ltd']
5
5
  s.email = ['info@blacksquaremedia.com']
6
6
  s.summary = %(Minimialist background jobs backed by ActiveJob and ActiveRecord.)
@@ -12,10 +12,10 @@ Gem::Specification.new do |s|
12
12
  s.files = `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^spec/}) }
13
13
  s.test_files = `git ls-files -z -- spec/*`.split("\x0")
14
14
  s.require_paths = ['lib']
15
- s.required_ruby_version = '>= 2.2'
15
+ s.required_ruby_version = '>= 2.6'
16
16
 
17
- s.add_dependency 'activejob', '>= 5.0'
18
- s.add_dependency 'activerecord', '>= 5.0'
17
+ s.add_dependency 'activejob', '>= 6.0'
18
+ s.add_dependency 'activerecord', '>= 6.0'
19
19
  s.add_dependency 'concurrent-ruby'
20
20
 
21
21
  s.add_development_dependency 'bundler'
@@ -1,9 +1,15 @@
1
+ require 'active_job'
2
+ require 'active_job/queue_adapters/fleiss_adapter'
3
+ require 'fleiss/backend'
4
+
1
5
  module Fleiss
2
6
  DEFAULT_QUEUE = 'default'.freeze
3
7
 
4
8
  def self.backend
5
- self::Backend::ActiveRecord
9
+ @backend ||= self::Backend::ActiveRecord
6
10
  end
7
- end
8
11
 
9
- require 'fleiss/backend'
12
+ def self.backend=(value)
13
+ @backend = value
14
+ end
15
+ end
@@ -8,15 +8,19 @@ module Fleiss
8
8
  scope :in_queue, ->(qs) { where(queue_name: Array.wrap(qs)) }
9
9
  scope :finished, -> { where.not(finished_at: nil) }
10
10
  scope :not_finished, -> { where(finished_at: nil) }
11
- scope :not_expired, ->(now=Time.zone.now) { where(arel_table[:expires_at].eq(nil).or(arel_table[:expires_at].gt(now))) }
11
+ scope :not_expired, ->(now = Time.zone.now) { where(arel_table[:expires_at].eq(nil).or(arel_table[:expires_at].gt(now))) }
12
12
  scope :started, -> { where(arel_table[:started_at].not_eq(nil)) }
13
13
  scope :not_started, -> { where(arel_table[:started_at].eq(nil)) }
14
- scope :scheduled, ->(now=Time.zone.now) { where(arel_table[:scheduled_at].gt(now)) }
14
+ scope :scheduled, ->(now = Time.zone.now) { where(arel_table[:scheduled_at].gt(now)) }
15
15
  end
16
16
 
17
17
  module ClassMethods
18
+ def wrap_perform(&block)
19
+ connection_pool.with_connection(&block)
20
+ end
21
+
18
22
  # @return [ActiveRecord::Relation] pending scope
19
- def pending(now=Time.zone.now)
23
+ def pending(now = Time.zone.now)
20
24
  not_finished
21
25
  .not_expired(now)
22
26
  .not_started
@@ -2,7 +2,6 @@ require 'singleton'
2
2
  require 'optparse'
3
3
  require 'yaml'
4
4
  require 'erb'
5
- require 'logger'
6
5
 
7
6
  module Fleiss
8
7
  class CLI
@@ -15,7 +14,6 @@ module Fleiss
15
14
  require: [],
16
15
  concurrency: 10,
17
16
  wait_time: 1.0,
18
- logfile: STDOUT,
19
17
  }.freeze
20
18
 
21
19
  attr_reader :opts
@@ -24,7 +22,7 @@ module Fleiss
24
22
  @opts = DEFAULT_OPTIONS.dup
25
23
  end
26
24
 
27
- def parse!(argv=ARGV)
25
+ def parse!(argv = ARGV)
28
26
  parser.parse!(argv)
29
27
 
30
28
  # Check config file
@@ -46,14 +44,11 @@ module Fleiss
46
44
  opts[:require].each {|n| require n }
47
45
  require 'fleiss/worker'
48
46
 
49
- logdev = opts[:logfile]
50
- logdev.sync = true if logdev.respond_to?(:sync=)
51
-
47
+ ActiveJob::Base.logger = ActiveSupport::TaggedLogging.new(ActiveSupport::Logger.new(opts[:logfile])) if opts[:logfile]
52
48
  Fleiss::Worker.run \
53
49
  queues: opts[:queues],
54
50
  concurrency: opts[:concurrency],
55
- wait_time: opts[:wait_time],
56
- logger: Logger.new(logdev)
51
+ wait_time: opts[:wait_time]
57
52
  end
58
53
 
59
54
  def parser # rubocop:disable Metrics/MethodLength
@@ -0,0 +1,22 @@
1
+ require 'fleiss'
2
+ require 'concurrent/executor/simple_executor_service'
3
+
4
+ class Fleiss::Executor < Concurrent::SimpleExecutorService
5
+ attr_reader :max_size
6
+
7
+ def post(&block)
8
+ super unless capacity.zero?
9
+ end
10
+
11
+ def capacity
12
+ val = @max_size - @count.value
13
+ val.positive? ? val : 0
14
+ end
15
+
16
+ private
17
+
18
+ def ns_initialize(opts = {})
19
+ super()
20
+ @max_size = opts.fetch(:max_size, 1).to_i
21
+ end
22
+ end
@@ -1,11 +1,9 @@
1
1
  require 'fleiss'
2
- require 'concurrent/executor/fixed_thread_pool'
3
- require 'concurrent/atomic/atomic_fixnum'
4
- require 'logger'
2
+ require 'fleiss/executor'
5
3
  require 'securerandom'
6
4
 
7
5
  class Fleiss::Worker
8
- attr_reader :queues, :uuid, :wait_time, :logger
6
+ attr_reader :queues, :uuid, :wait_time
9
7
 
10
8
  # Shortcut for new(*args).run
11
9
  def self.run(*args)
@@ -18,24 +16,22 @@ class Fleiss::Worker
18
16
  # @option [Array<String>] :queues queues to watch. Default: ["default"]
19
17
  # @option [Integer] :concurrency the number of concurrent pool. Default: 10
20
18
  # @option [Numeric] :wait_time maximum time (in seconds) to wait for jobs when retrieving next batch. Default: 1s.
21
- # @option [Logger] :logger optional logger.
22
- def initialize(queues: [Fleiss::DEFAULT_QUEUE], concurrency: 10, wait_time: 1, logger: nil)
19
+ def initialize(queues: [Fleiss::DEFAULT_QUEUE], concurrency: 10, wait_time: 1)
23
20
  @uuid = SecureRandom.uuid
24
21
  @queues = Array(queues)
25
- @pool = Concurrent::FixedThreadPool.new(concurrency, fallback_policy: :discard)
22
+ @pool = Fleiss::Executor.new(max_size: concurrency)
26
23
  @wait_time = wait_time
27
- @logger = logger || Logger.new(nil)
28
24
  end
29
25
 
30
26
  # Run starts the worker
31
27
  def run
32
- logger.info "Worker #{uuid} starting - queues: #{queues.inspect}, concurrency: #{@pool.max_length}"
28
+ log(:info) { "Worker #{uuid} starting - queues: #{queues.inspect}, concurrency: #{@pool.max_size}" }
33
29
  loop do
34
30
  run_cycle
35
31
  sleep @wait_time
36
32
  end
37
33
  rescue SignalException => e
38
- logger.info "Worker #{uuid} received #{e.message}. Shutting down..."
34
+ log(:info) { "Worker #{uuid} received #{e.message}. Shutting down..." }
39
35
  ensure
40
36
  @pool.shutdown
41
37
  @pool.wait_for_termination
@@ -43,20 +39,31 @@ class Fleiss::Worker
43
39
 
44
40
  private
45
41
 
42
+ def log(severity, &block)
43
+ logger = ActiveJob::Base.logger
44
+ if logger.respond_to?(:tagged)
45
+ logger.tagged('Fleiss') { logger.send(severity, &block) }
46
+ else
47
+ logger.send(severity, &block)
48
+ end
49
+ end
50
+
46
51
  def run_cycle
47
- return if @pool.shuttingdown?
52
+ return unless @pool.running?
48
53
 
49
- capacity = @pool.max_length - @pool.scheduled_task_count + @pool.completed_task_count
50
- return unless capacity.positive?
54
+ limit = @pool.capacity
55
+ return unless limit.positive?
51
56
 
52
57
  batch = Fleiss.backend
53
58
  .in_queue(queues)
54
59
  .pending
55
- .limit(capacity)
60
+ .limit(limit)
56
61
  .to_a
57
62
 
58
63
  batch.each do |job|
59
- @pool.post { perform(job) }
64
+ @pool.post do
65
+ Fleiss.backend.wrap_perform { perform(job) }
66
+ end
60
67
  end
61
68
  rescue StandardError => e
62
69
  handle_exception e, 'running cycle'
@@ -67,7 +74,7 @@ class Fleiss::Worker
67
74
  owner = "#{uuid}/#{thread_id}"
68
75
  return unless job.start(owner)
69
76
 
70
- logger.info { "Worker #{uuid} execute job ##{job.id} (by thread #{thread_id})" }
77
+ log(:info) { "Worker #{uuid} execute job ##{job.id} (by thread #{thread_id})" }
71
78
  finished = false
72
79
  begin
73
80
  ActiveJob::Base.execute job.job_data
@@ -83,12 +90,12 @@ class Fleiss::Worker
83
90
  end
84
91
 
85
92
  def handle_exception(err, intro)
86
- lines = [
87
- "Worker #{uuid} error on #{intro}:",
88
- "#{err.class.name}: #{err.message}",
89
- err.backtrace,
90
- ].compact.flatten
91
-
92
- logger.error lines.join("\n")
93
+ log(:error) do
94
+ [
95
+ "Worker #{uuid} error on #{intro}:",
96
+ "#{err.class.name}: #{err.message}",
97
+ err.backtrace,
98
+ ].compact.flatten.join("\n")
99
+ end
93
100
  end
94
101
  end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+ require 'fleiss/executor'
3
+
4
+ RSpec.describe Fleiss::Executor do
5
+ subject { described_class.new max_size: 2 }
6
+ after { subject.kill }
7
+
8
+ it 'should check capacity' do
9
+ expect(described_class.new.capacity).to eq(1)
10
+
11
+ expect(subject.capacity).to eq(2)
12
+ subject.post { sleep(1) }
13
+ expect(subject.capacity).to eq(1)
14
+ subject.post { sleep(1) }
15
+ expect(subject.capacity).to eq(0)
16
+ end
17
+
18
+ it 'should discard execution when capacity is reached' do
19
+ n = Concurrent::AtomicFixnum.new(0)
20
+ 10.times do
21
+ 10.times { subject.post { n.increment } }
22
+ sleep(0.001)
23
+ end
24
+ subject.shutdown
25
+ subject.wait_for_termination(1)
26
+ expect(n.value).to be_within(10).of(20)
27
+ end
28
+ end
@@ -5,15 +5,19 @@ require 'fleiss'
5
5
  require 'fleiss/backend/active_record/migration'
6
6
  require 'active_job'
7
7
  require 'active_job/queue_adapters/fleiss_adapter'
8
- require 'tempfile'
8
+ require 'fileutils'
9
9
 
10
10
  ActiveJob::Base.queue_adapter = :fleiss
11
11
  ActiveJob::Base.logger = Logger.new(nil)
12
12
 
13
13
  Time.zone_default = Time.find_zone!('UTC')
14
14
 
15
- database_url = ENV['DATABASE_URL'] || "sqlite3://#{Tempfile.new(['fleiss-test', '.sqlite3']).path}"
16
- ActiveRecord::Base.configurations['test'] = { 'url' => database_url, 'pool' => 20 }
15
+ tmpdir = File.expand_path('./tmp', __dir__)
16
+ FileUtils.rm_rf tmpdir
17
+ FileUtils.mkdir_p tmpdir
18
+
19
+ database_url = ENV['DATABASE_URL'] || "sqlite3://#{tmpdir}/fleiss-test.sqlite3"
20
+ ActiveRecord::Base.configurations = { 'test' => { 'url' => database_url, 'pool' => 20 } }
17
21
 
18
22
  ActiveRecord::Base.establish_connection :test
19
23
  ActiveRecord::Base.connection.drop_table('fleiss_jobs', if_exists: true)
@@ -32,7 +36,7 @@ class TestJob < ActiveJob::Base
32
36
  72.hours
33
37
  end
34
38
 
35
- def perform(msg=nil)
39
+ def perform(msg = nil)
36
40
  raise 'Failing' if msg == 'raise'
37
41
 
38
42
  self.class.performed.push(msg)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fleiss
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Black Square Media Ltd
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-12-19 00:00:00.000000000 Z
11
+ date: 2020-05-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activejob
@@ -16,28 +16,28 @@ dependencies:
16
16
  requirements:
17
17
  - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '5.0'
19
+ version: '6.0'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '5.0'
26
+ version: '6.0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activerecord
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '5.0'
33
+ version: '6.0'
34
34
  type: :runtime
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
- version: '5.0'
40
+ version: '6.0'
41
41
  - !ruby/object:Gem::Dependency
42
42
  name: concurrent-ruby
43
43
  requirement: !ruby/object:Gem::Requirement
@@ -176,8 +176,10 @@ files:
176
176
  - lib/fleiss/backend/active_record/concern.rb
177
177
  - lib/fleiss/backend/active_record/migration.rb
178
178
  - lib/fleiss/cli.rb
179
+ - lib/fleiss/executor.rb
179
180
  - lib/fleiss/worker.rb
180
181
  - spec/fleiss/backend/active_record_spec.rb
182
+ - spec/fleiss/executor_spec.rb
181
183
  - spec/fleiss/worker_spec.rb
182
184
  - spec/fleiss_spec.rb
183
185
  - spec/spec_helper.rb
@@ -193,20 +195,20 @@ required_ruby_version: !ruby/object:Gem::Requirement
193
195
  requirements:
194
196
  - - ">="
195
197
  - !ruby/object:Gem::Version
196
- version: '2.2'
198
+ version: '2.6'
197
199
  required_rubygems_version: !ruby/object:Gem::Requirement
198
200
  requirements:
199
201
  - - ">="
200
202
  - !ruby/object:Gem::Version
201
203
  version: '0'
202
204
  requirements: []
203
- rubyforge_project:
204
- rubygems_version: 2.7.7
205
+ rubygems_version: 3.1.2
205
206
  signing_key:
206
207
  specification_version: 4
207
208
  summary: Minimialist background jobs backed by ActiveJob and ActiveRecord.
208
209
  test_files:
209
210
  - spec/fleiss/backend/active_record_spec.rb
211
+ - spec/fleiss/executor_spec.rb
210
212
  - spec/fleiss/worker_spec.rb
211
213
  - spec/fleiss_spec.rb
212
214
  - spec/spec_helper.rb