resque-integration 3.5.1 → 3.7.1
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/.drone.yml +65 -19
- data/Appraisals +12 -9
- data/CHANGELOG.md +22 -2
- data/Gemfile +2 -0
- data/dip.yml +3 -2
- data/docker-compose.yml +7 -0
- data/lib/resque/integration/ordered.rb +27 -0
- data/lib/resque/integration/tasks/resque.rake +1 -1
- data/lib/resque/integration/version.rb +1 -1
- data/resque-integration.gemspec +0 -1
- data/spec/resque/integration/ordered_spec.rb +57 -0
- data/spec/resque/integration_spec.rb +30 -15
- data/spec/spec_helper.rb +3 -2
- metadata +2 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 412dbc6bf78abf735849d6df4910d260fa75be50
|
|
4
|
+
data.tar.gz: 23b9cab284dee3fdb8ecec03d80a3553d7c0263b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: bfd8b6370f0b85c13527ed1e52d96dea2a6def920c88721c0230f099b8298c994aa5b121cd5e7eacaa158759388930fac8f48131124526bbaeef82c0b83d4e0c
|
|
7
|
+
data.tar.gz: bcf2010e8a02e6499cd0f7e200f54dcee6abec5f9775847071341a37eb60ef73221c0d64311d3d67719f5a361c73a52deb441bf7b2ab76817e1e283c896f7a3a
|
data/.drone.yml
CHANGED
|
@@ -1,28 +1,74 @@
|
|
|
1
|
-
build
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
1
|
+
name: build
|
|
2
|
+
|
|
3
|
+
kind: pipeline
|
|
4
|
+
type: docker
|
|
5
|
+
|
|
6
|
+
volumes:
|
|
7
|
+
- name: images
|
|
8
|
+
host:
|
|
9
|
+
path: /home/data/drone/images
|
|
10
|
+
- name: bundle
|
|
11
|
+
host:
|
|
12
|
+
path: /home/data/drone/gems
|
|
13
|
+
- name: rubygems
|
|
14
|
+
host:
|
|
15
|
+
path: /home/data/drone/rubygems
|
|
16
|
+
|
|
17
|
+
spec_step_common: &spec_step_common
|
|
18
|
+
image: abakpress/dind-testing:1.0.3
|
|
19
|
+
pull: if-not-exists
|
|
20
|
+
privileged: true
|
|
21
|
+
volumes:
|
|
22
|
+
- name: images
|
|
23
|
+
path: /images
|
|
24
|
+
- name: bundle
|
|
25
|
+
path: /bundle
|
|
26
|
+
commands:
|
|
27
|
+
- prepare-build
|
|
28
|
+
|
|
29
|
+
- fetch-images
|
|
30
|
+
--image abakpress/ruby-app:$RUBY_IMAGE_TAG
|
|
31
|
+
--image redis:$REDIS_IMAGE_TAG
|
|
32
|
+
- dip provision
|
|
33
|
+
- dip rspec
|
|
34
|
+
|
|
35
|
+
steps:
|
|
36
|
+
- name: Tests Ruby 2.2
|
|
9
37
|
environment:
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
38
|
+
COMPOSE_FILE_EXT: drone
|
|
39
|
+
DOCKER_RUBY_VERSION: 2.2
|
|
40
|
+
RUBY_IMAGE_TAG: 2.2-latest
|
|
41
|
+
REDIS_IMAGE_TAG: 4-alpine
|
|
42
|
+
RAILS_ENV: test
|
|
43
|
+
<<: *spec_step_common
|
|
14
44
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
45
|
+
- name: Tests Ruby 2.3
|
|
46
|
+
environment:
|
|
47
|
+
COMPOSE_FILE_EXT: drone
|
|
48
|
+
DOCKER_RUBY_VERSION: 2.3
|
|
49
|
+
RUBY_IMAGE_TAG: 2.3-latest
|
|
50
|
+
REDIS_IMAGE_TAG: 4-alpine
|
|
51
|
+
RAILS_ENV: test
|
|
52
|
+
<<: *spec_step_common
|
|
53
|
+
|
|
54
|
+
- name: Tests Ruby 2.4
|
|
55
|
+
environment:
|
|
56
|
+
COMPOSE_FILE_EXT: drone
|
|
57
|
+
DOCKER_RUBY_VERSION: 2.4
|
|
58
|
+
RUBY_IMAGE_TAG: 2.4-latest
|
|
59
|
+
REDIS_IMAGE_TAG: 4-alpine
|
|
60
|
+
RAILS_ENV: test
|
|
61
|
+
<<: *spec_step_common
|
|
18
62
|
|
|
19
|
-
release
|
|
20
|
-
image: abakpress/gem-publication
|
|
21
|
-
pull:
|
|
63
|
+
- name: release
|
|
64
|
+
image: abakpress/gem-publication:latest
|
|
65
|
+
pull: if-not-exists
|
|
22
66
|
when:
|
|
23
67
|
event: push
|
|
24
68
|
branch: master
|
|
69
|
+
status: success
|
|
25
70
|
volumes:
|
|
26
|
-
-
|
|
71
|
+
- name: rubygems
|
|
72
|
+
path: /root/.gem
|
|
27
73
|
commands:
|
|
28
74
|
- release-gem --public
|
data/Appraisals
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
if RUBY_VERSION < '2.4'
|
|
2
|
+
appraise 'rails3.2' do
|
|
3
|
+
gem 'rails', '~> 3.2.0'
|
|
4
|
+
end
|
|
4
5
|
|
|
5
|
-
appraise 'rails4.0' do
|
|
6
|
-
|
|
7
|
-
end
|
|
6
|
+
appraise 'rails4.0' do
|
|
7
|
+
gem 'rails', '~> 4.0.13'
|
|
8
|
+
end
|
|
8
9
|
|
|
9
|
-
appraise 'rails4.1' do
|
|
10
|
-
|
|
10
|
+
appraise 'rails4.1' do
|
|
11
|
+
gem 'rails', '~> 4.1.16'
|
|
12
|
+
end
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
appraise 'rails4.2' do
|
|
@@ -23,5 +25,6 @@ appraise 'rails5.1' do
|
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
appraise 'rails5.2' do
|
|
26
|
-
gem 'rails', '~> 5.2.0'
|
|
28
|
+
gem 'rails', '~> 5.2.0', '< 5.2.4.1'
|
|
29
|
+
gem 'mimemagic', '<= 0.3.9' if RUBY_VERSION < '2.3'
|
|
27
30
|
end
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
|
-
# v3.
|
|
2
|
-
|
|
1
|
+
# v3.7.1
|
|
2
|
+
|
|
3
|
+
* 2021-07-22 [0caf11c](../../commit/0caf11c) - __(TamarinEA)__ chore: fetch redis image and add rubygems volume
|
|
4
|
+
* 2021-07-21 [7b94d0e](../../commit/7b94d0e) - __(TamarinEA)__ chore: release public gem
|
|
5
|
+
* 2021-07-20 [c51d9ab](../../commit/c51d9ab) - __(TamarinEA)__ chore: lock mimemagic for ruby 2.2
|
|
6
|
+
* 2021-07-20 [843c787](../../commit/843c787) - __(TamarinEA)__ Release 3.7.1
|
|
7
|
+
* 2021-07-20 [933115d](../../commit/933115d) - __(TamarinEA)__ chore: use redis image istead of mock
|
|
8
|
+
* 2021-07-20 [21aa6ba](../../commit/21aa6ba) - __(TamarinEA)__ chore: add ruby 2.4 support
|
|
9
|
+
* 2020-10-05 [05e046b](../../commit/05e046b) - __(TamarinEA)__ Relesae 3.7.0
|
|
10
|
+
* 2020-10-05 [6f12054](../../commit/6f12054) - __(Andrew N. Shalaev)__ Release v3.6.0
|
|
11
|
+
* 2020-09-04 [ee6104e](../../commit/ee6104e) - __(Zhidkov Denis)__ fix: change resque restart command to avoid unwatched workers appearance
|
|
12
|
+
https://jira.railsc.ru/browse/BPC-17334
|
|
13
|
+
|
|
14
|
+
* 2020-08-30 [7676652](../../commit/7676652) - __(TamarinEA)__ feature: add ordered queue check
|
|
15
|
+
https://jira.railsc.ru/browse/GOODS-2471
|
|
16
|
+
|
|
17
|
+
* 2020-04-20 [d36e4b2](../../commit/d36e4b2) - __(TamarinEA)__ chore: use drone 1.6
|
|
18
|
+
* 2020-04-19 [e33faa6](../../commit/e33faa6) - __(TamarinEA)__ Release 3.5.2
|
|
19
|
+
* 2020-04-16 [35eafa2](../../commit/35eafa2) - __(TamarinEA)__ fix: reload ordered meta before save
|
|
20
|
+
https://jira.railsc.ru/browse/GOODS-2326
|
|
21
|
+
|
|
22
|
+
* 2020-04-16 [0c53eef](../../commit/0c53eef) - __(TamarinEA)__ chore: lock gems for support ruby 2.2
|
|
3
23
|
* 2018-12-21 [c2b890d](../../commit/c2b890d) - __(Andrew N. Shalaev)__ Release v3.5.1
|
|
4
24
|
* 2018-12-21 [d7478c2](../../commit/d7478c2) - __(Andrew N. Shalaev)__ fix: add support for redis >= v4
|
|
5
25
|
* 2018-09-06 [49717a5](../../commit/49717a5) - __(Mikhail Nelaev)__ Release 3.5.0
|
data/Gemfile
CHANGED
data/dip.yml
CHANGED
data/docker-compose.yml
CHANGED
|
@@ -5,6 +5,13 @@ services:
|
|
|
5
5
|
image: abakpress/ruby-app:$RUBY_IMAGE_TAG
|
|
6
6
|
environment:
|
|
7
7
|
- BUNDLE_PATH=/bundle/$DOCKER_RUBY_VERSION
|
|
8
|
+
- TEST_REDIS_HOST=redis
|
|
8
9
|
command: bash
|
|
9
10
|
volumes:
|
|
10
11
|
- .:/app
|
|
12
|
+
depends_on:
|
|
13
|
+
- redis
|
|
14
|
+
|
|
15
|
+
redis:
|
|
16
|
+
image: redis:$REDIS_IMAGE_TAG
|
|
17
|
+
command: 'redis-server --bind 0.0.0.0'
|
|
@@ -112,12 +112,14 @@ module Resque
|
|
|
112
112
|
begin
|
|
113
113
|
execute(ordered_meta, *job_args)
|
|
114
114
|
rescue Exception
|
|
115
|
+
ordered_meta.reload!
|
|
115
116
|
ordered_meta.fail!
|
|
116
117
|
raise
|
|
117
118
|
ensure
|
|
118
119
|
uniqueness.remove(meta_id, job_args) if uniqueness
|
|
119
120
|
end
|
|
120
121
|
|
|
122
|
+
ordered_meta.reload!
|
|
121
123
|
ordered_meta.finish!
|
|
122
124
|
|
|
123
125
|
i += 1
|
|
@@ -137,6 +139,31 @@ module Resque
|
|
|
137
139
|
def ordered_meta_id(args)
|
|
138
140
|
Digest::SHA1.hexdigest([Time.now.to_f, rand, self, args].join)
|
|
139
141
|
end
|
|
142
|
+
|
|
143
|
+
def in_ordered_queue?(*args)
|
|
144
|
+
meta = enqueued?(*args)
|
|
145
|
+
return false unless meta
|
|
146
|
+
|
|
147
|
+
decoded_args = Resque.decode(Resque.encode(args))
|
|
148
|
+
|
|
149
|
+
args_key = ordered_queue_key(meta.meta_id)
|
|
150
|
+
|
|
151
|
+
args_meta_id = nil
|
|
152
|
+
|
|
153
|
+
::Resque.redis.lrange(args_key, 0, -1).each do |job_args|
|
|
154
|
+
job_args = ::Resque.decode(job_args)
|
|
155
|
+
meta_id = job_args.shift
|
|
156
|
+
|
|
157
|
+
if job_args == decoded_args
|
|
158
|
+
args_meta_id = meta_id
|
|
159
|
+
break
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
return false unless args_meta_id
|
|
164
|
+
|
|
165
|
+
get_meta(args_meta_id)
|
|
166
|
+
end
|
|
140
167
|
end
|
|
141
168
|
end
|
|
142
169
|
end
|
|
@@ -20,7 +20,7 @@ namespace :resque do
|
|
|
20
20
|
if god_stopped?
|
|
21
21
|
Rake::Task['resque:start'].invoke
|
|
22
22
|
else
|
|
23
|
-
puts `#{god} load #{Resque.config.config_file} stop && #{god}
|
|
23
|
+
puts `#{god} stop resque && #{god} load #{Resque.config.config_file} stop && #{god} start resque`
|
|
24
24
|
end
|
|
25
25
|
end
|
|
26
26
|
|
data/resque-integration.gemspec
CHANGED
|
@@ -34,7 +34,6 @@ Gem::Specification.new do |gem|
|
|
|
34
34
|
gem.add_development_dependency 'simplecov'
|
|
35
35
|
gem.add_development_dependency 'appraisal', '>= 1.0.2'
|
|
36
36
|
gem.add_development_dependency 'combustion', '>= 0.5.5'
|
|
37
|
-
gem.add_development_dependency 'mock_redis'
|
|
38
37
|
gem.add_development_dependency 'timecop'
|
|
39
38
|
gem.add_development_dependency 'pry-byebug'
|
|
40
39
|
end
|
|
@@ -6,6 +6,15 @@ describe Resque::Integration::Ordered do
|
|
|
6
6
|
|
|
7
7
|
unique { |company_id, param1| [company_id] }
|
|
8
8
|
ordered max_iterations: 2
|
|
9
|
+
|
|
10
|
+
def self.execute(ordered_meta, arg1, arg2)
|
|
11
|
+
old_meta = @meta_id
|
|
12
|
+
@meta_id = ordered_meta.meta_id
|
|
13
|
+
|
|
14
|
+
at(arg1, arg2, 'some message')
|
|
15
|
+
|
|
16
|
+
@meta_id = old_meta
|
|
17
|
+
end
|
|
9
18
|
end
|
|
10
19
|
|
|
11
20
|
class UniqueTestJob
|
|
@@ -62,6 +71,16 @@ describe Resque::Integration::Ordered do
|
|
|
62
71
|
expect(TestJob.ordered_queue_size(meta_id)).to eq 2
|
|
63
72
|
end
|
|
64
73
|
|
|
74
|
+
it 'save ordered meta' do
|
|
75
|
+
ordered_meta_id = TestJob.enqueue(1, 10).meta_id
|
|
76
|
+
meta_id = TestJob.meta_id(1, 10)
|
|
77
|
+
TestJob.perform(meta_id)
|
|
78
|
+
ordered_meta = TestJob.get_meta(ordered_meta_id)
|
|
79
|
+
|
|
80
|
+
expect(ordered_meta).to be_finished
|
|
81
|
+
expect(ordered_meta.progress).to eq(num: 1, total: 10, percent: 10, message: 'some message')
|
|
82
|
+
end
|
|
83
|
+
|
|
65
84
|
context 'uniqueness' do
|
|
66
85
|
it 'perform with unique args only once' do
|
|
67
86
|
UniqueTestJob.enqueue(1, 10)
|
|
@@ -84,4 +103,42 @@ describe Resque::Integration::Ordered do
|
|
|
84
103
|
expect(meta.meta_id).to_not eq UniqueTestJob.enqueue(1, 20).meta_id
|
|
85
104
|
end
|
|
86
105
|
end
|
|
106
|
+
|
|
107
|
+
describe '#in_ordered_queue?' do
|
|
108
|
+
before do
|
|
109
|
+
allow(TestJob).to receive(:rand).and_return(123)
|
|
110
|
+
|
|
111
|
+
Timecop.freeze
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
it do
|
|
115
|
+
TestJob.enqueue(1, 10)
|
|
116
|
+
TestJob.enqueue(1, 20)
|
|
117
|
+
|
|
118
|
+
expect(TestJob.in_ordered_queue?(1, 10)).to be_instance_of(Resque::Plugins::Meta::Metadata)
|
|
119
|
+
expect(TestJob.in_ordered_queue?(1, 10).meta_id).to eq TestJob.ordered_meta_id([1, 10])
|
|
120
|
+
expect(TestJob.in_ordered_queue?(1, 20)).to be_instance_of(Resque::Plugins::Meta::Metadata)
|
|
121
|
+
expect(TestJob.in_ordered_queue?(1, 20).meta_id).to eq TestJob.ordered_meta_id([1, 20])
|
|
122
|
+
expect(TestJob.in_ordered_queue?(1, 30)).to be_falsey
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
context 'when some complex arg' do
|
|
126
|
+
let(:complex_arg) { [Integer, {a: 1, 'b' => '2'}, 10] }
|
|
127
|
+
|
|
128
|
+
it do
|
|
129
|
+
expect(TestJob.in_ordered_queue?(1, complex_arg)).to be_falsey
|
|
130
|
+
|
|
131
|
+
TestJob.enqueue(1, complex_arg)
|
|
132
|
+
|
|
133
|
+
expect(TestJob.in_ordered_queue?(1, complex_arg)).to be_instance_of(Resque::Plugins::Meta::Metadata)
|
|
134
|
+
expect(TestJob.in_ordered_queue?(1, complex_arg).meta_id).to eq TestJob.ordered_meta_id([1, complex_arg])
|
|
135
|
+
|
|
136
|
+
TestJob.perform(TestJob.meta_id(1, complex_arg))
|
|
137
|
+
|
|
138
|
+
expect(TestJob.in_ordered_queue?(1, complex_arg)).to be_falsey
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
after { Timecop.return }
|
|
143
|
+
end
|
|
87
144
|
end
|
|
@@ -37,34 +37,49 @@ RSpec.describe Resque::Integration do
|
|
|
37
37
|
end
|
|
38
38
|
end
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
40
|
+
let(:redis) { Resque.redis }
|
|
41
|
+
let(:travel_redis) do
|
|
42
|
+
->(time) do
|
|
43
|
+
redis.keys.each do |key|
|
|
44
|
+
ttl = redis.ttl(key)
|
|
45
|
+
next if ttl <= 0
|
|
46
|
+
|
|
47
|
+
redis.expire(key, ttl - time.to_i)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
42
51
|
|
|
43
|
-
|
|
52
|
+
context 'when enqueues only one job' do
|
|
53
|
+
before do
|
|
44
54
|
UniqueJob.enqueue(1, param: 'one')
|
|
45
55
|
|
|
46
|
-
|
|
56
|
+
travel_redis.call(10.hours)
|
|
57
|
+
|
|
58
|
+
UniqueJob.enqueue(1, param: 'one')
|
|
47
59
|
end
|
|
48
|
-
end
|
|
49
60
|
|
|
50
|
-
|
|
51
|
-
|
|
61
|
+
it { expect(Resque.peek(:test, 0, 100).size).to eq(1) }
|
|
62
|
+
end
|
|
52
63
|
|
|
53
|
-
|
|
64
|
+
context 'when enqueues two jobs with differ args' do
|
|
65
|
+
before do
|
|
66
|
+
UniqueJob.enqueue(1, param: 'one')
|
|
54
67
|
UniqueJob.enqueue(1, param: 'two')
|
|
55
|
-
|
|
56
|
-
expect(Resque.peek(:test, 0, 100).size).to eq(2)
|
|
57
68
|
end
|
|
58
|
-
end
|
|
59
69
|
|
|
60
|
-
|
|
61
|
-
|
|
70
|
+
it { expect(Resque.peek(:test, 0, 100).size).to eq(2) }
|
|
71
|
+
end
|
|
62
72
|
|
|
63
|
-
|
|
73
|
+
context 'when enqueues two jobs after expire lock timeout' do
|
|
74
|
+
before do
|
|
64
75
|
UniqueJob.enqueue(1, param: 'one')
|
|
65
76
|
|
|
66
|
-
|
|
77
|
+
travel_redis.call(4.days)
|
|
78
|
+
|
|
79
|
+
UniqueJob.enqueue(1, param: 'one')
|
|
67
80
|
end
|
|
81
|
+
|
|
82
|
+
it { expect(Resque.peek(:test, 0, 100).size).to eq(2) }
|
|
68
83
|
end
|
|
69
84
|
|
|
70
85
|
describe 'unlock' do
|
data/spec/spec_helper.rb
CHANGED
|
@@ -4,12 +4,13 @@ require 'rspec'
|
|
|
4
4
|
require 'rspec/its'
|
|
5
5
|
require 'resque'
|
|
6
6
|
require 'simplecov'
|
|
7
|
-
require 'mock_redis'
|
|
8
7
|
require 'timecop'
|
|
9
8
|
require 'pry-byebug'
|
|
10
9
|
require 'combustion'
|
|
11
10
|
|
|
12
|
-
|
|
11
|
+
redis = Redis.new(host: ENV['TEST_REDIS_HOST'])
|
|
12
|
+
Redis.current = redis
|
|
13
|
+
Resque.redis = redis
|
|
13
14
|
|
|
14
15
|
SimpleCov.start
|
|
15
16
|
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: resque-integration
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.7.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alexei Mikhailov
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2021-07-22 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: resque
|
|
@@ -249,20 +249,6 @@ dependencies:
|
|
|
249
249
|
- - ">="
|
|
250
250
|
- !ruby/object:Gem::Version
|
|
251
251
|
version: 0.5.5
|
|
252
|
-
- !ruby/object:Gem::Dependency
|
|
253
|
-
name: mock_redis
|
|
254
|
-
requirement: !ruby/object:Gem::Requirement
|
|
255
|
-
requirements:
|
|
256
|
-
- - ">="
|
|
257
|
-
- !ruby/object:Gem::Version
|
|
258
|
-
version: '0'
|
|
259
|
-
type: :development
|
|
260
|
-
prerelease: false
|
|
261
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
262
|
-
requirements:
|
|
263
|
-
- - ">="
|
|
264
|
-
- !ruby/object:Gem::Version
|
|
265
|
-
version: '0'
|
|
266
252
|
- !ruby/object:Gem::Dependency
|
|
267
253
|
name: timecop
|
|
268
254
|
requirement: !ruby/object:Gem::Requirement
|