tennis-jobs 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9ee6ec8dbc807f4cb3ade4eac1651624555239b1
4
+ data.tar.gz: 73978213184591abc3a807957a31b76e81a23817
5
+ SHA512:
6
+ metadata.gz: 550aad703fa200b1f002ae8cfc84a9d22c55c39b0867885e4ee8a74d776d9241b65f5389fbe2133f31b601a3d49e53b0274d0391c2720d291d58e421931b7818
7
+ data.tar.gz: aa3f869f0a540088090b4a574d88288bd554931c84a9d0c15e8cf0088e46db21474da52d800c1a8ce7552f16063072efd4681348c59da47be85f63b1dcba01d6
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ /coverage/
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/.travis.yml ADDED
@@ -0,0 +1,8 @@
1
+ language: ruby
2
+ rvm:
3
+ - "2.2.3"
4
+ branches:
5
+ only:
6
+ - master
7
+ script: bundle exec rspec spec
8
+ before_install: gem install bundler -v 1.10.5
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,12 @@
1
+ guard :rspec, cmd: "bundle exec rspec" do
2
+ require "guard/rspec/dsl"
3
+ dsl = Guard::RSpec::Dsl.new(self)
4
+
5
+ rspec = dsl.rspec
6
+ watch(rspec.spec_helper) { rspec.spec_dir }
7
+ watch(rspec.spec_support) { rspec.spec_dir }
8
+ watch(rspec.spec_files)
9
+
10
+ ruby = dsl.ruby
11
+ dsl.watch_spec_files_for(ruby.lib_files)
12
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Nicolas ZERMATI
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.
data/README.markdown ADDED
@@ -0,0 +1,148 @@
1
+ This small library is intended to help creating asynchronous jobs
2
+ using Ruby and RabbitMQ via the Sneakers gem.
3
+
4
+ <a href="https://travis-ci.org/nicoolas25/tennis"><img src="https://travis-ci.org/nicoolas25/tennis.svg?branch=master" /></a>
5
+ <a href="https://codeclimate.com/github/nicoolas25/tennis"><img src="https://codeclimate.com/github/nicoolas25/tennis/badges/gpa.svg" /></a>
6
+ <a href="https://codeclimate.com/github/nicoolas25/tennis/coverage"><img src="https://codeclimate.com/github/nicoolas25/tennis/badges/coverage.svg" /></a>
7
+
8
+ ## Features
9
+
10
+ - Hooks: `.before(symbol, &block)`
11
+ - Serializers: `.serialize(loader:)`
12
+ - Helpers for defering method calls: `object.defer.method(*arguments)`
13
+
14
+ **Extra**
15
+
16
+ - A `GenericSerializer` handling classes and ActiveRecord objects
17
+
18
+ ## Examples
19
+
20
+ Those examples are what we wish to achieve.
21
+
22
+ The name of the queue is the name of the class by default and can be reset
23
+ using sneakers' `.from_queue` method of the `YourClass::Worker`'s class.
24
+
25
+ ### Hooks
26
+
27
+ ``` ruby
28
+ class MyClass
29
+ include GenericWorker
30
+
31
+ before do
32
+ puts "Before processing"
33
+ end
34
+
35
+ work do |message|
36
+ puts "Working with #{message}"
37
+ ack!
38
+ end
39
+ end
40
+
41
+ MyClass.execute("my class")
42
+ # => Before processing
43
+ # => Working with my class
44
+ ```
45
+
46
+ ### Serializers
47
+
48
+ You can provide a Proc for the loader and/or the dumper keywords.
49
+ The dumper will be used when calling `MyClass.execute(message)` receiving
50
+ the `message` as argument. It should return a string. The loader will be
51
+ used when the message is poped from the RabbitMQ queue.
52
+
53
+ ``` ruby
54
+ class MyClass
55
+ include GenericWorker
56
+
57
+ serialize loader: ->(message){ JSON.parse(message) },
58
+ dumper: ->(message){ JSON.generate(message) }
59
+
60
+ work do |message|
61
+ one, two = message
62
+ puts "Message is serialized and deserialized correctly"
63
+ puts "one: #{one}, two: #{two}"
64
+ ack!
65
+ end
66
+ end
67
+
68
+ MyClass.execute([1, "foo"])
69
+ # => Message is serialized and deserialized correctly
70
+ # => one: 1, two: foo
71
+ ```
72
+
73
+ ``` ruby
74
+ class MyClass
75
+ include GenericWorker
76
+
77
+ serialize GenericSerializer.new
78
+
79
+ work do |message|
80
+ klass, active_record_object = message
81
+ puts "Classes can be passed: #{klass.name} - #{klass.class}"
82
+ puts "Active record object can be passed too: #{active_record_object}"
83
+ ack!
84
+ end
85
+ end
86
+
87
+ MyClass.execute([String, User.find(1)])
88
+ # => Classes can be passed: String - Class
89
+ # => Active record object can be passed too: <User#1 ...>
90
+ ```
91
+
92
+ ### Helpers
93
+
94
+ Any class method can be defered:
95
+
96
+ ``` ruby
97
+ class MyClass
98
+ include DeferableWorker
99
+
100
+ def self.my_method(user)
101
+ puts "Running my method on #{user}"
102
+ end
103
+ end
104
+
105
+ MyClass.defer.my_method(User.find(1))
106
+ # => Running my method on <User#1 ...>
107
+ ```
108
+
109
+ An ActiveRecord::Base instance can be the receiver if it has an `id`:
110
+
111
+ ``` ruby
112
+ class MyModel < ActiveRecord::Base
113
+ include DeferableWorker
114
+
115
+ def my_method
116
+ puts "Running my method on #{self}"
117
+ end
118
+ end
119
+
120
+ instance = MyModel.find(1)
121
+ instance.defer.my_method
122
+ # => Running my method on <MyModel#1 ...>
123
+ ```
124
+
125
+ Handling errors and results for the deferred methods is via `on_error` and
126
+ `on_success` keyword. You must return `ack!` or `reject!` here as in a
127
+ Sneakers' `work` method.
128
+
129
+ ``` ruby
130
+ class MyModel < ActiveRecord::Base
131
+ include DeferableWorker
132
+
133
+ on_error do |exception|
134
+ puts "I just saved the day handling a #{exception}"
135
+ reject!
136
+ end
137
+
138
+ def my_method
139
+ puts "Running my method on #{self}"
140
+ raise StandardError
141
+ end
142
+ end
143
+
144
+ instance = MyModel.find(1)
145
+ instance.defer.my_method
146
+ # => Running my method on <MyModel#1 ...>
147
+ # => I just saved the day handling a StandardError
148
+ ```
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,46 @@
1
+ #
2
+ # Create a client or a worker showing the serialization process.
3
+ #
4
+ # To run this example you should start a worker, then a client in a separate
5
+ # shell. Of course you'll also need a running RabbitMQ server.
6
+ #
7
+ # Use RABBITMQ_URL environment variable if you need to customize the RabbitMQ
8
+ # address.
9
+ #
10
+
11
+ $LOAD_PATH.unshift("./lib")
12
+
13
+ require "tennis"
14
+
15
+ require_relative "example"
16
+
17
+ class Model
18
+ include Tennis::Worker::Deferable
19
+
20
+ attr_reader :id
21
+
22
+ def initialize(id)
23
+ @id = id
24
+ end
25
+
26
+ def method(numbers)
27
+ puts "At #{Time.now}, Model##{id} run method with #{numbers.inspect}\n"
28
+ end
29
+
30
+ # Usually the ORM provides this method. Here we're just faking it.
31
+ def self.find(id)
32
+ new(id)
33
+ end
34
+
35
+ # Usually the ORM provides this method. Here we're faking it too.
36
+ def attributes
37
+ { id: @id }
38
+ end
39
+ end
40
+
41
+ Example.new(__FILE__, Model).run do |i|
42
+ array = Array.new(5) { 1 + rand(5) }
43
+ print "Press enter to enqueue a random array..." ; gets
44
+ puts "Calling <Model##{i}>.method(#{array.inspect})"
45
+ Model.new(i).defer.method(array)
46
+ end
@@ -0,0 +1,52 @@
1
+ require "logger"
2
+ require "optparse"
3
+ require "sneakers/runner"
4
+
5
+ class Example
6
+ def initialize(filename, klass)
7
+ @filename = filename
8
+ @klass = klass
9
+ end
10
+
11
+ def run(&client_code)
12
+ configure_workers
13
+ parse_options
14
+ if @options[:client]
15
+ start_client(client_code)
16
+ elsif @options[:worker]
17
+ start_worker
18
+ end
19
+ end
20
+
21
+ protected
22
+
23
+ def configure_workers
24
+ Tennis.configure do |config|
25
+ config.async = true
26
+ config.exchange = "example"
27
+ config.workers = 1
28
+ config.logger = Logger.new(STDOUT)
29
+ config.logger.level = Logger::WARN
30
+ end
31
+ end
32
+
33
+ def start_client(block, i = 1)
34
+ block.call(i)
35
+ start_client(block, i + 1)
36
+ rescue Interrupt
37
+ puts "Exiting the client"
38
+ end
39
+
40
+ def start_worker
41
+ Sneakers::Runner.new([@klass.worker]).run
42
+ end
43
+
44
+ def parse_options
45
+ @options = {}
46
+ OptionParser.new do |opts|
47
+ opts.banner = "Usage: #{@filename}"
48
+ opts.on("-c", "--client", "Run the client") { @options[:client] = true }
49
+ opts.on("-w", "--worker", "Run the worker") { @options[:worker] = true }
50
+ end.parse!
51
+ end
52
+ end
@@ -0,0 +1,37 @@
1
+ #
2
+ # Create a client or a worker showing the serialization process.
3
+ #
4
+ # To run this example you should start a worker, then a client in a separate
5
+ # shell. Of course you'll also need a running RabbitMQ server.
6
+ #
7
+ # Use RABBITMQ_URL environment variable if you need to customize the RabbitMQ
8
+ # address.
9
+ #
10
+
11
+ $LOAD_PATH.unshift("./lib")
12
+
13
+ require "json"
14
+ require "tennis"
15
+
16
+ require_relative "example"
17
+
18
+ class SumWorker
19
+ include Tennis::Worker::Generic
20
+
21
+ serialize loader: ->(message){ JSON.parse(message) },
22
+ dumper: ->(message){ JSON.generate(message) }
23
+
24
+ work do |int_array|
25
+ sum = int_array.inject(0, &:+)
26
+ puts "At #{Time.now} I received an array. Sum of its elements is: #{sum}"
27
+ ack!
28
+ end
29
+ end
30
+
31
+ Example.new(__FILE__, SumWorker).run do
32
+ array = Array.new(5) { 1 + rand(5) }
33
+ print "Press enter to enqueue a random array..." ; gets
34
+ puts "Sending #{array.inspect} to the #{SumWorker::Worker.queue_name} queue!"
35
+ SumWorker.execute(array)
36
+ end
37
+
data/lib/tennis.rb ADDED
@@ -0,0 +1,25 @@
1
+ require "sneakers"
2
+
3
+ module Tennis
4
+ module Worker
5
+ autoload :Deferable, "tennis/worker/deferable"
6
+ autoload :Generic, "tennis/worker/generic"
7
+ end
8
+
9
+ module Serializer
10
+ autoload :Generic, "tennis/serializer/generic"
11
+ end
12
+
13
+ autoload :Configuration, "tennis/configuration"
14
+
15
+ def self.configure
16
+ @config = Configuration.new
17
+ yield @config if block_given?
18
+ @config.finalize!
19
+ end
20
+
21
+ def self.config
22
+ @config or fail "You must run Tennis.configure before accessing the configuration"
23
+ end
24
+
25
+ end
@@ -0,0 +1,28 @@
1
+ module Tennis
2
+ class Configuration
3
+ DEFAULT = {
4
+ async: true,
5
+ exchange: "tennis",
6
+ workers: 4,
7
+ logger: STDOUT,
8
+ sneakers_options: {},
9
+ }.freeze
10
+
11
+ attr_accessor :async, :exchange, :workers, :logger, :sneakers_options
12
+
13
+ def initialize(opts = {})
14
+ DEFAULT.merge(opts).each do |name, value|
15
+ __send__("#{name}=", value)
16
+ end
17
+ end
18
+
19
+ def finalize!
20
+ Sneakers.configure({
21
+ exchange: exchange,
22
+ workers: workers,
23
+ log: logger,
24
+ }.merge(sneakers_options))
25
+ end
26
+
27
+ end
28
+ end
@@ -0,0 +1,101 @@
1
+ require "json"
2
+
3
+ module Tennis
4
+ module Serializer
5
+ class Generic
6
+
7
+ RECOGNIZED_TYPES = {
8
+ active_record: "active_record".freeze,
9
+ class: "class".freeze,
10
+ }.freeze
11
+
12
+ def load(message)
13
+ object = JSON.parse(message)
14
+ deserialize_any(object)
15
+ end
16
+
17
+ def dump(message)
18
+ JSON.generate(serialize_any(message))
19
+ end
20
+
21
+ private
22
+
23
+ def serialize_any(object)
24
+ visit(object) do |type, object|
25
+ case type
26
+ when :object
27
+ object
28
+ when :class
29
+ {
30
+ _type: RECOGNIZED_TYPES[:class],
31
+ _class: object.to_s
32
+ }
33
+ when :active_record
34
+ {
35
+ _type: RECOGNIZED_TYPES[:active_record],
36
+ _class: object.class.to_s,
37
+ _id: object.id,
38
+ }
39
+ else
40
+ fail "Unexpected type: #{type} when visiting object"
41
+ end
42
+ end
43
+ end
44
+
45
+ def deserialize_any(object)
46
+ visit(object) do |type, object|
47
+ case type
48
+ when :object
49
+ object
50
+ when :class
51
+ Object.const_get(object["_class"])
52
+ when :active_record
53
+ klass = Object.const_get(object["_class"])
54
+ klass.find(object["_id"])
55
+ else
56
+ fail "Unexpected type: #{type} when visiting object"
57
+ end
58
+ end
59
+ end
60
+
61
+ def visit(object, &block)
62
+ visit_any(object, block)
63
+ end
64
+
65
+ def visit_any(object, block)
66
+ if object.kind_of?(Array)
67
+ visit_array(object, block)
68
+ elsif is_active_record?(object)
69
+ block.call(:active_record, object)
70
+ elsif is_class?(object)
71
+ block.call(:class, object)
72
+ elsif object.kind_of?(Hash)
73
+ visit_hash(object, block)
74
+ else
75
+ block.call(:object, object)
76
+ end
77
+ end
78
+
79
+ def visit_array(array, block)
80
+ array.map { |element| visit_any(element, block) }
81
+ end
82
+
83
+ def visit_hash(hash, block)
84
+ hash.each_with_object({}) do |(key, value), new_hash|
85
+ new_hash[key] = visit_any(value, block)
86
+ end
87
+ end
88
+
89
+ def is_active_record?(object)
90
+ object.respond_to?(:attributes) ||
91
+ object.is_a?(Hash) && object["_type"] == RECOGNIZED_TYPES[:active_record]
92
+ end
93
+
94
+ def is_class?(object)
95
+ object.is_a?(Class) ||
96
+ object.is_a?(Hash) && object["_type"] == RECOGNIZED_TYPES[:class]
97
+ end
98
+
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,55 @@
1
+ module Tennis
2
+ module Worker
3
+ module Deferable
4
+
5
+ autoload :Action, "tennis/worker/deferable/action"
6
+
7
+ def self.included(base)
8
+ base.extend DSL
9
+ base.include Worker::Generic
10
+ base.serialize Serializer::Generic.new
11
+ define_work_broker(base)
12
+ end
13
+
14
+ def self.define_work_broker(base)
15
+ base.work do |message|
16
+ begin
17
+ receiver, method, arguments = message
18
+ result = receiver.__send__(method, *arguments)
19
+ instance_exec(result, &base._on_success)
20
+ rescue => exception
21
+ instance_exec(exception, message, &base._on_error)
22
+ end
23
+ end
24
+ end
25
+
26
+ def defer
27
+ Action.new(self.class, self)
28
+ end
29
+
30
+ module DSL
31
+
32
+ def defer
33
+ Action.new(self, self)
34
+ end
35
+
36
+ def on_error(&block)
37
+ @_on_error = block
38
+ end
39
+
40
+ def on_success(&block)
41
+ @_on_success = block
42
+ end
43
+
44
+ def _on_error
45
+ @_on_error || ->(_, _){ reject! }
46
+ end
47
+
48
+ def _on_success
49
+ @_on_success || ->(_){ ack! }
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,25 @@
1
+ class Tennis::Worker::Deferable::Action
2
+ def initialize(worker_class, receiver)
3
+ @worker_class = worker_class
4
+ @receiver = receiver
5
+ _create_methods!
6
+ end
7
+
8
+ private
9
+
10
+ def _create_methods!
11
+ _methods.each do |method|
12
+ self.define_singleton_method(method) do |*arguments|
13
+ @worker_class.execute([@receiver, method, arguments])
14
+ end
15
+ end
16
+ end
17
+
18
+ def _methods
19
+ if @receiver.kind_of?(Class)
20
+ @receiver.methods(false).map(&:to_s)
21
+ else
22
+ @receiver.class.instance_methods(false).map(&:to_s)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,56 @@
1
+ module Tennis
2
+ module Worker
3
+ module Generic
4
+
5
+ autoload :BeforeHook, "tennis/worker/generic/before_hook"
6
+ autoload :Serialize, "tennis/worker/generic/serialize"
7
+
8
+ def self.included(base)
9
+ base.extend BeforeHook
10
+ base.extend Serialize
11
+ base.extend DSL
12
+ base.worker = Class.new do
13
+ @@parent = base
14
+
15
+ include Sneakers::Worker
16
+ from_queue @@parent.name
17
+
18
+ def work(message)
19
+ message = @@parent._apply_serializer(:load, message)
20
+ @@parent._process_before_hooks(message, self)
21
+ instance_exec(message, &@@parent._work)
22
+ end
23
+ end
24
+ end
25
+
26
+ module DSL
27
+
28
+ attr_accessor :worker
29
+
30
+ def work(&block)
31
+ @_work = block
32
+ end
33
+
34
+ def _work
35
+ @_work ||= ->(_){ ack! }
36
+ end
37
+
38
+ def execute(message)
39
+ message = _apply_serializer(:dump, message)
40
+ if Tennis.config.async
41
+ publisher_opts = worker.queue_opts.select do |opt_name, _|
42
+ opt_name == :exchange ||
43
+ opt_name == :exchange_type
44
+ end
45
+ publisher = Sneakers::Publisher.new(publisher_opts)
46
+ publisher.publish(message, to_queue: worker.queue_name)
47
+ publisher.instance_variable_get(:@bunny).close
48
+ else
49
+ worker.new.work(message)
50
+ end
51
+ end
52
+
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,17 @@
1
+ module Tennis::Worker::Generic::BeforeHook
2
+ def before(&block)
3
+ if block_given?
4
+ _before_hooks << block
5
+ end
6
+ end
7
+
8
+ def _process_before_hooks(message, worker)
9
+ _before_hooks.each do |hook|
10
+ hook.call(message, worker)
11
+ end
12
+ end
13
+
14
+ def _before_hooks
15
+ @_before_hooks ||= []
16
+ end
17
+ end
@@ -0,0 +1,26 @@
1
+ module Tennis::Worker::Generic::Serialize
2
+ def serialize(object = nil, loader: nil, dumper: nil)
3
+ if object
4
+ _serializers[:load] = object if object.respond_to?(:load)
5
+ _serializers[:dump] = object if object.respond_to?(:dump)
6
+ else
7
+ _serializers[:load] = loader if loader
8
+ _serializers[:dump] = dumper if dumper
9
+ end
10
+ end
11
+
12
+ def _apply_serializer(serializer_kind, message)
13
+ serializer = _serializers[serializer_kind]
14
+ if serializer.kind_of?(Proc)
15
+ serializer.call(message)
16
+ elsif serializer.respond_to?(serializer_kind)
17
+ serializer.__send__(serializer_kind, message)
18
+ else
19
+ fail "Unexpected (de)serializer, it must be a Proc or respond to ##{kind}"
20
+ end
21
+ end
22
+
23
+ def _serializers
24
+ @_serializers ||= Hash.new(->(message){ message })
25
+ end
26
+ end
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "tennis-jobs"
7
+ spec.version = "0.1.0"
8
+ spec.authors = ["Nicolas ZERMATI"]
9
+ spec.email = ["nicoolas25@gmail.com"]
10
+
11
+ spec.summary = %q{A small background job library on top of sneakers.}
12
+ spec.description = %q{A small background job library on top of sneakers.}
13
+ spec.homepage = "https://github.com/nicoolas25/tennis"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "sneakers"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.10"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "pry"
26
+ spec.add_development_dependency "rspec"
27
+ spec.add_development_dependency "simplecov"
28
+ spec.add_development_dependency "guard-rspec"
29
+ spec.add_development_dependency "codeclimate-test-reporter"
30
+ end
metadata ADDED
@@ -0,0 +1,176 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tennis-jobs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nicolas ZERMATI
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2015-09-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sneakers
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: guard-rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: codeclimate-test-reporter
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: A small background job library on top of sneakers.
126
+ email:
127
+ - nicoolas25@gmail.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".gitignore"
133
+ - ".rspec"
134
+ - ".travis.yml"
135
+ - Gemfile
136
+ - Guardfile
137
+ - LICENSE.txt
138
+ - README.markdown
139
+ - Rakefile
140
+ - examples/deferable.rb
141
+ - examples/example.rb
142
+ - examples/serializer.rb
143
+ - lib/tennis.rb
144
+ - lib/tennis/configuration.rb
145
+ - lib/tennis/serializer/generic.rb
146
+ - lib/tennis/worker/deferable.rb
147
+ - lib/tennis/worker/deferable/action.rb
148
+ - lib/tennis/worker/generic.rb
149
+ - lib/tennis/worker/generic/before_hook.rb
150
+ - lib/tennis/worker/generic/serialize.rb
151
+ - tennis-jobs.gemspec
152
+ homepage: https://github.com/nicoolas25/tennis
153
+ licenses:
154
+ - MIT
155
+ metadata: {}
156
+ post_install_message:
157
+ rdoc_options: []
158
+ require_paths:
159
+ - lib
160
+ required_ruby_version: !ruby/object:Gem::Requirement
161
+ requirements:
162
+ - - ">="
163
+ - !ruby/object:Gem::Version
164
+ version: '0'
165
+ required_rubygems_version: !ruby/object:Gem::Requirement
166
+ requirements:
167
+ - - ">="
168
+ - !ruby/object:Gem::Version
169
+ version: '0'
170
+ requirements: []
171
+ rubyforge_project:
172
+ rubygems_version: 2.4.5
173
+ signing_key:
174
+ specification_version: 4
175
+ summary: A small background job library on top of sneakers.
176
+ test_files: []