queue_client 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ queue-client
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-1.9.3-p448
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ queue_client (1.0.0)
5
+ amqp (= 0.9.8)
6
+ bunny (= 0.9.0.pre7)
7
+ eventmachine (= 1.0.0)
8
+ oj (= 2.0.5)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ amq-client (0.9.12)
14
+ amq-protocol (>= 1.2.0)
15
+ eventmachine
16
+ amq-protocol (1.3.0)
17
+ amqp (0.9.8)
18
+ amq-client (~> 0.9.5)
19
+ amq-protocol (>= 0.9.4)
20
+ eventmachine
21
+ bunny (0.9.0.pre7)
22
+ amq-protocol (>= 1.2.0)
23
+ diff-lcs (1.1.3)
24
+ eventmachine (1.0.0)
25
+ oj (2.0.5)
26
+ rspec (2.12.0)
27
+ rspec-core (~> 2.12.0)
28
+ rspec-expectations (~> 2.12.0)
29
+ rspec-mocks (~> 2.12.0)
30
+ rspec-core (2.12.2)
31
+ rspec-expectations (2.12.1)
32
+ diff-lcs (~> 1.1.3)
33
+ rspec-mocks (2.12.2)
34
+
35
+ PLATFORMS
36
+ ruby
37
+
38
+ DEPENDENCIES
39
+ queue_client!
40
+ rspec (>= 2.12.0)
@@ -0,0 +1,35 @@
1
+ require 'oj'
2
+ require 'bunny'
3
+
4
+ module Viki
5
+ module Queue
6
+ @host = 'localhost'
7
+ @port = 5672
8
+ @username = 'guest'
9
+ @password = 'guest'
10
+ class << self
11
+ attr_accessor :host, :port, :username, :password, :client_name, :_service
12
+ end
13
+
14
+ def self.configure(&block)
15
+ block.call self
16
+ raise "Viki::Queue.client_name not set" unless client_name
17
+ nil
18
+ end
19
+
20
+ def self.service
21
+ Viki::Queue._service ||= Service.new
22
+ end
23
+
24
+ def self.reconnect
25
+ Viki::Queue._service.stop
26
+ Viki::Queue._service = Service.new({routing: Viki::Queue._service.routing})
27
+ end
28
+ end
29
+ end
30
+
31
+ require_relative 'viki/queue/logger'
32
+ require_relative 'viki/queue/message'
33
+ require_relative 'viki/queue/runner'
34
+ require_relative 'viki/queue/service'
35
+ require_relative 'viki/queue/version'
@@ -0,0 +1,9 @@
1
+ module Viki::Queue
2
+ module Logger
3
+ def self.log(message, e = nil)
4
+ puts "#{message}\n"
5
+ puts "\t#{e.to_s}\n\tstack: #{e.backtrace.join("\n\t")}" unless e.nil?
6
+ return 0
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,56 @@
1
+ module Viki::Queue
2
+ module Message
3
+ def create_message(resource, id, payload=nil)
4
+ write(prepare_message([:create, resource, id, payload]))
5
+ end
6
+
7
+ def update_message(resource, id, payload=nil)
8
+ write(prepare_message([:update, resource, id, payload]))
9
+ end
10
+
11
+ def delete_message(resource, id, payload=nil)
12
+ write(prepare_message([:delete, resource, id, payload]))
13
+ end
14
+
15
+ def bulk(*events)
16
+ write_many(events.map { |e| prepare_message(e) })
17
+ end
18
+
19
+ private
20
+
21
+ def write(payload)
22
+ routing_key = "#{Viki::Queue._service.routing}.#{payload[:resource]}.#{payload[:action]}"
23
+ message = Oj.dump(payload, mode: :compat)
24
+ opts = {routing_key: routing_key, timestamp: Time.now.to_i, persistent: true}
25
+ attemps = 5
26
+ while attemps > 0
27
+ begin
28
+ send_to_queue(message, opts)
29
+ break
30
+ rescue Exception => e
31
+ attemps -= 1
32
+ if attemps > 0
33
+ sleep(0.5)
34
+ Viki::Queue.reconnect()
35
+ else
36
+ Viki::Queue::Logger.log('Failed to write in queue. Exiting.', e)
37
+ end
38
+ end
39
+ end
40
+ end
41
+
42
+ def send_to_queue(message, opts)
43
+ Viki::Queue._service.exchange.publish(message, opts)
44
+ end
45
+
46
+ def write_many(payload)
47
+ payload.each { |p| write(p) }
48
+ end
49
+
50
+ def prepare_message(payload)
51
+ m = {action: payload[0], resource: payload[1], id: payload[2], _meta: {client_name: Viki::Queue.client_name}}
52
+ m[:payload] = payload[3] unless payload[3].nil?
53
+ m
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,64 @@
1
+ require 'amqp'
2
+
3
+ module Viki::Queue
4
+ module Runner
5
+ def run(queue, router, config={})
6
+ config = {iterations: 1, fail_pause: 10}.merge(config)
7
+
8
+ begin
9
+ EventMachine.run do
10
+ connection = AMQP.connect({
11
+ host: Viki::Queue.host,
12
+ port: Viki::Queue.port,
13
+ username: Viki::Queue.username,
14
+ password: Viki::Queue.password})
15
+ channel = AMQP::Channel.new(connection)
16
+ loops = 0
17
+ channel.prefetch(1).queue(queue, :durable => true).subscribe(:ack => true) do |metadata, message|
18
+ processed = false
19
+ for i in 1..10 do
20
+ begin
21
+ payload = Oj.load(message, symbol_keys: true)
22
+ if payload[:_meta]
23
+ payload[:_meta][:timestamp] = metadata.timestamp
24
+ else
25
+ payload[:_meta] = {timestamp: metadata.timestamp}
26
+ end
27
+
28
+ if process(router, payload) == true
29
+ processed = true
30
+ metadata.ack
31
+ break
32
+ end
33
+ rescue => e
34
+ router.error(e)
35
+ end
36
+ sleep(config[:fail_pause])
37
+ end
38
+
39
+ unless processed
40
+ router.error("Failed to process message: #{message}")
41
+ connection.close { EventMachine.stop }
42
+ end
43
+
44
+ loops += 1
45
+ if loops == config[:iterations]
46
+ connection.close { EventMachine.stop }
47
+ end
48
+ end
49
+ end
50
+ rescue Interrupt
51
+ puts "Queue is interrupt. Good night!"
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def process(router, message)
58
+ unless message.nil?
59
+ method = "#{message[:action]}_#{message[:resource]}"
60
+ router.send(method, message)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,51 @@
1
+ module Viki::Queue
2
+ class Service
3
+ include Viki::Queue::Message
4
+ include Viki::Queue::Runner
5
+
6
+ attr_accessor :routing, :exchange
7
+
8
+ def initialize(opts={})
9
+ @connection = Bunny.new({
10
+ host: Viki::Queue.host,
11
+ port: Viki::Queue.port,
12
+ username: Viki::Queue.username,
13
+ password: Viki::Queue.password,
14
+ keepalive: true,
15
+ threaded: false,
16
+ socket_timeout: 0,
17
+ connect_timeout: 0})
18
+ @connection.start
19
+ @channel = @connection.create_channel
20
+ @exchange = @channel.topic("general", durable: true)
21
+ @routing = opts.fetch(:routing, 'resources')
22
+ end
23
+
24
+ def stop
25
+ @connection.stop
26
+ rescue
27
+ end
28
+
29
+ def subscribe(queue, resources)
30
+ resources.each do |r|
31
+ @channel.queue(queue, durable: true).bind(@exchange, :routing_key => "#{@routing}.#{r}.#")
32
+ end
33
+ end
34
+
35
+ def unsubscribe(queue, resources)
36
+ resources.each do |r|
37
+ @channel.queue(queue, durable: true).unbind(@exchange, :routing_key => "#{@routing}.#{r}.#")
38
+ end
39
+ end
40
+
41
+ def route(route)
42
+ return if route.nil? or route == ""
43
+ route = route[0...-1] if route[-1] == '.'
44
+ @routing = route
45
+ end
46
+
47
+ def delete(queue)
48
+ @channel.queue(queue, durable: true).delete()
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,5 @@
1
+ module Viki
2
+ module Queue
3
+ VERSION = "1.0.0"
4
+ end
5
+ end
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "viki/queue/version"
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.authors = ["Viki"]
7
+ gem.email = ["engineering@viki.com"]
8
+ gem.description = %q{A client for Viki's queue}
9
+ gem.homepage = "https://github.com/viki-org/queue-client"
10
+ gem.summary = %q{A client for Viki's queue}
11
+
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.name = "queue_client"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = Viki::Queue::VERSION
18
+
19
+ gem.add_development_dependency "rspec", ">= 2.12.0"
20
+
21
+ gem.add_runtime_dependency "bunny", "0.9.0.pre7"
22
+ gem.add_runtime_dependency "eventmachine", "1.0.0"
23
+ gem.add_runtime_dependency "amqp", "0.9.8"
24
+ gem.add_runtime_dependency "oj", "2.0.5"
25
+ end
data/readme.md ADDED
@@ -0,0 +1,106 @@
1
+ #Queue
2
+
3
+ ## Configuration
4
+ Viki::Queue.configure do |config|
5
+ config.client_name = "my_app"
6
+ config.host = "queue.dev.viki.io"
7
+ config.port = "5672"
8
+ config.username = "username"
9
+ config.password = "password"
10
+ end
11
+
12
+ It defaults to localhost queues.
13
+
14
+ ## Initialize
15
+ queue_service = Viki::Queue.service
16
+
17
+ ## Subscribe
18
+ Before being able to consume from a queue, you must first subscribe to some resources:
19
+
20
+ queue_service.subscribe('gaia-applications', ['application', 'user'])
21
+
22
+ This creates a queue named *gaia_applications* which will monitor the *application* and *user* resources, i.e. it will subscribe to `resources.application.#` and `resources.user.#`. It is possible to change the root of the route path by using the `.route(new_route)` command as described below (see *routing*).
23
+
24
+ ## Unsubscribe
25
+ You can unsubscribe from some resources:
26
+
27
+ queue_service.unsubscribe('gaia-applications', ['user'])
28
+
29
+ This leaves a queue named *gaia_applications* which only monitors the *application* resource, i.e. it will subscribe to `resources.application.#`. It is possible to change the root of the route path by using the `.route(new_route)` command as described below (see *routing*).
30
+
31
+ ## Deletion
32
+ You can delete a queue and all of its queued events:
33
+
34
+ queue_service.delete('gaia_applications')
35
+
36
+ ## Routing
37
+ By default, all the messages will be routed under `resources.RESOURCE_NAME.ACTION`, e.g. `resources.videos.create`.It is possible to modify the root of the route.
38
+
39
+ queue_service.route('delayed_jobs.subbing')
40
+ queue_service.create_message(:compile, '3v')
41
+
42
+ This commands will create a message under the route `delayed_jobs.subbing.compile.create`.
43
+
44
+ On the other hand, by default subscriptions are routed via `resources.RESOURCE_NAME.#`, e.g. `resources.videos.#`.
45
+
46
+ queue_service.route('services.gaia')
47
+ queue_service.subscribe('gaia-consumer', [apps])
48
+
49
+ This will create a new queue called `gaia-consumer` that is subscribed to `services.gaia.apps.#`.
50
+
51
+ *Note* that the part `.resource.action` is always present in both examples, changing the route only affects the root of it.
52
+
53
+ #Messages
54
+
55
+ Messages have always the following structure:
56
+
57
+ action: mandatory, it will always be 'create', 'update' or 'delete'
58
+ resource: mandatory
59
+ id: mandatory
60
+ payload: optional attribute.
61
+
62
+ ## Writing
63
+ Use the `create_message`, `update_message` and `delete_message` methods:
64
+
65
+ queue_service.create_message(:application, '38a')
66
+ queue_service.update_message(:user, '9003u')
67
+ queue_service.delete_message(:container, '50c')
68
+
69
+ An optional payload parameter can be supplied:
70
+
71
+ queue_service.create_message(:application, '38a', {name: 'gaia'})
72
+
73
+ This will create a message in the queue that looks like:
74
+
75
+ {action: 'create', resource: 'application', id: '38a', payload: {name: 'gaia'} }
76
+
77
+ ### Bulk write
78
+ Multiple events can be sent at once:
79
+
80
+ queue_service.bulk([:create, :video, '1v'], [:update, :user, '1u', 'optional_payload'], [:delete, :container, '1c'])
81
+
82
+ The events will be queued in-order.
83
+
84
+ ## Consumption
85
+
86
+ Consumption involves using the built-in runner and providing a routing class:
87
+
88
+ Class GaiaRouter
89
+ def self.delete_application(event)
90
+ # do something
91
+ true
92
+ end
93
+ def self.error(error)
94
+ # handle
95
+ end
96
+ end
97
+ queue_service.run(QUEUE_NAME, GaiaRouter)
98
+
99
+ The method names look like `ACTION_RESOURCE`, where `ACTION` can be `create`, `update` or `delete`. The `RESOURCE` can be any resource. *Note* that if noone is registered for listening the kind of resource you are sending, the message will just be dropped. There's no need to `close` the queue, simply return true when the event has been successfully processed. *Note* that a message is only acknowledged to the queue when the processing method returns true.
100
+
101
+ If an exception is raised while processing the message, the runner will call the error method of the router with the exception. Afterwards, the runner will wait before trying to reprocess the same message again.
102
+
103
+ `run` takes a 3rd optional argument to configure the runner, possible values are:
104
+
105
+ * `iterations`: The number of iterations to run, i.e. of messages to process. 0 means loop forever (DEFAULT 1).
106
+ * `fail_pause`: Seconds to pause until trying to reprocess a failed message (DEFAULT 10)
@@ -0,0 +1,13 @@
1
+ require 'spec_helper'
2
+
3
+ describe Viki::Queue do
4
+ describe 'authentication' do
5
+ it 'should fail is wrong username/pasword is given' do
6
+ Viki::Queue.configure do |c|
7
+ c.username = 'wrong'
8
+ c.password = 'alsowrong'
9
+ end
10
+ expect{ Viki::Queue.service.create_message(:kitten, '22k') }.to raise_error(Bunny::PossibleAuthenticationFailureError)
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,74 @@
1
+ require 'spec_helper'
2
+
3
+ describe Viki::Queue do
4
+ describe "write" do
5
+ it "writes a create event" do
6
+ assert({action: 'create', resource: 'kitten', id: '22k', _meta: {client_name: 'testing'}}) do
7
+ Viki::Queue.service.create_message(:kitten, '22k')
8
+ end
9
+ end
10
+
11
+ it "writes an update event" do
12
+ assert({action: 'update', resource: 'kitten', id: '22k', _meta: {client_name: 'testing'}}) do
13
+ Viki::Queue.service.update_message(:kitten, '22k')
14
+ end
15
+ end
16
+
17
+ it "writes a delete event" do
18
+ assert({action: 'delete', resource: 'kitten', id: '22k', _meta: {client_name: 'testing'}}) do
19
+ Viki::Queue.service.delete_message(:kitten, '22k')
20
+ end
21
+ end
22
+
23
+ it "sends payload when provided" do
24
+ assert({action: 'create', resource: 'kitten', id: '22k', payload: [1,2,3], _meta: {client_name: 'testing'}}) do
25
+ Viki::Queue.service.create_message(:kitten, '22k', [1,2,3])
26
+ end
27
+ end
28
+
29
+ it "writes multiple events at once" do
30
+ assert([
31
+ {action: 'create', resource: 'kitten', id: '22k', payload: 'some_payload', _meta: {client_name: 'testing'}},
32
+ {action: 'delete', resource: 'dogs', id: '34', _meta: {client_name: 'testing'}}], 2) do
33
+ Viki::Queue.service.bulk([:create, :kitten, '22k', 'some_payload'], [:delete, :dogs, '34'])
34
+ end
35
+ end
36
+
37
+ it "writes the client name" do
38
+ assert({action: 'create', resource: 'kitten', _meta: {client_name: 'testing'}, id: '22k', _meta: {client_name: 'testing'}}) do
39
+ orig = Viki::Queue.client_name
40
+ Viki::Queue.client_name = "testing"
41
+ Viki::Queue.service.create_message(:kitten, '22k')
42
+ Viki::Queue.client_name = orig
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def assert(message, total=1)
49
+ EventMachine.run do
50
+ connection = AMQP.connect(host: Viki::Queue.host, port: Viki::Queue.port)
51
+ channel = AMQP::Channel.new(connection)
52
+ q = channel.queue("", exclusive: true)
53
+ routing_key = message.class == Array ? "resources.#" : "resources.#{message[:resource]}.#"
54
+ q.bind("general", routing_key: routing_key) do
55
+ yield
56
+ end
57
+ count = 0
58
+ q.subscribe do |m|
59
+ m = Oj.load(m, symbol_keys: true)
60
+ if message.class == Array
61
+ message.include?(m).should == true
62
+ else
63
+ m.should == message
64
+ end
65
+ count += 1
66
+ if count == total
67
+ connection.close { EventMachine.stop }
68
+ end
69
+ end
70
+ end
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe Viki::Queue::Runner do
4
+ describe "run" do
5
+ it "polls the queue and noops a nil" do
6
+ fakeRouter = Class.new do
7
+ def self.create_application(e)
8
+ e[:resource].should == 'application'
9
+ e[:action].should == 'create'
10
+ e[:id].should == '22a'
11
+ true
12
+ end
13
+ def self.error(e)
14
+ puts(%{===> e: %s} % [(e).inspect])
15
+ end
16
+ end
17
+ q = Viki::Queue.service
18
+ q.subscribe('a_queue', ['application'])
19
+ q.create_message(:application, '22a')
20
+ q.run('a_queue', fakeRouter)
21
+ q.delete('a_queue')
22
+ end
23
+
24
+ it "includes the timestamp and the existing meta" do
25
+ fakeRouter = Class.new do
26
+ def self.create_application(e)
27
+ meta = e.delete(:_meta)
28
+ meta.include?(:timestamp).should == true
29
+ meta[:client_name].should == "testing"
30
+ true
31
+ end
32
+ def self.error(e)
33
+ puts(%{===> e: %s} % [(e).inspect])
34
+ end
35
+ end
36
+ q = Viki::Queue.service
37
+ q.subscribe('a_queue', ['application'])
38
+ orig = Viki::Queue.client_name
39
+ Viki::Queue.client_name = "testing"
40
+ q.create_message(:application, '22a')
41
+ Viki::Queue.client_name = orig
42
+ q.run('a_queue', fakeRouter)
43
+ q.delete('a_queue')
44
+ end
45
+
46
+ it "iterates multiple times" do
47
+ fakeRouter = Class.new do
48
+ class << self
49
+ attr_accessor :count
50
+ end
51
+ def self.create_application(e)
52
+ @count ||= 0
53
+ @count += 1
54
+ true
55
+ end
56
+ def self.update_video(e)
57
+ @count ||= 0
58
+ @count += 1
59
+ true
60
+ end
61
+ def self.error(e)
62
+ puts(%{===> e: %s} % [(e).inspect])
63
+ end
64
+ end
65
+ Time.stub(:now).and_return(Time.at(323232323))
66
+ q = Viki::Queue.service
67
+ q.subscribe('the-queue', ['application', 'video'])
68
+ q.create_message(:application, '22a')
69
+ q.update_message(:video, '1v')
70
+ q.run('the-queue', fakeRouter, iterations: 2)
71
+ fakeRouter.count.should == 2
72
+ q.delete('the-queue')
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,16 @@
1
+ require_relative '../lib/queue_client'
2
+ require 'amqp'
3
+ require 'json'
4
+ Dir["./spec/support/**/*.rb"].each { |f| require f }
5
+
6
+ RSpec.configure do |config|
7
+ config.before(:each) {
8
+ Viki::Queue.configure do |c|
9
+ c.host = 'localhost'
10
+ c.port = 5672
11
+ c.username = 'guest'
12
+ c.password = 'guest'
13
+ c.client_name = 'testing'
14
+ end
15
+ }
16
+ end
data/t ADDED
@@ -0,0 +1,2 @@
1
+ #!/bin/sh
2
+ rspec --color spec
metadata ADDED
@@ -0,0 +1,146 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: queue_client
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Viki
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-07-23 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 2.12.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 2.12.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: bunny
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - '='
36
+ - !ruby/object:Gem::Version
37
+ version: 0.9.0.pre7
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - '='
44
+ - !ruby/object:Gem::Version
45
+ version: 0.9.0.pre7
46
+ - !ruby/object:Gem::Dependency
47
+ name: eventmachine
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - '='
52
+ - !ruby/object:Gem::Version
53
+ version: 1.0.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: amqp
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - '='
68
+ - !ruby/object:Gem::Version
69
+ version: 0.9.8
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - '='
76
+ - !ruby/object:Gem::Version
77
+ version: 0.9.8
78
+ - !ruby/object:Gem::Dependency
79
+ name: oj
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - '='
84
+ - !ruby/object:Gem::Version
85
+ version: 2.0.5
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - '='
92
+ - !ruby/object:Gem::Version
93
+ version: 2.0.5
94
+ description: A client for Viki's queue
95
+ email:
96
+ - engineering@viki.com
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - .ruby-gemset
102
+ - .ruby-version
103
+ - Gemfile
104
+ - Gemfile.lock
105
+ - lib/queue_client.rb
106
+ - lib/viki/queue/logger.rb
107
+ - lib/viki/queue/message.rb
108
+ - lib/viki/queue/runner.rb
109
+ - lib/viki/queue/service.rb
110
+ - lib/viki/queue/version.rb
111
+ - queue_client.gemspec
112
+ - readme.md
113
+ - spec/queue/authentication_spec.rb
114
+ - spec/queue/write_spec.rb
115
+ - spec/runner/run_spec.rb
116
+ - spec/spec_helper.rb
117
+ - t
118
+ homepage: https://github.com/viki-org/queue-client
119
+ licenses: []
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ none: false
126
+ requirements:
127
+ - - ! '>='
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ! '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ requirements: []
137
+ rubyforge_project:
138
+ rubygems_version: 1.8.24
139
+ signing_key:
140
+ specification_version: 3
141
+ summary: A client for Viki's queue
142
+ test_files:
143
+ - spec/queue/authentication_spec.rb
144
+ - spec/queue/write_spec.rb
145
+ - spec/runner/run_spec.rb
146
+ - spec/spec_helper.rb