async-jobs 0.0.3

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: ad313be63e58bd780e2b68297719ec64cb7f46e6
4
+ data.tar.gz: 4f341fc468bf497f4266185e930351c3a1c6ce1b
5
+ SHA512:
6
+ metadata.gz: eff272909fab9a45836846e6576762ef7e9da0bee6442e47aa769a3bc3be2d8eafdf0e33bed032dd37332ec69b410b0a1fe71d2e13ba25093585e571a8600352
7
+ data.tar.gz: 0fe48659523783bd0314e8c5b1e6425fd99bdba12f721c245bc36589d745c90466e73003e51388a8d1e573f4eaca3624aa6241f55446960856f8fe3f4ef3daee
@@ -0,0 +1 @@
1
+ async*.gem
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'redis'
7
+ gem 'cubbyhole', :require => false
8
+ gem 'qu'
9
+ gem 'rspec'
10
+ gem 'resque'
11
+ gem 'sidekiq'
12
+ end
@@ -0,0 +1,66 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ async-jobs (0.0.3)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ celluloid (0.12.4)
10
+ facter (>= 1.6.12)
11
+ timers (>= 1.0.0)
12
+ connection_pool (1.0.0)
13
+ cubbyhole (0.2.1)
14
+ diff-lcs (1.1.3)
15
+ facter (1.6.18)
16
+ multi_json (1.7.0)
17
+ qu (0.2.0)
18
+ multi_json
19
+ rack (1.4.5)
20
+ rack-protection (1.4.0)
21
+ rack
22
+ redis (3.0.2)
23
+ redis-namespace (1.2.1)
24
+ redis (~> 3.0.0)
25
+ resque (1.23.0)
26
+ multi_json (~> 1.0)
27
+ redis-namespace (~> 1.0)
28
+ sinatra (>= 0.9.2)
29
+ vegas (~> 0.1.2)
30
+ rspec (2.11.0)
31
+ rspec-core (~> 2.11.0)
32
+ rspec-expectations (~> 2.11.0)
33
+ rspec-mocks (~> 2.11.0)
34
+ rspec-core (2.11.1)
35
+ rspec-expectations (2.11.2)
36
+ diff-lcs (~> 1.1.3)
37
+ rspec-mocks (2.11.1)
38
+ sidekiq (2.8.0)
39
+ celluloid (~> 0.12.0)
40
+ connection_pool (~> 1.0)
41
+ multi_json (~> 1)
42
+ redis (~> 3)
43
+ redis-namespace
44
+ sinatra (1.3.5)
45
+ rack (~> 1.4)
46
+ rack-protection (~> 1.3)
47
+ tilt (~> 1.3, >= 1.3.3)
48
+ tilt (1.3.4)
49
+ timers (1.1.0)
50
+ vegas (0.1.11)
51
+ rack (>= 1.0.0)
52
+
53
+ PLATFORMS
54
+ ruby
55
+
56
+ DEPENDENCIES
57
+ async-jobs!
58
+ cubbyhole
59
+ qu
60
+ redis
61
+ resque
62
+ rspec
63
+ sidekiq
64
+
65
+ BUNDLED WITH
66
+ 1.12.5
@@ -0,0 +1,20 @@
1
+ Copyright (c) Engine Yard. All rights reserved.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,28 @@
1
+ #Yet another background processing abstraction layer
2
+
3
+ Assuming that every time you want to do something in a background job, it's defined in a method on an active record object.
4
+
5
+ Zero explicit dependencies. (just respond to `id` and `find` like AR does)
6
+
7
+ ##Example
8
+
9
+ gem is called `async-jobs`
10
+
11
+ ```ruby
12
+ require 'async'
13
+ require 'async/resque'
14
+ Async.backend = Async::ResqueBackend
15
+
16
+ class Invoice < ActiveRecord::Base
17
+ def process(arg)
18
+ Async.run{ process_now(arg)}
19
+ end
20
+ def process_now(arg)
21
+ #actually do it
22
+ end
23
+ end
24
+
25
+ invoice.process 1
26
+ ```
27
+
28
+ Will enqueue a Resque job that runs `invoice.process_now 1`
@@ -0,0 +1,17 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "async/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "async-jobs"
7
+ s.version = Async::VERSION
8
+ s.author = "Jacob"
9
+ s.email = "jacob@engineyard.com"
10
+ s.homepage = "https://github.com/engineyard/async"
11
+ s.summary = "abstraction over background job systems"
12
+
13
+ s.files = `git ls-files`.split("\n")
14
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
15
+ s.require_paths = ['lib']
16
+
17
+ end
@@ -0,0 +1,119 @@
1
+ class Async
2
+ class << self
3
+ attr_accessor :backend
4
+ end
5
+
6
+ class MethodCatcher
7
+ attr_reader :_method_name, :_args
8
+ def method_missing(method_name, *args)
9
+ @_method_name = method_name
10
+ @_args = args
11
+ end
12
+ end
13
+
14
+ def self.ensure_backend!
15
+ unless Async.backend
16
+ raise "Please configure the background processing system of choice by setting: Async.backend="
17
+ end
18
+ end
19
+
20
+ def self.run(&block)
21
+ ensure_backend!
22
+ receiver = block.binding.eval("self")
23
+ mc = MethodCatcher.new
24
+ mc.instance_eval(&block)
25
+ run_later self.to_s, receiver, mc._method_name, *mc._args
26
+ end
27
+
28
+ def self.run_later(job_class, receiver, method_name, *args)
29
+ if receiver.is_a?(Class)
30
+ receiver_class, receiver_id = receiver.to_s, nil
31
+ else
32
+ receiver_class, receiver_id = receiver.class.to_s, receiver.id
33
+ end
34
+ Async.backend.enqueue Async.backend.job_class, job_class, receiver_class, receiver_id, method_name, Job.transform_args(args)
35
+ end
36
+
37
+ def self.run_now(receiver, method_name, args)
38
+ Notifications.notify_job("run", receiver, method_name, args)
39
+ receiver.send(method_name, *args)
40
+ ensure
41
+ Notifications.notify_job("finish", receiver, method_name, args)
42
+ end
43
+
44
+ #TODO: test this
45
+ class Notifications
46
+ class << self
47
+ attr_accessor :handler
48
+ end
49
+ def self.notify_lock(thing, lock_name)
50
+ handler && handler.call(thing, {:lock_name => lock_name})
51
+ end
52
+ def self.notify_job(thing, receiver, method_name, args)
53
+ handler && handler.call(thing, {
54
+ :receiver => receiver,
55
+ :method_name => method_name.to_sym,
56
+ :args => args
57
+ })
58
+ end
59
+ end
60
+
61
+ #TODO: test this
62
+ class ErrorReporting
63
+ class << self
64
+ attr_accessor :handler
65
+ end
66
+ def self.notify_exception(e, job_args)
67
+ handler && handler.call(e, job_args)
68
+ end
69
+ end
70
+
71
+ class Job
72
+
73
+ def self.perform(wrapper, receiver_class_str, receiver_id, method, args)
74
+ receiver_class = constantize(receiver_class_str)
75
+ receiver = receiver_id ? receiver_class.find(receiver_id) : receiver_class
76
+ untransform_args = untransform_args(args)
77
+ constantize(wrapper).run_now(receiver, method, untransform_args)
78
+ rescue => e
79
+ ErrorReporting.notify_exception(e,
80
+ receiver_class_str: receiver_class_str, receiver_id: receiver_id, method: method, args: args)
81
+ end
82
+
83
+ def self.transform_args(args)
84
+ args.map do |x|
85
+ if x.class.respond_to?(:find)
86
+ {'_transform_arg' => true, 'class' => x.class.to_s, 'id' => x.id}
87
+ elsif x.is_a?(Array)
88
+ transform_args(x)
89
+ else
90
+ x
91
+ end
92
+ end
93
+ end
94
+
95
+ def self.untransform_args(args)
96
+ args.map do |x|
97
+ if x.is_a?(Hash) && x['_transform_arg']
98
+ constantize(x['class']).find(x['id'])
99
+ elsif x.is_a?(Array)
100
+ untransform_args(x)
101
+ else
102
+ x
103
+ end
104
+ end
105
+ end
106
+
107
+ private
108
+
109
+ def self.constantize(str)
110
+ if str.respond_to?(:constantize)
111
+ str.constantize
112
+ else
113
+ eval(str)
114
+ end
115
+ end
116
+
117
+ end
118
+
119
+ end
@@ -0,0 +1,141 @@
1
+ class Async
2
+ class << self
3
+ attr_accessor :redis, :lock_time
4
+ end
5
+
6
+ class Locked < Async
7
+
8
+ def self.run_now(receiver, method_name, args)
9
+ Notifications.notify_job("consider", receiver, method_name, args)
10
+ if Lock.is_lock_arg?(args.first)
11
+ lock_arg = args.shift
12
+ lock = Lock.claim(make_lock_name(receiver)) || Lock.create(make_lock_name(receiver))
13
+ else
14
+ lock = Lock.create(make_lock_name(receiver))
15
+ end
16
+ if lock
17
+ super
18
+ else
19
+ run_later(self.to_s, receiver, method_name, *args)
20
+ end
21
+ ensure
22
+ lock && lock.release
23
+ end
24
+
25
+ def self.run_later(job_class, receiver, method_name, *args)
26
+ if lock = Lock.pass_on(make_lock_name(receiver))
27
+ super(job_class, receiver, method_name, lock.as_job_arg, *args)
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ def self.make_lock_name(receiver)
34
+ if receiver.is_a?(Class)
35
+ Lock.make_name(receiver.to_s, nil)
36
+ else
37
+ Lock.make_name(receiver.class.to_s, receiver.id)
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ class Lock
44
+ def self.make_name(*names)
45
+ "lock:"+names.join(":")
46
+ end
47
+
48
+ def initialize(lock_name)
49
+ @lock_name = lock_name
50
+ @passed_on = false
51
+ end
52
+
53
+ def self.is_lock_arg?(arg)
54
+ arg.is_a?(Hash) && arg["_lock_arg"]
55
+ end
56
+
57
+ def as_job_arg
58
+ {"_lock_arg" => true, 'lock_name' => @lock_name}
59
+ end
60
+
61
+ def claim
62
+ Notifications.notify_lock("claim", @lock_name)
63
+ if redis.get(@lock_name) #still locked
64
+ refresh!
65
+ return true
66
+ else
67
+ lock
68
+ end
69
+ end
70
+
71
+ def lock
72
+ if redis.setnx(@lock_name, "locked")
73
+ refresh!
74
+ Notifications.notify_lock("lock", @lock_name)
75
+ return true
76
+ else
77
+ return false
78
+ end
79
+ end
80
+
81
+ def refresh!
82
+ redis.expire(@lock_name, Async::Locked.lock_time || 15)
83
+ end
84
+
85
+ def pass_on
86
+ if @passed_on
87
+ #already passed on, can't pass again
88
+ false
89
+ else
90
+ @passed_on = true
91
+ true
92
+ end
93
+ end
94
+
95
+ def release
96
+ if @passed_on
97
+ return false
98
+ end
99
+ redis.del(@lock_name)
100
+ Notifications.notify_lock("release", @lock_name)
101
+ Thread.current["Async::Lock.named"][@lock_name] = nil
102
+ return true
103
+ end
104
+
105
+ def redis
106
+ Async::Locked.redis
107
+ end
108
+
109
+ def self.thread_kill_lock(lock_name)
110
+ Thread.current["Async::Lock.named"] ||= {}
111
+ Thread.current["Async::Lock.named"][lock_name] = nil
112
+ end
113
+
114
+ def self.thread_save_lock(lock_name, lock)
115
+ Thread.current["Async::Lock.named"] ||= {}
116
+ Thread.current["Async::Lock.named"][lock_name] = lock
117
+ end
118
+
119
+ def self.thread_fetch_lock(lock_name)
120
+ Thread.current["Async::Lock.named"] ||= {}
121
+ Thread.current["Async::Lock.named"][lock_name]
122
+ end
123
+
124
+ def self.claim(lock_name)
125
+ lock = Lock.new(lock_name)
126
+ lock.claim && thread_save_lock(lock_name, lock) && lock
127
+ end
128
+
129
+ def self.pass_on(lock_name)
130
+ lock = thread_fetch_lock(lock_name)
131
+ lock && lock.pass_on && lock
132
+ end
133
+
134
+ def self.create(lock_name)
135
+ lock = Lock.new(lock_name)
136
+ lock.lock && thread_save_lock(lock_name, lock) && lock
137
+ end
138
+
139
+ end
140
+
141
+ end
@@ -0,0 +1,15 @@
1
+ class Async
2
+
3
+ class QuBackend
4
+
5
+ def self.enqueue(job_class, *args)
6
+ Qu.enqueue(job_class, *args)
7
+ end
8
+
9
+ def self.job_class
10
+ Async::Job
11
+ end
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,19 @@
1
+ class Async
2
+
3
+ class ResqueBackend
4
+
5
+ def self.enqueue(job_class, *args)
6
+ Resque.enqueue(job_class, *args)
7
+ end
8
+
9
+ def self.job_class
10
+ Async::ResqueBackend::Job
11
+ end
12
+
13
+ class Job < Async::Job
14
+ @queue = :default
15
+ end
16
+
17
+ end
18
+
19
+ end
@@ -0,0 +1,25 @@
1
+ require 'sidekiq'
2
+ class Async
3
+
4
+ class SidekiqBackend
5
+
6
+ def self.enqueue(job_class, *args)
7
+ job_class.perform_async(*args)
8
+ end
9
+
10
+ def self.job_class
11
+ Async::SidekiqBackend::Job
12
+ end
13
+
14
+ class Job < Async::Job
15
+ include Sidekiq::Worker
16
+
17
+ def perform(*args)
18
+ self.class.perform(*args)
19
+ end
20
+
21
+ end
22
+
23
+ end
24
+
25
+ end
@@ -0,0 +1,3 @@
1
+ class Async
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1,121 @@
1
+ require 'async'
2
+ require 'async/qu'
3
+ require 'async/locked'
4
+ require 'qu'
5
+ require 'redis'
6
+ require 'shared/models'
7
+
8
+ require 'qu-immediate'
9
+ class PoppableQu < Qu::Backend::Immediate
10
+
11
+ def self.queue
12
+ @queue ||= []
13
+ end
14
+
15
+ def enqueue(payload)
16
+ PoppableQu.queue.unshift(payload)
17
+ end
18
+
19
+ end
20
+
21
+ describe Async::Locked do
22
+ before(:all) do
23
+ Async.backend = Async::QuBackend
24
+ Async::Locked.redis = Redis.new
25
+ end
26
+ before(:each) do
27
+ @qubackend = Qu.backend
28
+ Qu.backend = PoppableQu.new
29
+ end
30
+ after(:each) do
31
+ Qu.backend = @qubackend
32
+ end
33
+
34
+ it "works" do
35
+ Async::Locked.redis.flushdb
36
+
37
+ order_of_ops = []
38
+ Async::Notifications.handler = Proc.new do |name, hash|
39
+ # puts [name, (hash[:method_name] || hash[:lock_name]).to_s, hash[:args]].inspect
40
+ order_of_ops << [name, (hash[:method_name] || hash[:lock_name]).to_s]
41
+ end
42
+
43
+ Thread.current["Async::Lock.named"] = nil
44
+ y = Yard.new
45
+ y.save
46
+ y.do_all_the_work("front")
47
+ y.conflict
48
+
49
+ PoppableQu.queue.size.should eq 2
50
+ next_job = PoppableQu.queue.pop
51
+ next_job.args.should eq ["Async::Locked", "Yard", y.id, :now_do_all_the_work, ["front"]]
52
+
53
+ # puts ""
54
+ Thread.current["Async::Lock.named"] = nil
55
+ next_job.perform
56
+
57
+ PoppableQu.queue.size.should eq 3
58
+ next_job = PoppableQu.queue.pop
59
+ next_job.args.should eq ["Async::Locked", "Yard", 0, :now_conflict, []]
60
+
61
+ # puts ""
62
+ Thread.current["Async::Lock.named"] = nil
63
+ next_job.perform
64
+
65
+ PoppableQu.queue.size.should eq 3
66
+ next_job = PoppableQu.queue.pop
67
+ next_job.args.should eq ["Async::Locked", "Yard", y.id, :now_trim, [{"_lock_arg"=>true, "lock_name"=>"lock:Yard:0"}, "front"]]
68
+
69
+ # puts ""
70
+ Thread.current["Async::Lock.named"] = nil
71
+ next_job.perform
72
+
73
+ PoppableQu.queue.size.should eq 2
74
+ next_job = PoppableQu.queue.pop
75
+ next_job.args.should eq ["Async", "Yard", y.id, :now_mow, ["front"]]
76
+
77
+ # puts ""
78
+ Thread.current["Async::Lock.named"] = nil
79
+ next_job.perform
80
+
81
+ PoppableQu.queue.size.should eq 2
82
+ next_job = PoppableQu.queue.pop
83
+ next_job.args.should eq ["Async::Locked", "Yard", 0, :now_conflict, []]
84
+
85
+ # puts ""
86
+ Thread.current["Async::Lock.named"] = nil
87
+ next_job.perform
88
+
89
+ PoppableQu.queue.size.should eq 1
90
+ next_job = PoppableQu.queue.pop
91
+ next_job.args.should eq ["Async", "Yard", nil, :now_burn, []]
92
+
93
+ # puts ""
94
+ Thread.current["Async::Lock.named"] = nil
95
+ next_job.perform
96
+
97
+ PoppableQu.queue.size.should eq 0
98
+
99
+ order_of_ops.should eq [
100
+ ["consider", "now_do_all_the_work"],
101
+ ["lock", "lock:Yard:0"],
102
+ ["run", "now_do_all_the_work"],
103
+ ["finish", "now_do_all_the_work"],
104
+ ["consider", "now_conflict"],
105
+ ["consider", "now_trim"],
106
+ ["claim", "lock:Yard:0"],
107
+ ["run", "now_trim"],
108
+ ["finish", "now_trim"],
109
+ ["release", "lock:Yard:0"],
110
+ ["run", "now_mow"],
111
+ ["finish", "now_mow"],
112
+ ["consider", "now_conflict"],
113
+ ["lock", "lock:Yard:0"],
114
+ ["run", "now_conflict"],
115
+ ["finish", "now_conflict"],
116
+ ["release", "lock:Yard:0"],
117
+ ["run", "now_burn"],
118
+ ["finish", "now_burn"]]
119
+ end
120
+
121
+ end
@@ -0,0 +1,40 @@
1
+ require 'async'
2
+ require 'shared/models'
3
+
4
+ class TestBackend
5
+ def initialize
6
+ @jobs = []
7
+ end
8
+ def enqueue(job_class, *args)
9
+ @jobs << [job_class, args]
10
+ end
11
+ def run_all_jobs!
12
+ while(job = @jobs.pop)
13
+ job_class, args = job
14
+ job_class.perform(*args)
15
+ end
16
+ end
17
+ def job_class
18
+ Async::Job
19
+ end
20
+ end
21
+
22
+ describe "Async basics" do
23
+ before(:all) do
24
+ @test_backend = TestBackend.new
25
+ Async.backend = @test_backend
26
+ end
27
+
28
+ it "works" do
29
+ y = Yard.new
30
+ y.save
31
+ y.mowed.should be_nil
32
+ y.mow("front")
33
+
34
+ @test_backend.run_all_jobs!
35
+
36
+ y.reload
37
+ y.mowed.should eq "front"
38
+ end
39
+
40
+ end
@@ -0,0 +1,23 @@
1
+ require 'async'
2
+ require 'async/qu'
3
+ require 'shared/models'
4
+ require 'qu'
5
+ require 'qu-immediate'
6
+
7
+ describe "Async qu" do
8
+ before(:all) do
9
+ Qu.backend = Qu::Backend::Immediate.new
10
+ Async.backend = Async::QuBackend
11
+ end
12
+
13
+ it "works" do
14
+ y = Yard.new
15
+ y.save
16
+ y.mowed.should be_nil
17
+ y.mow("front")
18
+
19
+ y.reload
20
+ y.mowed.should eq "front"
21
+ end
22
+
23
+ end
@@ -0,0 +1,28 @@
1
+ require 'async'
2
+ require 'async/resque'
3
+ require 'shared/models'
4
+ require 'redis'
5
+ require 'resque'
6
+
7
+ describe "Async resque" do
8
+ before(:all) do
9
+ Resque.redis = Redis.new
10
+ Async.backend = Async::ResqueBackend
11
+ end
12
+
13
+ it "works" do
14
+ y = Yard.new
15
+ y.save
16
+ y.mowed.should be_nil
17
+ y.mow("front")
18
+
19
+ worker = Resque::Worker.new("*")
20
+ while job = worker.reserve
21
+ job.perform
22
+ end
23
+
24
+ y.reload
25
+ y.mowed.should eq "front"
26
+ end
27
+
28
+ end
@@ -0,0 +1,54 @@
1
+ require 'cubbyhole/base'
2
+ class Yard < Cubbyhole::Base
3
+
4
+ def self.backend
5
+ @backend ||= Hash.new
6
+ end
7
+
8
+ def self.find(id)
9
+ get(id)
10
+ end
11
+
12
+ def do_all_the_work(which)
13
+ Async::Locked.run{ now_do_all_the_work(which) }
14
+ end
15
+
16
+ def now_do_all_the_work(which)
17
+ # puts "do_all_the_work"
18
+ trim(which)
19
+ mow(which)
20
+ end
21
+
22
+ def trim(which)
23
+ Async::Locked.run{ now_trim(which) }
24
+ end
25
+
26
+ def now_trim(which)
27
+ # puts "trim #{which}"
28
+ end
29
+
30
+ def mow(which)
31
+ Async.run{ now_mow(which) }
32
+ end
33
+ def now_mow(which)
34
+ # puts "mow #{which}"
35
+ self.mowed = which
36
+ self.save
37
+ Yard.burn
38
+ end
39
+
40
+ def self.burn
41
+ Async.run{ now_burn }
42
+ end
43
+ def self.now_burn
44
+ # puts "burning..."
45
+ end
46
+
47
+ def conflict
48
+ Async::Locked.run{ now_conflict }
49
+ end
50
+ def now_conflict
51
+ # puts "conflict"
52
+ end
53
+
54
+ end
@@ -0,0 +1,23 @@
1
+ require 'async'
2
+ require 'async/sidekiq'
3
+ require 'shared/models'
4
+ require 'sidekiq/testing'
5
+
6
+ describe "Async sidekiq" do
7
+ before(:all) do
8
+ Async.backend = Async::SidekiqBackend
9
+ end
10
+
11
+ it "works" do
12
+ y = Yard.new
13
+ y.save
14
+ y.mowed.should be_nil
15
+ y.mow("front")
16
+
17
+ Async::SidekiqBackend::Job.drain
18
+
19
+ y.reload
20
+ y.mowed.should eq "front"
21
+ end
22
+
23
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: async-jobs
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ platform: ruby
6
+ authors:
7
+ - Jacob
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-03-30 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email: jacob@engineyard.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - ".gitignore"
20
+ - Gemfile
21
+ - Gemfile.lock
22
+ - MIT-LICENSE
23
+ - README.md
24
+ - async.gemspec
25
+ - lib/async.rb
26
+ - lib/async/locked.rb
27
+ - lib/async/qu.rb
28
+ - lib/async/resque.rb
29
+ - lib/async/sidekiq.rb
30
+ - lib/async/version.rb
31
+ - spec/async_and_lock_spec.rb
32
+ - spec/basics_spec.rb
33
+ - spec/qu_spec.rb
34
+ - spec/resque_spec.rb
35
+ - spec/shared/models.rb
36
+ - spec/sidekiq_spec.rb
37
+ homepage: https://github.com/engineyard/async
38
+ licenses: []
39
+ metadata: {}
40
+ post_install_message:
41
+ rdoc_options: []
42
+ require_paths:
43
+ - lib
44
+ required_ruby_version: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirements: []
55
+ rubyforge_project:
56
+ rubygems_version: 2.5.1
57
+ signing_key:
58
+ specification_version: 4
59
+ summary: abstraction over background job systems
60
+ test_files:
61
+ - spec/async_and_lock_spec.rb
62
+ - spec/basics_spec.rb
63
+ - spec/qu_spec.rb
64
+ - spec/resque_spec.rb
65
+ - spec/shared/models.rb
66
+ - spec/sidekiq_spec.rb
67
+ has_rdoc: