reliable-queue 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ef20e5154d605fc6c1469cb10e9a3cdfc656c0c2
4
+ data.tar.gz: aa28b4ee3dbd541441f83f317aab458ac146182a
5
+ SHA512:
6
+ metadata.gz: 0bde4b76e29f9b12c67f2e37be07401955b32e0ea0ed949fcf3273a4d7c46bb3783c8545f3a078050d781ababcc673ad6a9e3ac0ca1d581d34909c8a67ff16db
7
+ data.tar.gz: 205b8d56659f268522b14bfbdc246ec94b4d865e0606ce605c67bf0a0676f1d8ec4d16450f7c982a2e5f79a6c960a3efb1d2f7e0c13d299a6e02dff1233aaed2
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1 @@
1
+ 2.2.2
@@ -0,0 +1,7 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.2
4
+ - 1.9.3
5
+ services:
6
+ - redis
7
+ before_install: gem install bundler -v 1.10.5
@@ -0,0 +1,13 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities.
4
+
5
+ We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, ethnicity, age, or religion.
6
+
7
+ Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct.
8
+
9
+ Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team.
10
+
11
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers.
12
+
13
+ This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/)
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in reliable.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 myobie
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,154 @@
1
+ [![Build
2
+ Status](https://travis-ci.org/wunderlist/reliable.svg?branch=master)](https://travis-ci.org/wunderlist/reliable)
3
+
4
+ # Reliable is.
5
+
6
+ Redis is a great storage service for building a reliable queue. That's
7
+ what this is for.
8
+
9
+ ## Is this like [Ost](https://github.com/soveran/ost)?
10
+
11
+ Ost was the inspiration for this project. We love ost, but it lacks a few of the nice things we want (retry, failures count, etc) and will will implement those extra features here. We also wanted parallelism baked in.
12
+
13
+ ## Configuring redis
14
+
15
+ One must set the `REDIS_URL` environment variable.
16
+
17
+ ## Enqueueing messages
18
+
19
+ The developer is responsible for enqueuing `String`s or string-like
20
+ objects.
21
+
22
+ ```ruby
23
+ Reliable[:messages].push(JSON.generate({
24
+ id: 123,
25
+ title: "Hello"
26
+ }))
27
+ ```
28
+
29
+ In this example `:messages` is the queue name. The developer can make as
30
+ many queues as they like.
31
+
32
+ ## Processing messages
33
+
34
+ ### Processing all messages as they arrive
35
+
36
+ ```ruby
37
+ Reliable[:messages].each do |message|
38
+ hash = JSON.generate(message)
39
+ DatabaseTable.find(hash["id"]).do_something_awesome(message)
40
+ end
41
+ ```
42
+
43
+ or
44
+
45
+ ```ruby
46
+ Reliable[:emails].each do |message|
47
+ hash = JSON.generate(message)
48
+ Emailer.new(hash).deliver
49
+ end
50
+ ```
51
+
52
+ Calling `#each` will block the main thread and sleep forever.
53
+
54
+ ### Processing messages in parallel
55
+
56
+ If the processing code is thread-safe, the developer can spawn any
57
+ number of threads with `#peach`:
58
+
59
+ ```ruby
60
+ Reliable[:touches].peach(concurrency: 12) { |id| Model.find(id).touch }
61
+ ```
62
+
63
+ In this example 12 threads will be created and all are joined with the
64
+ main thread.
65
+
66
+ ### Processing some messages
67
+
68
+ It's also possible to process only some message by `#take`-ing as many
69
+ as necessary:
70
+
71
+ ```ruby
72
+ Reliable[:urls].take(2) do |url|
73
+ content = open(url)
74
+ PersistentStore.store(content)
75
+ end
76
+ ```
77
+
78
+ Or if you just want the urls themselves as an array:
79
+
80
+ ```ruby
81
+ urls = Reliable[:urls].take(2)
82
+
83
+ urls.each do |url|
84
+ content = open(url)
85
+ PersistentStore.store(content)
86
+ end
87
+ ```
88
+
89
+ And if the developer wants, they can get an enumerator object and
90
+ interact with it as necessary:
91
+
92
+ ```ruby
93
+ enumerator = Reliable[:ids].to_enum { |id| notify(id) }
94
+ assert_equal 0, notifications.length
95
+ 4.times { enumerator.next }
96
+ assert_equal 4, notifications.length
97
+ ```
98
+
99
+ ## Time
100
+
101
+ Make sure the distributed clock starts moving before you lock the main thread.
102
+ The clock makes it possible to re-enqueue stale items.
103
+
104
+ Here is a full example:
105
+
106
+ ```ruby
107
+ Reliable[:emails].periodically_move_time_forward
108
+ Reliable[:emails].peach(concurrency: 6) do |message|
109
+ hash = JSON.generate(message)
110
+ Emailer.new(hash).deliver
111
+ end
112
+ ```
113
+
114
+ ## Rails
115
+
116
+ Probably best to create an initializer:
117
+
118
+ ```ruby
119
+ # config/initializers/reliable.rb
120
+ Reliable[:emails].periodically_move_time_forward
121
+ ```
122
+
123
+ Then in a controller one might:
124
+
125
+ ```ruby
126
+ # app/controllers/users_controller.rb
127
+ class UsersController < ApplicationController
128
+ def create
129
+ @user = User.create!(params.require(:email))
130
+ Reliable[:emails].push JSON.generate({
131
+ user_id: @user.id
132
+ })
133
+ redirect_to root_url
134
+ end
135
+ end
136
+ ```
137
+
138
+ Then, in a worker file:
139
+
140
+ ```ruby
141
+ # app/workers/emails_worker.rb
142
+ Reliable[:emails].peach(concurrency: 6) do |message|
143
+ hash = JSON.parse(message)
144
+ user = User.find(hash[:user_id])
145
+ Emailer.welcome_email(user).deliver
146
+ end
147
+ ```
148
+
149
+ And maybe one would have a `Procfile` like this:
150
+
151
+ ```
152
+ web: bin/rails s -p$PORT
153
+ worker: bin/rails r app/workers/emails_worker.rb
154
+ ```
@@ -0,0 +1,17 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
8
+ task :console do
9
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
10
+ ENV["RELIABLE_TIMEOUT"] = "1"
11
+ ENV["RELIABLE_TIME_TRAVEL_DELAY"] = "1"
12
+ ENV["REDIS_URI"] = "redis://127.0.0.1:6379/0"
13
+ require 'reliable'
14
+ require 'irb'
15
+ ARGV.clear
16
+ IRB.start
17
+ end
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "reliable"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,33 @@
1
+ require_relative 'reliable/version'
2
+ require_relative 'reliable/queue'
3
+ require_relative 'reliable/redis'
4
+
5
+ module Reliable
6
+ POP_TIMEOUT = ENV.fetch("RELIABLE_TIMEOUT", "2").to_i
7
+ TIME_TRAVEL_DELAY = ENV.fetch("RELIABLE_TIME_TRAVEL_DELAY", "60").to_i
8
+ PROCESSING_TIMEOUT = ENV.fetch("PROCESSING_TIMEOUT", "120").to_i
9
+
10
+ @queues = Hash.new do |h, k|
11
+ h[k] = Queue.new(k)
12
+ end
13
+
14
+ def self.[](queue)
15
+ @queues[queue]
16
+ end
17
+
18
+ class NullLogger
19
+ def debug(*); end
20
+ def info(*); end
21
+ def error(*); end
22
+ def warn(*); end
23
+ def fatal(*); end
24
+ end
25
+
26
+ def self.logger
27
+ @logger ||= NullLogger.new
28
+ end
29
+
30
+ def self.logger=(new_logger)
31
+ @logger = new_logger
32
+ end
33
+ end
@@ -0,0 +1,14 @@
1
+ module Reliable
2
+ class List
3
+ attr_reader :key
4
+
5
+ def initialize(key, redis)
6
+ @key = key
7
+ @redis = redis
8
+ end
9
+
10
+ def llen
11
+ @redis.llen(key)
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,111 @@
1
+ require 'logger'
2
+ require 'securerandom'
3
+
4
+ require_relative '../reliable'
5
+ require_relative 'list'
6
+ require_relative 'uuid'
7
+ require_relative 'worker'
8
+
9
+ module Reliable
10
+ class Queue
11
+ FatalError = Class.new(StandardError)
12
+
13
+ attr_accessor :base_key, :uuid
14
+
15
+ def initialize(name)
16
+ @name = name
17
+ @base_key = "reliable:queues:#{name}"
18
+ @redis = Redis.new
19
+ @pending_key = @base_key + ":pending"
20
+ @failed_key = @base_key + ":failed"
21
+ end
22
+
23
+ def current_time
24
+ @redis.time
25
+ end
26
+
27
+ def push(value)
28
+ UUID.new(current_time) do |uuid|
29
+ @redis.set_and_lpush(pending.key, uuid.to_s, value)
30
+ end
31
+ end
32
+ alias_method :<<, :push
33
+
34
+ def pending
35
+ @pending ||= List.new(@pending_key, @redis)
36
+ end
37
+
38
+ def failed
39
+ @failed ||= List.new(@failed_key, @redis)
40
+ end
41
+
42
+ def create_worker(&work)
43
+ Worker.new(self, &work)
44
+ end
45
+
46
+ def to_enum(&work)
47
+ work ||= ->(item) { item }
48
+ worker = create_worker(&work)
49
+ Enumerator.new do |y|
50
+ loop do # forever
51
+ result = worker.next # do work
52
+ y.yield result # then release control
53
+ end
54
+ end
55
+ end
56
+
57
+ def take(number, &block)
58
+ to_enum(&block).take(number)
59
+ end
60
+
61
+ def each(&block)
62
+ if block_given?
63
+ # This is because we confuse iteration with work here
64
+ # So we need to front-load the work, then fake the iteration
65
+ to_enum(&block).each { |item| item }
66
+ else
67
+ to_enum
68
+ end
69
+ end
70
+
71
+ def peach(opts = {}, &block)
72
+ raise "must supply a block" unless block_given?
73
+
74
+ concurrency = opts.fetch(:concurrency)
75
+
76
+ threads = concurrency.times.map do
77
+ Thread.new { each(&block) }
78
+ end
79
+
80
+ threads.map { |t| t.abort_on_exception = true }
81
+ threads.map(&:join)
82
+ end
83
+
84
+ def total_processing
85
+ keys = @redis.scan "reliable:queues:*:workers:*:processing"
86
+
87
+ lengths = @redis.pipeline do |pipe|
88
+ keys.each do |key|
89
+ pipe.llen key
90
+ end
91
+ end
92
+
93
+ lengths.map(&:to_i).reduce(0, &:+)
94
+ end
95
+
96
+ def total_items
97
+ @redis.scan("reliable:items:*").length
98
+ end
99
+
100
+ def logger
101
+ Reliable.logger
102
+ end
103
+
104
+ def notify(e, other = {})
105
+ # TODO: make configurable
106
+ logger.info e.inspect
107
+ logger.info e.backtrace
108
+ logger.info other.inspect
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,121 @@
1
+ require 'redic'
2
+
3
+ module Reliable
4
+ class Redis
5
+ def initialize
6
+ url = ENV.fetch("REDIS_URI") { ENV.fetch("REDIS_URL") }
7
+ @connection = Redic.new(url)
8
+ @mutex = Mutex.new
9
+ end
10
+
11
+ def synchronize
12
+ @mutex.synchronize { yield }
13
+ end
14
+
15
+ def command(*args)
16
+ @connection.call!(*args)
17
+ end
18
+
19
+ def scommand(*args)
20
+ synchronize { command(*args) }
21
+ end
22
+
23
+ def time
24
+ scommand("TIME").join(".")
25
+ end
26
+
27
+ def get(key)
28
+ scommand "GET", key
29
+ end
30
+
31
+ def set(key, value)
32
+ scommand "SET", key, value
33
+ end
34
+
35
+ def incr(key)
36
+ scommand "INCR", key
37
+ end
38
+
39
+ def llen(key)
40
+ scommand "LLEN", key
41
+ end
42
+
43
+ def brpoplpush(pop_key, push_key, timeout = POP_TIMEOUT)
44
+ scommand "BRPOPLPUSH", pop_key, push_key, timeout
45
+ end
46
+
47
+ def rpoplpush(pop_key, push_key)
48
+ scommand "RPOPLPUSH", pop_key, push_key
49
+ end
50
+
51
+ def lpush(key, value)
52
+ scommand "LPUSH", key, value
53
+ end
54
+
55
+ def lpop(key)
56
+ scommand "LPOP", key
57
+ end
58
+
59
+ class Pipeline
60
+ def initialize(conn)
61
+ @connection = conn
62
+ end
63
+
64
+ def queue(*args)
65
+ @connection.queue(*args)
66
+ end
67
+ alias_method :q, :queue
68
+
69
+ def llen(key)
70
+ queue "LLEN", key
71
+ end
72
+ end
73
+
74
+ def pipeline
75
+ synchronize do
76
+ @connection.reset
77
+ pipe = Pipeline.new(@connection)
78
+ yield(pipe)
79
+ @connection.commit
80
+ end
81
+ end
82
+
83
+ def multi
84
+ synchronize do
85
+ begin
86
+ command "MULTI"
87
+ yield
88
+ command "EXEC"
89
+ rescue StandardError => e
90
+ command "DISCARD"
91
+ raise
92
+ end
93
+ end
94
+ end
95
+
96
+ def set_and_lpush(list_key, key, value)
97
+ multi do
98
+ command "SET", key, value
99
+ command "LPUSH", list_key, key
100
+ end
101
+ end
102
+
103
+ def lpop_and_del(list_key, key)
104
+ multi do
105
+ command "LPOP", list_key
106
+ command "DEL", key
107
+ end
108
+ end
109
+
110
+ def scan(pattern)
111
+ keys = []
112
+ cursor = "0"
113
+ loop do
114
+ cursor, list = scommand "SCAN", cursor, "MATCH", pattern
115
+ keys << list
116
+ break if cursor == "0"
117
+ end
118
+ keys.flatten.compact.uniq
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,38 @@
1
+ require 'securerandom'
2
+
3
+ module Reliable
4
+ MalformedUUID = Class.new(StandardError)
5
+
6
+ class UUID
7
+ def self.parse(key)
8
+ _, _, time, random, tries = key.split(":")
9
+ raise MalformedUUID unless time && random && tries
10
+ new(time, random, tries)
11
+ end
12
+
13
+ attr_reader :random
14
+
15
+ def initialize(time, random = SecureRandom.uuid, tries = 0)
16
+ @time = time
17
+ @random = random
18
+ @tries = tries || 0
19
+ yield(self) if block_given?
20
+ end
21
+
22
+ def time
23
+ @time.to_i
24
+ end
25
+
26
+ def tries
27
+ @tries.to_i
28
+ end
29
+
30
+ def incr
31
+ @tries = tries + 1
32
+ end
33
+
34
+ def to_s
35
+ "reliable:items:#{time}:#{random}:#{tries}"
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,3 @@
1
+ module Reliable
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,62 @@
1
+ require 'securerandom'
2
+ require 'delegate'
3
+
4
+ module Reliable
5
+ class Worker
6
+ extend Forwardable
7
+
8
+ delegate [:pending, :failed] => :@queue
9
+
10
+ def initialize(queue, &block)
11
+ @queue = queue
12
+ @block = block
13
+ @processing_key = "#{queue.base_key}:workers:#{SecureRandom.uuid}:processing"
14
+ @redis = Redis.new
15
+ end
16
+
17
+ def processing
18
+ @processing ||= List.new(@processing_key, @redis)
19
+ end
20
+
21
+ def next
22
+ uuid = @redis.brpoplpush pending.key, processing.key
23
+ return if uuid.nil?
24
+
25
+ item = @redis.get uuid
26
+
27
+ if item
28
+ catch(:failed) do
29
+ process item, &@block
30
+ @redis.lpop_and_del processing.key, uuid
31
+ logger.info "Processed #{uuid}"
32
+ end
33
+ end
34
+
35
+ item
36
+ rescue StandardError => e
37
+ @redis.rpoplpush processing.key, failed.key
38
+ notify(e, uuid: uuid, worker: processing.key)
39
+ nil
40
+ end
41
+
42
+ def process(item, &block)
43
+ block.yield item
44
+ rescue StandardError => e
45
+ @redis.rpoplpush processing.key, failed.key
46
+ notify(e, worker: processing.key)
47
+ throw :failed
48
+ end
49
+ private :process
50
+
51
+ def logger
52
+ Reliable.logger
53
+ end
54
+
55
+ def notify(e, other = {})
56
+ # TODO: make configurable
57
+ logger.info e.inspect
58
+ logger.info e.backtrace
59
+ logger.info other.inspect
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'reliable/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "reliable-queue"
8
+ spec.version = Reliable::VERSION
9
+ spec.authors = ["myobie"]
10
+ spec.email = ["me@nathanherald.com"]
11
+
12
+ spec.summary = %q{A reliable queue using redis}
13
+ spec.description = %q{A relialbe queue library for enqueuing and creating workers.}
14
+ spec.homepage = "https://github.com/wunderlist/reliable"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.bindir = "exe"
19
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
20
+ spec.require_paths = ["lib"]
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.10"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec"
25
+
26
+ spec.add_dependency "redic"
27
+ end
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: reliable-queue
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - myobie
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-12-20 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.10'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.10'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: redic
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: A relialbe queue library for enqueuing and creating workers.
70
+ email:
71
+ - me@nathanherald.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - ".rspec"
78
+ - ".ruby-version"
79
+ - ".travis.yml"
80
+ - CODE_OF_CONDUCT.md
81
+ - Gemfile
82
+ - LICENSE.txt
83
+ - README.md
84
+ - Rakefile
85
+ - bin/console
86
+ - bin/setup
87
+ - lib/reliable.rb
88
+ - lib/reliable/list.rb
89
+ - lib/reliable/queue.rb
90
+ - lib/reliable/redis.rb
91
+ - lib/reliable/uuid.rb
92
+ - lib/reliable/version.rb
93
+ - lib/reliable/worker.rb
94
+ - reliable.gemspec
95
+ homepage: https://github.com/wunderlist/reliable
96
+ licenses:
97
+ - MIT
98
+ metadata: {}
99
+ post_install_message:
100
+ rdoc_options: []
101
+ require_paths:
102
+ - lib
103
+ required_ruby_version: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ version: '0'
108
+ required_rubygems_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ requirements: []
114
+ rubyforge_project:
115
+ rubygems_version: 2.5.0
116
+ signing_key:
117
+ specification_version: 4
118
+ summary: A reliable queue using redis
119
+ test_files: []