fastly_nsq 0.4.0 → 0.5.0
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/README.md +2 -2
- data/fastly_nsq.gemspec +1 -1
- data/lib/fastly_nsq/fake_message_queue.rb +17 -5
- data/lib/fastly_nsq/message_queue/consumer.rb +10 -10
- data/lib/fastly_nsq/message_queue/listener.rb +8 -9
- data/lib/fastly_nsq/version.rb +1 -1
- data/spec/lib/fastly_nsq/fake_message_queue_spec.rb +38 -48
- data/spec/lib/fastly_nsq/message_queue/consumer_spec.rb +42 -64
- data/spec/lib/fastly_nsq/message_queue/listener_spec.rb +66 -54
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b927006ea75538d36b77a9988c99f159327e72c
|
4
|
+
data.tar.gz: 0ba8e537719bcb36721c65292741d779f9d4da4c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: acaa3eb91200476a12e5f4099ef6515803293007f8643d49cd9651d5f786a6c9e2f36f39ff8086cff38a339a2767346ffada9131bb584db7d6155343d08788ed
|
7
|
+
data.tar.gz: 667ebee86054600dbd16e1f0450f51ce0186d759210c4c4c5e3fbac1f1d84e994124c4496cbcfce2ce6281da1398a5e3e0b8c22e45fdf3b1700511081be008bf
|
data/README.md
CHANGED
@@ -29,7 +29,7 @@ Please use [GitHub Issues] to report bugs.
|
|
29
29
|
|
30
30
|
`fastly_nsq` is a Ruby Gem
|
31
31
|
tested against Rails `>= 4.2`
|
32
|
-
and Ruby `>= 2.
|
32
|
+
and Ruby `>= 2.1.8`.
|
33
33
|
|
34
34
|
To get started,
|
35
35
|
add `fastly_nsq` to your `Gemfile`
|
@@ -88,7 +88,7 @@ read messages off of the queue:
|
|
88
88
|
consumer = MessageQueue::Consumer.new(
|
89
89
|
topic: 'topic',
|
90
90
|
channel: 'channel'
|
91
|
-
)
|
91
|
+
)
|
92
92
|
|
93
93
|
consumer.size #=> 1
|
94
94
|
message = consumer.pop
|
data/fastly_nsq.gemspec
CHANGED
@@ -21,7 +21,7 @@ Gem::Specification.new do |gem|
|
|
21
21
|
gem.require_paths = ['lib']
|
22
22
|
|
23
23
|
gem.add_development_dependency 'awesome_print', '~> 1.6'
|
24
|
-
gem.add_development_dependency 'bundler', '~> 1.
|
24
|
+
gem.add_development_dependency 'bundler', '~> 1.12'
|
25
25
|
gem.add_development_dependency 'bundler-audit', '~> 0.5.0'
|
26
26
|
gem.add_development_dependency 'overcommit', '~> 0.32.0'
|
27
27
|
gem.add_development_dependency 'pry-byebug', '~> 3.3'
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module FakeMessageQueue
|
2
2
|
@@logger = Logger.new(nil)
|
3
|
+
@@delay = 0.5
|
4
|
+
@@queue = []
|
3
5
|
|
4
6
|
def self.queue
|
5
7
|
@@queue
|
@@ -21,6 +23,14 @@ module FakeMessageQueue
|
|
21
23
|
@@logger
|
22
24
|
end
|
23
25
|
|
26
|
+
def self.delay
|
27
|
+
@@delay
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.delay=(delay)
|
31
|
+
@@delay = delay
|
32
|
+
end
|
33
|
+
|
24
34
|
class Producer
|
25
35
|
def initialize(nsqd:, topic:, ssl_context: nil)
|
26
36
|
end
|
@@ -42,22 +52,24 @@ module FakeMessageQueue
|
|
42
52
|
end
|
43
53
|
|
44
54
|
class Consumer
|
45
|
-
|
46
|
-
|
47
|
-
def initialize(nsqlookupd:, topic:, channel:, ssl_context: nil)
|
55
|
+
def initialize(nsqlookupd: nil, topic:, channel:, ssl_context: nil)
|
48
56
|
end
|
49
57
|
|
50
|
-
def pop
|
58
|
+
def pop(delay = FakeMessageQueue.delay)
|
51
59
|
message = nil
|
52
60
|
|
53
61
|
until message
|
54
62
|
message = queue.pop
|
55
|
-
sleep
|
63
|
+
sleep delay
|
56
64
|
end
|
57
65
|
|
58
66
|
message
|
59
67
|
end
|
60
68
|
|
69
|
+
def pop_without_blocking
|
70
|
+
queue.pop
|
71
|
+
end
|
72
|
+
|
61
73
|
def size
|
62
74
|
queue.size
|
63
75
|
end
|
@@ -1,27 +1,27 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
|
1
3
|
class InvalidParameterError < StandardError; end
|
2
4
|
|
3
5
|
module MessageQueue
|
4
6
|
class Consumer
|
7
|
+
extend Forwardable
|
8
|
+
def_delegator :connection, :pop
|
9
|
+
def_delegator :connection, :pop_without_blocking
|
10
|
+
def_delegator :connection, :size
|
11
|
+
def_delegator :connection, :terminate
|
12
|
+
|
5
13
|
def initialize(topic:, channel:, ssl_context: nil)
|
6
14
|
@topic = topic
|
7
15
|
@channel = channel
|
8
16
|
@ssl_context = SSLContext.new(ssl_context)
|
9
17
|
end
|
10
18
|
|
11
|
-
def connection
|
12
|
-
@connection ||= consumer.new(params)
|
13
|
-
end
|
14
|
-
|
15
|
-
def terminate
|
16
|
-
@connection.terminate
|
17
|
-
end
|
18
|
-
|
19
19
|
private
|
20
20
|
|
21
21
|
attr_reader :channel, :topic, :ssl_context
|
22
22
|
|
23
|
-
def
|
24
|
-
Strategy.for_queue::Consumer
|
23
|
+
def connection
|
24
|
+
Strategy.for_queue::Consumer.new(params)
|
25
25
|
end
|
26
26
|
|
27
27
|
def params
|
@@ -1,8 +1,10 @@
|
|
1
1
|
module MessageQueue
|
2
2
|
class Listener
|
3
|
-
def initialize(topic:, channel:)
|
4
|
-
@topic
|
5
|
-
@channel
|
3
|
+
def initialize(topic:, channel:, processor: nil, consumer: nil)
|
4
|
+
@topic = topic
|
5
|
+
@channel = channel
|
6
|
+
@processor = processor || DEFAULT_PROCESSOR
|
7
|
+
@consumer = consumer || MessageQueue::Consumer.new(consumer_params)
|
6
8
|
end
|
7
9
|
|
8
10
|
def go
|
@@ -26,18 +28,15 @@ module MessageQueue
|
|
26
28
|
|
27
29
|
private
|
28
30
|
|
29
|
-
attr_reader :channel, :topic
|
31
|
+
attr_reader :channel, :topic, :processor, :consumer
|
32
|
+
DEFAULT_PROCESSOR = ->(body, topic) { MessageProcessor.new(message_body: body, topic: topic).go }
|
30
33
|
|
31
34
|
def process_one_message
|
32
35
|
message = consumer.pop
|
33
|
-
|
36
|
+
processor.call(message.body, topic)
|
34
37
|
message.finish
|
35
38
|
end
|
36
39
|
|
37
|
-
def consumer
|
38
|
-
@consumer ||= MessageQueue::Consumer.new(consumer_params).connection
|
39
|
-
end
|
40
|
-
|
41
40
|
def consumer_params
|
42
41
|
{ topic: topic, channel: channel }
|
43
42
|
end
|
data/lib/fastly_nsq/version.rb
CHANGED
@@ -88,65 +88,55 @@ RSpec.describe FakeMessageQueue::Message do
|
|
88
88
|
end
|
89
89
|
|
90
90
|
RSpec.describe FakeMessageQueue::Consumer do
|
91
|
+
let(:topic) { 'death_star' }
|
92
|
+
let(:channel) { 'star_killer_base' }
|
93
|
+
let(:consumer) { FakeMessageQueue::Consumer.new topic: topic, channel: channel }
|
94
|
+
|
91
95
|
after do
|
92
96
|
FakeMessageQueue.reset!
|
93
97
|
end
|
94
98
|
|
95
|
-
describe '
|
96
|
-
it 'tells you
|
97
|
-
|
98
|
-
|
99
|
-
channel = 'star_killer_base'
|
99
|
+
describe 'when there are no messages on the queue' do
|
100
|
+
it 'tells you there are 0 messages in the queue' do
|
101
|
+
expect(consumer.size).to eq 0
|
102
|
+
end
|
100
103
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
104
|
+
it 'blocks forever (until timeout) from #pop' do
|
105
|
+
FakeMessageQueue.delay = 0.1
|
106
|
+
delay = FakeMessageQueue.delay + 0.1
|
107
|
+
|
108
|
+
expect do
|
109
|
+
Timeout.timeout(delay) do
|
110
|
+
consumer.pop
|
111
|
+
end
|
112
|
+
end.to raise_error(Timeout::Error)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'returns nil from #pop_without_blocking' do
|
116
|
+
popped_message = consumer.pop_without_blocking
|
107
117
|
|
108
|
-
expect(
|
118
|
+
expect(popped_message).to be_nil
|
109
119
|
end
|
110
120
|
end
|
111
121
|
|
112
|
-
describe '
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
channel = 'star_killer_base'
|
119
|
-
|
120
|
-
consumer = FakeMessageQueue::Consumer.new(
|
121
|
-
nsqlookupd: ENV.fetch('NSQLOOKUPD_HTTP_ADDRESS'),
|
122
|
-
topic: topic,
|
123
|
-
channel: channel,
|
124
|
-
)
|
125
|
-
popped_message = consumer.pop
|
126
|
-
|
127
|
-
expect(popped_message).to eq message
|
128
|
-
end
|
122
|
+
describe 'when there is a message on the queue' do
|
123
|
+
let(:message) { FakeMessageQueue::Message.new 'hello' }
|
124
|
+
before { FakeMessageQueue.queue = [message] }
|
125
|
+
|
126
|
+
it 'tells you there are messages in the queue' do
|
127
|
+
expect(consumer.size).to eq 1
|
129
128
|
end
|
130
129
|
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
channel: channel,
|
142
|
-
)
|
143
|
-
|
144
|
-
expect do
|
145
|
-
Timeout.timeout(delay) do
|
146
|
-
consumer.pop
|
147
|
-
end
|
148
|
-
end.to raise_error(Timeout::Error)
|
149
|
-
end
|
130
|
+
it 'returns a message immediately from #pop' do
|
131
|
+
popped_message = consumer.pop
|
132
|
+
|
133
|
+
expect(popped_message).to eq message
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'returns a message immediately from #pop_without_blocking' do
|
137
|
+
popped_message = consumer.pop_without_blocking
|
138
|
+
|
139
|
+
expect(popped_message).to eq message
|
150
140
|
end
|
151
141
|
end
|
152
142
|
|
@@ -1,87 +1,65 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe MessageQueue::Consumer do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
allow(Nsq::Consumer).to receive(:new)
|
8
|
-
topic = 'death_star'
|
9
|
-
channel = 'star_killer_base'
|
4
|
+
let(:channel) { 'star_killer_base' }
|
5
|
+
let(:topic) { 'death_star' }
|
6
|
+
let(:consumer) { MessageQueue::Consumer.new(topic: topic, channel: channel) }
|
10
7
|
|
11
|
-
|
8
|
+
def fake_consumer
|
9
|
+
double 'Consumer', connection: nil, terminate: nil, pop: :popped, size: 0
|
10
|
+
end
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
topic: topic,
|
17
|
-
channel: channel,
|
18
|
-
ssl_context: nil,
|
19
|
-
).at_least(:once)
|
20
|
-
end
|
21
|
-
end
|
12
|
+
describe 'when the ENV is set incorrectly' do
|
13
|
+
it 'raises with a helpful error' do
|
14
|
+
allow(ENV).to receive(:[]).with('FAKE_QUEUE').and_return('taco')
|
22
15
|
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
topic = 'death_star'
|
27
|
-
channel = 'star_killer_base'
|
16
|
+
expect { consumer.terminate }.to raise_error(InvalidParameterError)
|
17
|
+
end
|
18
|
+
end
|
28
19
|
|
29
|
-
|
20
|
+
describe 'when using the real queue', fake_queue: false do
|
21
|
+
before(:example) do
|
22
|
+
@fake_consumer = fake_consumer
|
23
|
+
allow(Nsq::Consumer).to receive(:new).and_return(@fake_consumer)
|
24
|
+
end
|
30
25
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
topic: topic,
|
35
|
-
channel: channel,
|
36
|
-
ssl_context: nil,
|
37
|
-
).at_least(:once)
|
38
|
-
end
|
26
|
+
it 'forwards #pop to Nsq::Consumer' do
|
27
|
+
consumer.pop
|
28
|
+
expect(@fake_consumer).to have_received(:pop)
|
39
29
|
end
|
40
|
-
end
|
41
30
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
channel = 'star_killer_base'
|
31
|
+
it 'forwards #size to Nsq::Consumer' do
|
32
|
+
consumer.size
|
33
|
+
expect(@fake_consumer).to have_received(:size)
|
34
|
+
end
|
47
35
|
|
48
|
-
|
36
|
+
it 'forwards #terminate to Nsq::Consumer' do
|
37
|
+
consumer.terminate
|
49
38
|
|
50
|
-
expect
|
39
|
+
expect(@fake_consumer).to have_received(:terminate)
|
51
40
|
end
|
52
41
|
end
|
53
42
|
|
54
|
-
describe '
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
topic = 'death_star'
|
60
|
-
channel = 'star_killer_base'
|
61
|
-
params = { topic: topic, channel: channel }
|
62
|
-
|
63
|
-
live_consumer = MessageQueue::Consumer.new(params)
|
64
|
-
live_consumer.connection
|
65
|
-
live_consumer.terminate
|
43
|
+
describe 'when using the fake queue', fake_queue: true do
|
44
|
+
before(:example) do
|
45
|
+
@fake_consumer = fake_consumer
|
46
|
+
allow(FakeMessageQueue::Consumer).to receive(:new).and_return(@fake_consumer)
|
47
|
+
end
|
66
48
|
|
67
|
-
|
68
|
-
|
49
|
+
it 'forwards #pop to FakeMessageQueue::Consumer' do
|
50
|
+
consumer.pop
|
51
|
+
expect(@fake_consumer).to have_received(:pop)
|
69
52
|
end
|
70
53
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
topic = 'death_star'
|
76
|
-
channel = 'star_killer_base'
|
77
|
-
params = { topic: topic, channel: channel }
|
54
|
+
it 'forwards #size to FakeMessageQueue::Consumer' do
|
55
|
+
consumer.size
|
56
|
+
expect(@fake_consumer).to have_received(:size)
|
57
|
+
end
|
78
58
|
|
79
|
-
|
80
|
-
|
81
|
-
live_consumer.terminate
|
59
|
+
it 'forwards #terminate to FakeMessageQueue::Consumer' do
|
60
|
+
consumer.terminate
|
82
61
|
|
83
|
-
|
84
|
-
end
|
62
|
+
expect(@fake_consumer).to have_received(:terminate)
|
85
63
|
end
|
86
64
|
end
|
87
65
|
end
|
@@ -1,67 +1,87 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
RSpec.describe MessageQueue::Listener do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
4
|
+
let(:topic) { 'testing_topic' }
|
5
|
+
let(:channel) { 'testing_channel' }
|
6
|
+
let(:consumer) { FakeMessageQueue::Consumer.new topic: topic, channel: channel }
|
7
|
+
|
8
|
+
module TestMessageProcessor
|
9
|
+
@@messages_processed = []
|
10
|
+
Message = Struct.new(:body, :topic) do
|
11
|
+
def finish
|
12
|
+
@did_finish = true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.call(body, topic)
|
17
|
+
@@messages_processed.push Message.new(body, topic)
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.messages_processed
|
21
|
+
@@messages_processed
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.clear
|
25
|
+
@@messages_processed = []
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:listener) do
|
30
|
+
MessageQueue::Listener.new topic: topic,
|
31
|
+
channel: channel,
|
32
|
+
processor: TestMessageProcessor,
|
33
|
+
consumer: consumer
|
34
|
+
end
|
35
|
+
|
36
|
+
let(:message) { TestMessageProcessor::Message.new 'this is message body', topic }
|
37
|
+
let(:messages_processed) { TestMessageProcessor.messages_processed }
|
38
|
+
let(:expected_message) { TestMessageProcessor::Message.new('this is message body', topic) }
|
39
|
+
let(:expected_messages) { [expected_message] }
|
40
|
+
|
41
|
+
describe 'instantiating without a consumer' do
|
42
|
+
it 'instantiates a consumer, passing the topic and channel' do
|
43
|
+
allow(MessageQueue::Consumer).to receive(:new)
|
44
|
+
|
45
|
+
MessageQueue::Listener.new topic: topic,
|
46
|
+
channel: channel,
|
47
|
+
processor: TestMessageProcessor,
|
48
|
+
consumer: nil
|
16
49
|
|
17
50
|
expect(MessageQueue::Consumer).to have_received(:new).
|
18
51
|
with(topic: topic, channel: channel)
|
19
52
|
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe 'when processing next message' do
|
56
|
+
before(:each) { TestMessageProcessor.clear }
|
57
|
+
|
58
|
+
it 'processes the next message' do
|
59
|
+
allow(consumer).to receive(:pop).and_return(message)
|
60
|
+
listener.process_next_message
|
20
61
|
|
21
|
-
|
22
|
-
process_message = double(go: nil)
|
23
|
-
allow(MessageProcessor).to receive(:new).and_return(process_message)
|
24
|
-
message_body = { data: 'value' }.to_json
|
25
|
-
message = double('Message', finish: nil, body: message_body)
|
26
|
-
connection = double('Connection', pop: message, terminate: nil)
|
27
|
-
consumer = double('Consumer', connection: connection)
|
28
|
-
allow(MessageQueue::Consumer).to receive(:new).and_return(consumer)
|
29
|
-
topic = 'testing_topic'
|
30
|
-
channel = 'testing_channel'
|
31
|
-
|
32
|
-
MessageQueue::Listener.new(topic: topic, channel: channel).
|
33
|
-
process_next_message
|
34
|
-
|
35
|
-
expect(MessageProcessor).to have_received(:new).
|
36
|
-
with(topic: topic, message_body: message_body)
|
37
|
-
expect(process_message).to have_received(:go)
|
62
|
+
expect(messages_processed).to eql(expected_messages)
|
38
63
|
end
|
39
64
|
|
40
65
|
it 'finishes the message' do
|
41
|
-
allow(
|
42
|
-
message
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
channel = 'testing_channel'
|
48
|
-
|
49
|
-
MessageQueue::Listener.new(topic: topic, channel: channel).
|
50
|
-
process_next_message
|
51
|
-
|
52
|
-
expect(message).to have_received(:finish)
|
66
|
+
allow(consumer).to receive(:pop).and_return(message)
|
67
|
+
allow(message).to receive(:finish)
|
68
|
+
|
69
|
+
listener.process_next_message
|
70
|
+
|
71
|
+
expect(message).to have_received(:finish).once
|
53
72
|
end
|
54
73
|
|
55
74
|
context 'when using the fake queue and it is empty', fake_queue: true do
|
75
|
+
before do
|
76
|
+
FakeMessageQueue.delay = 0.1
|
77
|
+
end
|
78
|
+
|
56
79
|
it 'blocks on the process for longer than the check cycle' do
|
57
|
-
|
58
|
-
channel = 'testing_channel'
|
59
|
-
delay = FakeMessageQueue::Consumer::SECONDS_BETWEEN_QUEUE_CHECKS + 0.1
|
80
|
+
delay = FakeMessageQueue.delay + 0.1
|
60
81
|
|
61
82
|
expect do
|
62
83
|
Timeout.timeout(delay) do
|
63
|
-
|
64
|
-
process_next_message
|
84
|
+
listener.process_next_message
|
65
85
|
end
|
66
86
|
end.to raise_error(Timeout::Error)
|
67
87
|
end
|
@@ -71,14 +91,6 @@ RSpec.describe MessageQueue::Listener do
|
|
71
91
|
describe '#go' do
|
72
92
|
describe 'when a SIGTERM is received' do
|
73
93
|
it 'closes the consumer connection' do
|
74
|
-
allow(SampleMessageProcessor).to receive_message_chain(:new, :go)
|
75
|
-
message = double(finish: nil, body: nil)
|
76
|
-
connection = double('Connection', pop: message, terminate: nil)
|
77
|
-
consumer = double('Consumer', connection: connection)
|
78
|
-
allow(MessageQueue::Consumer).to receive(:new).and_return(consumer)
|
79
|
-
topic = 'testing_topic'
|
80
|
-
channel = 'testing_channel'
|
81
|
-
|
82
94
|
pid = fork do
|
83
95
|
MessageQueue::Listener.new(topic: topic, channel: channel).go
|
84
96
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fastly_nsq
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tommy O'Neil
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2016-
|
12
|
+
date: 2016-05-10 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: awesome_print
|
@@ -31,14 +31,14 @@ dependencies:
|
|
31
31
|
requirements:
|
32
32
|
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: 1.
|
34
|
+
version: '1.12'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: 1.
|
41
|
+
version: '1.12'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: bundler-audit
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|