sidekiq-merger 0.0.9 → 0.0.10
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 +4 -4
- data/.codeclimate.yml +3 -0
- data/.travis.yml +9 -0
- data/Dockerfile +5 -2
- data/Gemfile +2 -0
- data/README.md +14 -1
- data/app/Gemfile.lock +2 -2
- data/app/views/index.erb +53 -26
- data/docker-compose.yml +2 -1
- data/lib/sidekiq/merger.rb +8 -0
- data/lib/sidekiq/merger/config.rb +1 -1
- data/lib/sidekiq/merger/version.rb +1 -1
- data/misc/bulk_notification_flow.png +0 -0
- data/misc/cancel_task_flow.png +0 -0
- data/spec/sidekiq/merger/merge_spec.rb +12 -27
- data/spec/sidekiq/merger/middleware_spec.rb +1 -18
- data/spec/sidekiq/merger_spec.rb +16 -1
- data/spec/spec_helper.rb +6 -19
- data/spec/support/worker_class.rb +34 -0
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c2966632f9d19e7a52b7394eb98d0fe8b114e9ba
|
4
|
+
data.tar.gz: 2ade2314686d38e8b93c62be6aee28878ef3eac9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4eb0461a1a436e3a1a5b96afafa36a094e1b6c5a658dd40727766283ec6d6a5f2a8b19d5a93fad528e9ec6ce138bf756b2a80ec2d7f4afe9f17af293c8cff63
|
7
|
+
data.tar.gz: bdcc4958eaba9693a28e2a6707280a9e7a563e72b00c339b2e36cbd8549eb43026668ad3248ef8f956beef520403a157102321f9579b548f8020d663334a0419
|
data/.codeclimate.yml
ADDED
data/.travis.yml
CHANGED
@@ -4,6 +4,13 @@ rvm:
|
|
4
4
|
- 2.2.6
|
5
5
|
- 2.3.3
|
6
6
|
- 2.4.0
|
7
|
+
env:
|
8
|
+
-
|
9
|
+
- SIDEKIQ_VERSION=3.4.0
|
10
|
+
- SIDEKIQ_VERSION=3.5.0
|
11
|
+
- SIDEKIQ_VERSION=4.0.0
|
12
|
+
- SIDEKIQ_VERSION=4.1.0
|
13
|
+
- SIDEKIQ_VERSION=4.2.0
|
7
14
|
services:
|
8
15
|
- redis-server
|
9
16
|
before_install:
|
@@ -12,3 +19,5 @@ before_install:
|
|
12
19
|
script:
|
13
20
|
- "bundle exec rake spec"
|
14
21
|
- "bundle exec rubocop -D"
|
22
|
+
notifications:
|
23
|
+
email: false
|
data/Dockerfile
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
FROM ruby:2.3.3
|
2
2
|
MAINTAINER dtaniwaki
|
3
3
|
|
4
|
-
ENV PORT
|
4
|
+
ENV PORT 3000
|
5
|
+
ENV REDIS_HOST 127.0.0.1
|
6
|
+
ENV REDIS_PORT 6379
|
7
|
+
|
5
8
|
RUN gem install bundler
|
6
9
|
ADD . /gem
|
7
10
|
WORKDIR /gem/app
|
8
11
|
RUN bundle install -j4
|
9
12
|
|
10
|
-
EXPOSE
|
13
|
+
EXPOSE $PORT
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -6,8 +6,20 @@
|
|
6
6
|
[![Coverage Status][cov-image]][cov-link]
|
7
7
|
[![Code Climate][gpa-image]][gpa-link]
|
8
8
|
|
9
|
+
[![Docker][docker-hub-image]][docker-hub-link]
|
10
|
+
|
9
11
|
Merge [sidekiq](http://sidekiq.org/) jobs occurring before the execution times. Inspired by [sidekiq-grouping](https://github.com/gzigzigzeo/sidekiq-grouping).
|
10
12
|
|
13
|
+
## Use Case
|
14
|
+
|
15
|
+
### Cancel Task
|
16
|
+
|
17
|
+

|
18
|
+
|
19
|
+
### Bulk Notification
|
20
|
+
|
21
|
+

|
22
|
+
|
11
23
|
## Installation
|
12
24
|
|
13
25
|
Add this line to your application's Gemfile:
|
@@ -133,4 +145,5 @@ Copyright (c) 2017 dtaniwaki. See [LICENSE](LICENSE) for details.
|
|
133
145
|
[cov-link]: https://coveralls.io/r/dtaniwaki/sidekiq-merger
|
134
146
|
[gpa-image]: https://codeclimate.com/github/dtaniwaki/sidekiq-merger.svg
|
135
147
|
[gpa-link]: https://codeclimate.com/github/dtaniwaki/sidekiq-merger
|
136
|
-
|
148
|
+
[docker-hub-image]: http://dockeri.co/image/dtaniwaki/sidekiq-merger
|
149
|
+
[docker-hub-link]: https://hub.docker.com/r/dtaniwaki/sidekiq-merger/
|
data/app/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: ../
|
3
3
|
specs:
|
4
|
-
sidekiq-merger (0.0.
|
4
|
+
sidekiq-merger (0.0.9)
|
5
5
|
activesupport (>= 3.2, < 6)
|
6
6
|
concurrent-ruby (~> 1.0)
|
7
7
|
sidekiq (>= 3.4, < 5)
|
@@ -16,7 +16,7 @@ GEM
|
|
16
16
|
tzinfo (~> 1.1)
|
17
17
|
concurrent-ruby (1.0.4)
|
18
18
|
connection_pool (2.2.1)
|
19
|
-
i18n (0.
|
19
|
+
i18n (0.8.0)
|
20
20
|
minitest (5.10.1)
|
21
21
|
rack (1.6.5)
|
22
22
|
rack-flash3 (1.0.5)
|
data/app/views/index.erb
CHANGED
@@ -1,35 +1,62 @@
|
|
1
|
-
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
2
3
|
<head>
|
3
|
-
<meta charset="UTF-8">
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
|
4
6
|
</head>
|
5
7
|
<body>
|
6
|
-
<
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
<p>
|
11
|
-
<a href="/sidekiq" target="_blank">Open sidekiq console</a>
|
12
|
-
</p>
|
13
|
-
<h2>Workers</h2>
|
14
|
-
<div style="margin-left: 20px;">
|
15
|
-
<h3>SomeWorker</h3>
|
16
|
-
<div>
|
17
|
-
<form action="/some_worker/perform_in" method="post" style="display: inline-block;">
|
18
|
-
<input type="submit" name="perform_in" value="perform_in">
|
19
|
-
</form>
|
20
|
-
<form action="/some_worker/perform_in" method="post" style="display: inline-block;">
|
21
|
-
<input type="submit" name="perform_async" value="perform_async">
|
22
|
-
</form>
|
8
|
+
<header class="navbar">
|
9
|
+
<div class="container">
|
10
|
+
<h1>Sidekiq Merger</h1>
|
11
|
+
<a href="https://github.com/dtaniwaki/sidekiq-merger">View Source on GitHub →</a>
|
23
12
|
</div>
|
24
|
-
|
13
|
+
</header>
|
14
|
+
<div class="container">
|
15
|
+
<% if flash[:notice] %>
|
16
|
+
<div class="alert alert-info">
|
17
|
+
<a href="#" class="close" data-dismiss="alert">×</a>
|
18
|
+
<p><%= flash[:notice] %></p>
|
19
|
+
</div>
|
20
|
+
<% end %>
|
21
|
+
<p class="lead">
|
22
|
+
Click the `perform_in` buttons to create or merge tasks until the execution time (in 60s).<br>
|
23
|
+
Click the `perform_async` buttons to execute a single task.<br><br>
|
24
|
+
Open <a href="/sidekiq/merges?poll=true" target="_blank">sidekiq console</a> to check what happens.
|
25
|
+
</p>
|
25
26
|
<div>
|
26
|
-
<
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
27
|
+
<h2>SomeWorker</h2>
|
28
|
+
<p>
|
29
|
+
<code>sidekiq_options merger: { unique: false }</code>
|
30
|
+
</p>
|
31
|
+
<p class="lead">
|
32
|
+
<small>Tasks will be merged regardless of uniqueness.</small>
|
33
|
+
</p>
|
34
|
+
<div>
|
35
|
+
<form action="/some_worker/perform_in" method="post" style="display: inline-block;">
|
36
|
+
<input type="submit" name="perform_in" value="perform_in" class="btn btn-primary">
|
37
|
+
</form>
|
38
|
+
<form action="/some_worker/perform_in" method="post" style="display: inline-block;">
|
39
|
+
<input type="submit" name="perform_async" value="perform_async" class="btn btn-default">
|
40
|
+
</form>
|
41
|
+
</div>
|
42
|
+
<h2>UniqueWorker</h2>
|
43
|
+
<p>
|
44
|
+
<code>sidekiq_options merger: { unique: true }</code>
|
45
|
+
</p>
|
46
|
+
<p class="lead">
|
47
|
+
<small>Tasks will be merged if they haven't added already.</small>
|
48
|
+
</p>
|
49
|
+
<div>
|
50
|
+
<form action="/unique_worker/perform_in" method="post" style="display: inline-block;">
|
51
|
+
<input type="submit" name="perform_in" value="perform_in" class="btn btn-primary">
|
52
|
+
</form>
|
53
|
+
<form action="/unique_worker/perform_in" method="post" style="display: inline-block;">
|
54
|
+
<input type="submit" name="perform_async" value="perform_async" class="btn btn-default">
|
55
|
+
</form>
|
56
|
+
</div>
|
32
57
|
</div>
|
33
58
|
</div>
|
59
|
+
<script type="text/javascript" src="https://code.jquery.com/jquery-3.1.1.min.js"></script>
|
60
|
+
<script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
|
34
61
|
</body>
|
35
62
|
</html>
|
data/docker-compose.yml
CHANGED
data/lib/sidekiq/merger.rb
CHANGED
Binary file
|
Binary file
|
@@ -1,28 +1,13 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
describe Sidekiq::Merger::Merge do
|
3
|
+
describe Sidekiq::Merger::Merge, worker_class: true do
|
4
4
|
subject { described_class.new(worker_class, queue, args, redis: redis) }
|
5
5
|
let(:args) { "foo" }
|
6
6
|
let(:redis) { Sidekiq::Merger::Redis.new }
|
7
7
|
let(:queue) { "queue" }
|
8
8
|
let(:now) { Time.now }
|
9
9
|
let(:execution_time) { now + 10.seconds }
|
10
|
-
let(:
|
11
|
-
let(:worker_class) do
|
12
|
-
local_options = options
|
13
|
-
Class.new do
|
14
|
-
include Sidekiq::Worker
|
15
|
-
|
16
|
-
sidekiq_options merger: local_options
|
17
|
-
|
18
|
-
def self.name
|
19
|
-
"name"
|
20
|
-
end
|
21
|
-
|
22
|
-
def perform(args)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
10
|
+
let(:worker_options) { { key: -> (args) { args.to_json } } }
|
26
11
|
before { Timecop.freeze(now) }
|
27
12
|
|
28
13
|
describe ".all" do
|
@@ -64,30 +49,30 @@ describe Sidekiq::Merger::Merge do
|
|
64
49
|
|
65
50
|
describe ".merge_key" do
|
66
51
|
let(:args) { "foo" }
|
67
|
-
let(:
|
52
|
+
let(:worker_options) { {} }
|
68
53
|
it "returns an empty string" do
|
69
54
|
expect(described_class.merge_key(worker_class, args)).to eq ""
|
70
55
|
end
|
71
56
|
context "string key" do
|
72
|
-
let(:
|
57
|
+
let(:worker_options) { { key: "bar" } }
|
73
58
|
it "returns the string" do
|
74
59
|
expect(described_class.merge_key(worker_class, args)).to eq "bar"
|
75
60
|
end
|
76
61
|
end
|
77
62
|
context "other type key" do
|
78
|
-
let(:
|
63
|
+
let(:worker_options) { { key: [1, 2, 3] } }
|
79
64
|
it "returns nil" do
|
80
65
|
expect(described_class.merge_key(worker_class, args)).to eq "[1,2,3]"
|
81
66
|
end
|
82
67
|
end
|
83
68
|
context "proc key" do
|
84
69
|
let(:args) { [1, 2, 3] }
|
85
|
-
let(:
|
70
|
+
let(:worker_options) { { key: -> (args) { args[0].to_s } } }
|
86
71
|
it "returns the result of the proc" do
|
87
72
|
expect(described_class.merge_key(worker_class, args)).to eq "1"
|
88
73
|
end
|
89
74
|
context "non-string result" do
|
90
|
-
let(:
|
75
|
+
let(:worker_options) { { key: -> (args) { args[0] } } }
|
91
76
|
it "returns nil" do
|
92
77
|
expect(described_class.merge_key(worker_class, args)).to eq "1"
|
93
78
|
end
|
@@ -97,13 +82,13 @@ describe Sidekiq::Merger::Merge do
|
|
97
82
|
|
98
83
|
describe "#add" do
|
99
84
|
it "adds the args in lazy merge" do
|
100
|
-
expect(redis).to receive(:push_message).with("
|
85
|
+
expect(redis).to receive(:push_message).with("some_worker:queue:foo", [1, 2, 3], execution_time)
|
101
86
|
subject.add([1, 2, 3], execution_time)
|
102
87
|
end
|
103
88
|
context "with unique option" do
|
104
|
-
let(:
|
89
|
+
let(:worker_options) { { key: -> (args) { args.to_json }, unique: true } }
|
105
90
|
it "adds the args in lazy merge" do
|
106
|
-
expect(redis).to receive(:push_message).with("
|
91
|
+
expect(redis).to receive(:push_message).with("some_worker:queue:foo", [1, 2, 3], execution_time)
|
107
92
|
subject.add([1, 2, 3], execution_time)
|
108
93
|
end
|
109
94
|
context "the args has alredy been added" do
|
@@ -118,7 +103,7 @@ describe Sidekiq::Merger::Merge do
|
|
118
103
|
|
119
104
|
describe "#delete" do
|
120
105
|
it "adds the args in lazy merge" do
|
121
|
-
expect(redis).to receive(:delete_message).with("
|
106
|
+
expect(redis).to receive(:delete_message).with("some_worker:queue:foo", [1, 2, 3])
|
122
107
|
subject.delete([1, 2, 3])
|
123
108
|
end
|
124
109
|
end
|
@@ -195,7 +180,7 @@ describe Sidekiq::Merger::Merge do
|
|
195
180
|
|
196
181
|
describe "#full_merge_key" do
|
197
182
|
it "returns full merge key" do
|
198
|
-
expect(subject.full_merge_key).to eq "
|
183
|
+
expect(subject.full_merge_key).to eq "some_worker:queue:foo"
|
199
184
|
end
|
200
185
|
end
|
201
186
|
end
|
@@ -1,28 +1,11 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
-
describe Sidekiq::Merger::Middleware do
|
3
|
+
describe Sidekiq::Merger::Middleware, worker_class: true do
|
4
4
|
subject { described_class.new }
|
5
5
|
let(:flusher) { Sidekiq::Merger::Flusher.new(Sidekiq.logger) }
|
6
6
|
let(:queue) { "queue" }
|
7
7
|
let(:now) { Time.now }
|
8
|
-
let(:options) { { key: -> (args) { "key" } } }
|
9
|
-
let(:worker_class) do
|
10
|
-
local_options = options
|
11
|
-
Class.new do
|
12
|
-
include Sidekiq::Worker
|
13
|
-
|
14
|
-
sidekiq_options merger: local_options
|
15
|
-
|
16
|
-
def self.name
|
17
|
-
"name"
|
18
|
-
end
|
19
|
-
|
20
|
-
def perform(*args)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
8
|
before :example do
|
25
|
-
allow(Object).to receive(:const_get).with("Name").and_return worker_class
|
26
9
|
Timecop.freeze(now)
|
27
10
|
end
|
28
11
|
|
data/spec/sidekiq/merger_spec.rb
CHANGED
@@ -4,11 +4,26 @@ describe Sidekiq::Merger do
|
|
4
4
|
it "has a version number" do
|
5
5
|
expect(described_class::VERSION).not_to be nil
|
6
6
|
end
|
7
|
-
describe "
|
7
|
+
describe ".create_task" do
|
8
8
|
it "starts a monitoring task" do
|
9
9
|
task = described_class.create_task
|
10
10
|
expect(task).to be_a Concurrent::TimerTask
|
11
11
|
task.shutdown
|
12
12
|
end
|
13
13
|
end
|
14
|
+
describe ".configure" do
|
15
|
+
it "yields to the config" do
|
16
|
+
expect { |b| described_class.configure(&b) }.to yield_with_args(described_class.config)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
describe ".config" do
|
20
|
+
it "returns a config" do
|
21
|
+
expect(described_class.config).to be_a Sidekiq::Merger::Config
|
22
|
+
end
|
23
|
+
context "called twice" do
|
24
|
+
it "returns the same config instance" do
|
25
|
+
expect(described_class.config).to be described_class.config
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
14
29
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -21,31 +21,15 @@ require "sidekiq/merger"
|
|
21
21
|
Dir[File.join(__dir__, "support", "**", "*.rb")].each { |f| require f }
|
22
22
|
|
23
23
|
RSpec.configure do |config|
|
24
|
-
# rspec-expectations config goes here. You can use an alternate
|
25
|
-
# assertion/expectation library such as wrong or the stdlib/minitest
|
26
|
-
# assertions if you prefer.
|
27
24
|
config.expect_with :rspec do |expectations|
|
28
|
-
# This option will default to `true` in RSpec 4. It makes the `description`
|
29
|
-
# and `failure_message` of custom matchers include text for helper methods
|
30
|
-
# defined using `chain`, e.g.:
|
31
|
-
# be_bigger_than(2).and_smaller_than(4).description
|
32
|
-
# # => "be bigger than 2 and smaller than 4"
|
33
|
-
# ...rather than:
|
34
|
-
# # => "be bigger than 2"
|
35
25
|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
36
26
|
end
|
37
27
|
|
38
28
|
config.mock_with :rspec do |mocks|
|
39
|
-
# Prevents you from mocking or stubbing a method that does not exist on
|
40
|
-
# a real object. This is generally recommended, and will default to
|
41
|
-
# `true` in RSpec 4.
|
42
29
|
mocks.verify_partial_doubles = true
|
43
30
|
end
|
44
31
|
|
45
32
|
if config.files_to_run.one?
|
46
|
-
# Use the documentation formatter for detailed output,
|
47
|
-
# unless a formatter has already been configured
|
48
|
-
# (e.g. via a command-line flag).
|
49
33
|
config.default_formatter = "doc"
|
50
34
|
end
|
51
35
|
|
@@ -59,9 +43,12 @@ RSpec.configure do |config|
|
|
59
43
|
Sidekiq.logger = nil
|
60
44
|
end
|
61
45
|
|
62
|
-
config.
|
63
|
-
Sidekiq::Merger::Redis.redis
|
64
|
-
|
46
|
+
config.around :example do |example|
|
47
|
+
Sidekiq::Merger::Redis.redis { |conn| conn.flushall }
|
48
|
+
begin
|
49
|
+
example.run
|
50
|
+
ensure
|
51
|
+
Sidekiq::Merger::Redis.redis { |conn| conn.flushall }
|
65
52
|
end
|
66
53
|
end
|
67
54
|
|
@@ -0,0 +1,34 @@
|
|
1
|
+
RSpec.shared_context "worker class", worker_class: true do
|
2
|
+
let(:worker_options) { { key: -> (args) { "key" } } }
|
3
|
+
let(:worker_class) do
|
4
|
+
local_options = worker_options
|
5
|
+
Class.new do
|
6
|
+
include Sidekiq::Worker
|
7
|
+
|
8
|
+
sidekiq_options merger: local_options
|
9
|
+
|
10
|
+
def self.name
|
11
|
+
"SomeWorker"
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.to_s
|
15
|
+
"SomeWorker"
|
16
|
+
end
|
17
|
+
|
18
|
+
def perform(*args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
before :example do
|
23
|
+
allow(Object).to receive(:const_get).with(anything).and_call_original
|
24
|
+
allow(Object).to receive(:const_get).with("SomeWorker").and_return worker_class
|
25
|
+
end
|
26
|
+
around :example do |example|
|
27
|
+
worker_class.jobs.clear
|
28
|
+
begin
|
29
|
+
example.run
|
30
|
+
ensure
|
31
|
+
worker_class.jobs.clear
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: sidekiq-merger
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- dtaniwaki
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-02-
|
11
|
+
date: 2017-02-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake
|
@@ -167,6 +167,7 @@ executables: []
|
|
167
167
|
extensions: []
|
168
168
|
extra_rdoc_files: []
|
169
169
|
files:
|
170
|
+
- ".codeclimate.yml"
|
170
171
|
- ".dockerignore"
|
171
172
|
- ".gemrelease"
|
172
173
|
- ".gitignore"
|
@@ -199,6 +200,8 @@ files:
|
|
199
200
|
- lib/sidekiq/merger/version.rb
|
200
201
|
- lib/sidekiq/merger/views/index.erb
|
201
202
|
- lib/sidekiq/merger/web.rb
|
203
|
+
- misc/bulk_notification_flow.png
|
204
|
+
- misc/cancel_task_flow.png
|
202
205
|
- misc/web_ui.png
|
203
206
|
- sidekiq-merger.gemspec
|
204
207
|
- spec/sidekiq/merger/flusher_spec.rb
|
@@ -209,6 +212,7 @@ files:
|
|
209
212
|
- spec/sidekiq/merger_spec.rb
|
210
213
|
- spec/spec_helper.rb
|
211
214
|
- spec/support/matchers.rb
|
215
|
+
- spec/support/worker_class.rb
|
212
216
|
homepage: https://github.com/dtaniwaki/sidekiq-merger
|
213
217
|
licenses:
|
214
218
|
- MIT
|
@@ -245,3 +249,4 @@ test_files:
|
|
245
249
|
- spec/sidekiq/merger_spec.rb
|
246
250
|
- spec/spec_helper.rb
|
247
251
|
- spec/support/matchers.rb
|
252
|
+
- spec/support/worker_class.rb
|