backburner 1.4.1 → 1.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/.travis.yml +6 -2
- data/CHANGELOG.md +4 -0
- data/README.md +5 -2
- data/backburner.gemspec +1 -1
- data/lib/backburner/connection.rb +1 -1
- data/lib/backburner/job.rb +23 -7
- data/lib/backburner/logger.rb +3 -3
- data/lib/backburner/version.rb +1 -1
- data/lib/backburner/worker.rb +1 -1
- data/lib/backburner/workers/threading.rb +48 -14
- data/test/fixtures/test_jobs.rb +18 -0
- data/test/job_test.rb +33 -8
- data/test/test_helper.rb +1 -1
- data/test/workers/forking_worker_test.rb +1 -1
- data/test/workers/simple_worker_test.rb +2 -2
- data/test/workers/threading_worker_test.rb +40 -6
- data/test/workers/threads_on_fork_worker_test.rb +1 -1
- metadata +8 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9dfd7f06d2b08cb31ed2cf463506169544e0920d
|
4
|
+
data.tar.gz: 5d7585331fa5ccc082901017e9149d9982cb9de3
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 38f1deb4da6faef87915b8fca99d99209adf76c5059f3478a75883b38a5e7abf89a9c0c9cec439641530f2b3e272362bfb0b8cc3343b9c1f9fcabf256b5bee24
|
7
|
+
data.tar.gz: d881653ea35fa55df59d53c5e104bc6dea13d857cabebacd8ecb5ba8ff845b9e736263ac7d1c386bbd6ff1d062e9abe0137ff8cf995262accd7231c7e4dc5a0b
|
data/.travis.yml
CHANGED
@@ -2,6 +2,11 @@
|
|
2
2
|
rvm:
|
3
3
|
- 1.9.3
|
4
4
|
- 2.0.0
|
5
|
+
- 2.1
|
6
|
+
- 2.2
|
7
|
+
- 2.3
|
8
|
+
- 2.4
|
9
|
+
- 2.5
|
5
10
|
- rbx-2
|
6
11
|
before_install:
|
7
12
|
- curl -L https://github.com/kr/beanstalkd/archive/v1.9.tar.gz | tar xz -C /tmp
|
@@ -11,11 +16,10 @@ before_install:
|
|
11
16
|
- cd $TRAVIS_BUILD_DIR
|
12
17
|
- gem update --system
|
13
18
|
- gem update bundler
|
14
|
-
install:
|
15
|
-
- bundle install
|
16
19
|
matrix:
|
17
20
|
allow_failures:
|
18
21
|
- rvm: rbx-2
|
22
|
+
- rvm: 2.0.0
|
19
23
|
script:
|
20
24
|
- bundle exec rake test
|
21
25
|
gemfile: Gemfile
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Backburner
|
1
|
+
# Backburner [](https://travis-ci.org/nesquena/backburner)
|
2
2
|
|
3
3
|
Backburner is a [beanstalkd](http://kr.github.com/beanstalkd/)-powered job queue that can handle a very high volume of jobs.
|
4
4
|
You create background jobs and place them on multiple work queues to be processed later.
|
@@ -34,7 +34,7 @@ The real question then is... "Why Beanstalk?".
|
|
34
34
|
|
35
35
|
Illya has an excellent blog post
|
36
36
|
[Scalable Work Queues with Beanstalk](http://www.igvita.com/2010/05/20/scalable-work-queues-with-beanstalk/) and
|
37
|
-
Adam Wiggins posted [an excellent comparison](http://adam.
|
37
|
+
Adam Wiggins posted [an excellent comparison](http://adam.herokuapp.com/past/2010/4/24/beanstalk_a_simple_and_fast_queueing_backend/).
|
38
38
|
|
39
39
|
You will quickly see that **beanstalkd** is an underrated but incredible project that is extremely well-suited as a job queue.
|
40
40
|
Significantly better suited for this task than Redis or a database. Beanstalk is a simple,
|
@@ -513,6 +513,9 @@ end
|
|
513
513
|
|
514
514
|
Now all backburner queue errors will appear on airbrake for deeper inspection.
|
515
515
|
|
516
|
+
If you wish to retry a job without logging an error (for example when handling transient issues in a cloud or service oriented environment),
|
517
|
+
simply raise a `Backburner::Job::RetryJob` error.
|
518
|
+
|
516
519
|
### Logging
|
517
520
|
|
518
521
|
Logging in backburner is rather simple. When a job is run, the log records that. When a job
|
data/backburner.gemspec
CHANGED
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
|
|
18
18
|
|
19
19
|
s.add_runtime_dependency 'beaneater', '~> 1.0'
|
20
20
|
s.add_runtime_dependency 'dante', '> 0.1.5'
|
21
|
-
s.add_runtime_dependency 'concurrent-ruby', '~> 1.0.1'
|
21
|
+
s.add_runtime_dependency 'concurrent-ruby', '~> 1.0', '>= 1.0.1'
|
22
22
|
|
23
23
|
s.add_development_dependency 'rake'
|
24
24
|
s.add_development_dependency 'minitest', '3.2.0'
|
@@ -34,7 +34,7 @@ module Backburner
|
|
34
34
|
def connected?
|
35
35
|
begin
|
36
36
|
!!(@beanstalk && @beanstalk.connection && @beanstalk.connection.connection && !@beanstalk.connection.connection.closed?) # Would be nice if beaneater provided a connected? method
|
37
|
-
rescue
|
37
|
+
rescue
|
38
38
|
false
|
39
39
|
end
|
40
40
|
end
|
data/lib/backburner/job.rb
CHANGED
@@ -7,6 +7,7 @@ module Backburner
|
|
7
7
|
class JobTimeout < RuntimeError; end
|
8
8
|
class JobNotFound < RuntimeError; end
|
9
9
|
class JobFormatInvalid < RuntimeError; end
|
10
|
+
class RetryJob < RuntimeError; end
|
10
11
|
|
11
12
|
attr_accessor :task, :body, :name, :args
|
12
13
|
|
@@ -43,10 +44,10 @@ module Backburner
|
|
43
44
|
#
|
44
45
|
def process
|
45
46
|
# Invoke before hook and stop if false
|
46
|
-
res = @hooks.invoke_hook_events(
|
47
|
+
res = @hooks.invoke_hook_events(job_name, :before_perform, *args)
|
47
48
|
return false unless res
|
48
49
|
# Execute the job
|
49
|
-
@hooks.around_hook_events(
|
50
|
+
@hooks.around_hook_events(job_name, :around_perform, *args) do
|
50
51
|
# We subtract one to ensure we timeout before beanstalkd does, except if:
|
51
52
|
# a) ttr == 0, to support never timing out
|
52
53
|
# b) ttr == 1, so that we don't accidentally set it to never time out
|
@@ -56,19 +57,19 @@ module Backburner
|
|
56
57
|
end
|
57
58
|
task.delete
|
58
59
|
# Invoke after perform hook
|
59
|
-
@hooks.invoke_hook_events(
|
60
|
+
@hooks.invoke_hook_events(job_name, :after_perform, *args)
|
60
61
|
rescue => e
|
61
|
-
@hooks.invoke_hook_events(
|
62
|
+
@hooks.invoke_hook_events(job_name, :on_failure, e, *args)
|
62
63
|
raise e
|
63
64
|
end
|
64
65
|
|
65
66
|
def bury
|
66
|
-
@hooks.invoke_hook_events(
|
67
|
+
@hooks.invoke_hook_events(job_name, :on_bury, *args)
|
67
68
|
task.bury
|
68
69
|
end
|
69
70
|
|
70
71
|
def retry(count, delay)
|
71
|
-
@hooks.invoke_hook_events(
|
72
|
+
@hooks.invoke_hook_events(job_name, :on_retry, count, delay, *args)
|
72
73
|
task.release(delay: delay)
|
73
74
|
end
|
74
75
|
|
@@ -80,11 +81,26 @@ module Backburner
|
|
80
81
|
# job_class # => NewsletterSender
|
81
82
|
#
|
82
83
|
def job_class
|
83
|
-
handler =
|
84
|
+
handler = try_job_class
|
84
85
|
raise(JobNotFound, self.name) unless handler
|
85
86
|
handler
|
86
87
|
end
|
87
88
|
|
89
|
+
# Attempts to return a constantized job name, otherwise reverts to the name string
|
90
|
+
#
|
91
|
+
# @example
|
92
|
+
# job_name # => "SomeUnknownJob"
|
93
|
+
def job_name
|
94
|
+
handler = try_job_class
|
95
|
+
handler ? handler : self.name
|
96
|
+
end
|
97
|
+
|
98
|
+
def try_job_class
|
99
|
+
constantize(self.name)
|
100
|
+
rescue NameError
|
101
|
+
nil
|
102
|
+
end
|
103
|
+
|
88
104
|
# Timeout job within specified block after given time.
|
89
105
|
#
|
90
106
|
# @example
|
data/lib/backburner/logger.rb
CHANGED
@@ -10,7 +10,7 @@ module Backburner
|
|
10
10
|
# Print out when a job is about to begin
|
11
11
|
def log_job_begin(name, args)
|
12
12
|
log_info "Work job #{name} with #{args.inspect}"
|
13
|
-
|
13
|
+
Thread.current[:job_started_at] = Time.now
|
14
14
|
end
|
15
15
|
|
16
16
|
# Print out when a job completed
|
@@ -24,7 +24,7 @@ module Backburner
|
|
24
24
|
|
25
25
|
# Returns true if the job logging started
|
26
26
|
def job_started_at
|
27
|
-
|
27
|
+
Thread.current[:job_started_at]
|
28
28
|
end
|
29
29
|
|
30
30
|
# Print a message to stdout
|
@@ -50,4 +50,4 @@ module Backburner
|
|
50
50
|
Backburner.configuration.logger
|
51
51
|
end
|
52
52
|
end
|
53
|
-
end
|
53
|
+
end
|
data/lib/backburner/version.rb
CHANGED
data/lib/backburner/worker.rb
CHANGED
@@ -140,7 +140,7 @@ module Backburner
|
|
140
140
|
rescue Backburner::Job::JobFormatInvalid => e
|
141
141
|
self.log_error self.exception_message(e)
|
142
142
|
rescue => e # Error occurred processing job
|
143
|
-
self.log_error self.exception_message(e)
|
143
|
+
self.log_error self.exception_message(e) unless e.is_a?(Backburner::Job::RetryJob)
|
144
144
|
|
145
145
|
unless job
|
146
146
|
self.log_error "Error occurred before we were able to assign a job. Giving up without retrying!"
|
@@ -3,9 +3,13 @@ require 'concurrent'
|
|
3
3
|
module Backburner
|
4
4
|
module Workers
|
5
5
|
class Threading < Worker
|
6
|
+
attr_accessor :self_read, :self_write, :exit_on_shutdown
|
7
|
+
|
8
|
+
@shutdown_timeout = 10
|
9
|
+
|
6
10
|
class << self
|
7
|
-
attr_accessor :shutdown
|
8
11
|
attr_accessor :threads_number
|
12
|
+
attr_accessor :shutdown_timeout
|
9
13
|
end
|
10
14
|
|
11
15
|
# Custom initializer just to set @tubes_data
|
@@ -13,6 +17,7 @@ module Backburner
|
|
13
17
|
@tubes_data = {}
|
14
18
|
super
|
15
19
|
self.process_tube_options
|
20
|
+
@exit_on_shutdown = true
|
16
21
|
end
|
17
22
|
|
18
23
|
# Used to prepare job queues before processing jobs.
|
@@ -48,16 +53,19 @@ module Backburner
|
|
48
53
|
connection.on_reconnect = lambda { |conn| conn.tubes.watch!(tube_name) }
|
49
54
|
|
50
55
|
# Make it work jobs using its own connection per thread
|
51
|
-
pool.post(connection)
|
52
|
-
|
56
|
+
pool.post(connection) do |memo_connection|
|
57
|
+
# TODO: use read-write lock?
|
58
|
+
loop do
|
53
59
|
begin
|
54
|
-
|
55
|
-
|
60
|
+
break if @in_shutdown
|
61
|
+
work_one_job(memo_connection)
|
56
62
|
rescue => e
|
57
63
|
log_error("Exception caught in thread pool loop. Continuing. -> #{e.message}\nBacktrace: #{e.backtrace}")
|
58
64
|
end
|
59
|
-
|
60
|
-
|
65
|
+
end
|
66
|
+
|
67
|
+
connection.close
|
68
|
+
end
|
61
69
|
end
|
62
70
|
end
|
63
71
|
|
@@ -111,18 +119,44 @@ module Backburner
|
|
111
119
|
|
112
120
|
# Wait for the shutdown signel
|
113
121
|
def wait_for_shutdown!
|
114
|
-
while
|
115
|
-
|
122
|
+
raise Interrupt while IO.select([self_read])
|
123
|
+
rescue Interrupt
|
124
|
+
shutdown
|
125
|
+
end
|
126
|
+
|
127
|
+
def shutdown_threadpools
|
128
|
+
@thread_pools.each { |_name, pool| pool.shutdown }
|
129
|
+
shutdown_time = Time.now
|
130
|
+
@in_shutdown = true
|
131
|
+
all_shutdown = @thread_pools.all? do |_name, pool|
|
132
|
+
time_to_wait = self.class.shutdown_timeout - (Time.now - shutdown_time).to_i
|
133
|
+
pool.wait_for_termination(time_to_wait) if time_to_wait > 0
|
116
134
|
end
|
135
|
+
rescue Interrupt
|
136
|
+
log_info "graceful shutdown aborted, shutting down immediately"
|
137
|
+
ensure
|
138
|
+
kill unless all_shutdown
|
139
|
+
end
|
117
140
|
|
118
|
-
|
119
|
-
|
120
|
-
@thread_pools.each { |name, pool| pool.kill }
|
141
|
+
def kill
|
142
|
+
@thread_pools.each { |_name, pool| pool.kill unless pool.shutdown? }
|
121
143
|
end
|
122
144
|
|
123
145
|
def shutdown
|
124
|
-
|
125
|
-
|
146
|
+
log_info "beginning graceful worker shutdown"
|
147
|
+
shutdown_threadpools
|
148
|
+
super if @exit_on_shutdown
|
149
|
+
end
|
150
|
+
|
151
|
+
# Registers signal handlers TERM and INT to trigger
|
152
|
+
def register_signal_handlers!
|
153
|
+
@self_read, @self_write = IO.pipe
|
154
|
+
%w[TERM INT].each do |sig|
|
155
|
+
trap(sig) do
|
156
|
+
raise Interrupt if @in_shutdown
|
157
|
+
self_write.puts(sig)
|
158
|
+
end
|
159
|
+
end
|
126
160
|
end
|
127
161
|
end # Threading
|
128
162
|
end # Workers
|
data/test/fixtures/test_jobs.rb
CHANGED
@@ -13,6 +13,24 @@ class TestJob
|
|
13
13
|
def self.perform(x, y); $worker_test_count += x + y; end
|
14
14
|
end
|
15
15
|
|
16
|
+
class TestSlowJob
|
17
|
+
include Backburner::Queue
|
18
|
+
queue_priority :medium
|
19
|
+
queue_respond_timeout 300
|
20
|
+
def self.perform(x, y); sleep 1; $worker_test_count += x + y; end
|
21
|
+
end
|
22
|
+
|
23
|
+
class TestStuckJob
|
24
|
+
include Backburner::Queue
|
25
|
+
queue_priority :medium
|
26
|
+
queue_respond_timeout 300
|
27
|
+
def self.perform(_x, _y)
|
28
|
+
loop do
|
29
|
+
sleep 0.5
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
16
34
|
class TestFailJob
|
17
35
|
include Backburner::Queue
|
18
36
|
def self.perform(x, y); raise RuntimeError; end
|
data/test/job_test.rb
CHANGED
@@ -113,16 +113,41 @@ describe "Backburner::Job module" do
|
|
113
113
|
end # process
|
114
114
|
|
115
115
|
describe "for simple delegation method" do
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
116
|
+
describe "with valid class" do
|
117
|
+
before do
|
118
|
+
@task_body = { "class" => "NestedDemo::TestJobC", "args" => [56] }
|
119
|
+
@task = stub(:body => @task_body, :ttr => 120, :delete => true, :bury => true)
|
120
|
+
@task.expects(:bury).once
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should call bury for task" do
|
124
|
+
@job = Backburner::Job.new(@task)
|
125
|
+
@job.bury
|
126
|
+
end # bury
|
120
127
|
end
|
121
128
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
129
|
+
describe "with invalid class" do
|
130
|
+
before do
|
131
|
+
@task_body = { "class" => "AnUnknownClass", "args" => [] }
|
132
|
+
@task = stub(:body => @task_body, :ttr => 120, :delete => true, :bury => true, :release => true)
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should call bury for task" do
|
136
|
+
@task.expects(:bury).once
|
137
|
+
@job = Backburner::Job.new(@task)
|
138
|
+
Backburner::Hooks.expects(:invoke_hook_events)
|
139
|
+
.with("AnUnknownClass", :on_bury, anything)
|
140
|
+
@job.bury
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should call retry for task" do
|
144
|
+
@task.expects(:release).once
|
145
|
+
@job = Backburner::Job.new(@task)
|
146
|
+
Backburner::Hooks.expects(:invoke_hook_events)
|
147
|
+
.with("AnUnknownClass", :on_retry, 0, is_a(Integer), anything)
|
148
|
+
@job.retry(0, 0)
|
149
|
+
end
|
150
|
+
end
|
126
151
|
end # simple delegation
|
127
152
|
|
128
153
|
describe "timing out for various values of ttr" do
|
data/test/test_helper.rb
CHANGED
@@ -11,7 +11,7 @@ describe "Backburner::Workers::Forking module" do
|
|
11
11
|
describe "for prepare method" do
|
12
12
|
it "should make tube names array always unique to avoid duplication" do
|
13
13
|
worker = @worker_class.new(["foo", "demo.test.foo"])
|
14
|
-
worker.prepare
|
14
|
+
capture_stdout { worker.prepare }
|
15
15
|
assert_equal ["demo.test.foo"], worker.tube_names
|
16
16
|
end
|
17
17
|
|
@@ -11,7 +11,7 @@ describe "Backburner::Workers::Simple module" do
|
|
11
11
|
describe "for prepare method" do
|
12
12
|
it "should make tube names array always unique to avoid duplication" do
|
13
13
|
worker = @worker_class.new(["foo", "demo.test.foo"])
|
14
|
-
worker.prepare
|
14
|
+
capture_stdout { worker.prepare }
|
15
15
|
assert_equal ["demo.test.foo"], worker.tube_names
|
16
16
|
end
|
17
17
|
|
@@ -309,7 +309,7 @@ describe "Backburner::Workers::Simple module" do
|
|
309
309
|
worker = @worker_class.new('foo.bar')
|
310
310
|
connection = mock('connection')
|
311
311
|
worker.expects(:reserve_job).with(connection).returns(stub_everything('job'))
|
312
|
-
worker.work_one_job(connection)
|
312
|
+
capture_stdout { worker.work_one_job(connection) }
|
313
313
|
end
|
314
314
|
|
315
315
|
after do
|
@@ -6,24 +6,25 @@ describe "Backburner::Workers::Threading worker" do
|
|
6
6
|
before do
|
7
7
|
Backburner.default_queues.clear
|
8
8
|
@worker_class = Backburner::Workers::Threading
|
9
|
+
@worker_class.shutdown_timeout = 2
|
9
10
|
end
|
10
11
|
|
11
12
|
describe "for prepare method" do
|
12
13
|
it "should make tube names array always unique to avoid duplication" do
|
13
14
|
worker = @worker_class.new(["foo", "demo.test.foo"])
|
14
|
-
worker.prepare
|
15
|
+
capture_stdout { worker.prepare }
|
15
16
|
assert_equal ["demo.test.foo"], worker.tube_names
|
16
17
|
end
|
17
18
|
|
18
19
|
it 'creates a thread pool per queue' do
|
19
20
|
worker = @worker_class.new(%w(foo bar))
|
20
|
-
|
21
|
+
capture_stdout { worker.prepare }
|
21
22
|
assert_equal 2, worker.instance_variable_get("@thread_pools").keys.size
|
22
23
|
end
|
23
24
|
|
24
25
|
it 'uses Concurrent.processor_count if no custom thread count is provided' do
|
25
26
|
worker = @worker_class.new("foo")
|
26
|
-
|
27
|
+
capture_stdout { worker.prepare }
|
27
28
|
assert_equal ::Concurrent.processor_count, worker.instance_variable_get("@thread_pools")["demo.test.foo"].max_length
|
28
29
|
end
|
29
30
|
end # prepare
|
@@ -61,10 +62,43 @@ describe "Backburner::Workers::Threading worker" do
|
|
61
62
|
it 'runs work_on_job per thread' do
|
62
63
|
clear_jobs!("foo")
|
63
64
|
job_count=10
|
64
|
-
|
65
|
-
@
|
66
|
-
|
65
|
+
# TestJob adds the given arguments together and then to $worker_test_count
|
66
|
+
job_count.times { @worker_class.enqueue TestJob, [1, 0], :queue => "foo" }
|
67
|
+
capture_stdout do
|
68
|
+
@worker.start(false) # don't wait for shutdown
|
69
|
+
sleep 0.5 # Wait for threads to do their work
|
70
|
+
end
|
67
71
|
assert_equal job_count, $worker_test_count
|
68
72
|
end
|
69
73
|
end # working a queue
|
74
|
+
|
75
|
+
describe 'shutting down' do
|
76
|
+
before do
|
77
|
+
@thread_count = 3
|
78
|
+
@worker = @worker_class.new(["threaded-shutdown:#{@thread_count}"])
|
79
|
+
@worker.exit_on_shutdown = false
|
80
|
+
$worker_test_count = 0
|
81
|
+
clear_jobs!("threaded-shutdown")
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'gracefully exits and completes all in-flight jobs' do
|
85
|
+
6.times { @worker_class.enqueue TestSlowJob, [1, 0], :queue => "threaded-shutdown" }
|
86
|
+
Thread.new { sleep 0.1; @worker.self_write.puts("TERM") }
|
87
|
+
capture_stdout do
|
88
|
+
@worker.start
|
89
|
+
end
|
90
|
+
|
91
|
+
assert_equal @thread_count, $worker_test_count
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'forces an exit when a job is stuck' do
|
95
|
+
6.times { @worker_class.enqueue TestStuckJob, [1, 0], :queue => "threaded-shutdown" }
|
96
|
+
Thread.new { sleep 0.1; @worker.self_write.puts("TERM") }
|
97
|
+
capture_stdout do
|
98
|
+
@worker.start
|
99
|
+
end
|
100
|
+
|
101
|
+
assert_equal 0, $worker_test_count
|
102
|
+
end
|
103
|
+
end
|
70
104
|
end
|
@@ -70,7 +70,7 @@ describe "Backburner::Workers::ThreadsOnFork module" do
|
|
70
70
|
|
71
71
|
it "should make tube names array always unique to avoid duplication" do
|
72
72
|
worker = @worker_class.new(["foo", "demo.test.foo"])
|
73
|
-
worker.prepare
|
73
|
+
capture_stdout { worker.prepare }
|
74
74
|
assert_equal ["demo.test.foo"], worker.tube_names
|
75
75
|
end
|
76
76
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: backburner
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nathan Esquenazi
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: beaneater
|
@@ -43,6 +43,9 @@ dependencies:
|
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.0'
|
48
|
+
- - ">="
|
46
49
|
- !ruby/object:Gem::Version
|
47
50
|
version: 1.0.1
|
48
51
|
type: :runtime
|
@@ -50,6 +53,9 @@ dependencies:
|
|
50
53
|
version_requirements: !ruby/object:Gem::Requirement
|
51
54
|
requirements:
|
52
55
|
- - "~>"
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '1.0'
|
58
|
+
- - ">="
|
53
59
|
- !ruby/object:Gem::Version
|
54
60
|
version: 1.0.1
|
55
61
|
- !ruby/object:Gem::Dependency
|