actionable 0.0.1 → 0.0.2
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/Gemfile +1 -7
- data/README.md +5 -0
- data/Rakefile +7 -1
- data/actionable.gemspec +2 -2
- data/lib/actionable.rb +2 -0
- data/lib/actionable/recurrence.rb +61 -0
- data/lib/actionable/sweep.rb +1 -0
- data/lib/actionable/target.rb +0 -1
- data/lib/actionable/version.rb +1 -1
- data/spec/{work_spec.rb → job_spec.rb} +21 -11
- data/spec/recurrence_spec.rb +99 -0
- data/spec/spec_helper.rb +8 -1
- data/spec/support/recurring_test_job.rb +12 -0
- data/spec/sweep_spec.rb +28 -24
- data/spec/{schedule_spec.rb → target_spec.rb} +0 -0
- metadata +19 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 45a9bd1c736c4e4a5ad2b7346e96604fe762252f
|
4
|
+
data.tar.gz: afd9fa6c64ff5ca69285019c34605ed46c3abbdd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d63c6d2439e5923342173b1b998e122876c4d4533e24782fd2f4e5898ff0e57ed64dcbfe4306622684f5f1872227c203918fd0e579e5c41af2b502f27ccd61ec
|
7
|
+
data.tar.gz: b64f3d371ce3c95716162ed3ebf215351176253497ff7837d051b6966643aae9210101031a7d5b0b4b974738b076aa4d49a1f786327d21fc1e3d1e9eedb95df9
|
data/Gemfile
CHANGED
@@ -1,11 +1,5 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
|
-
gem 'resque', github: '
|
4
|
-
gem 'redis'
|
5
|
-
gem 'redis-namespace'
|
6
|
-
|
3
|
+
gem 'resque', github: 'resque/resque', branch: 'master'
|
7
4
|
gem 'mongoid', github: 'mongoid/mongoid', branch: 'master'
|
8
|
-
gem 'moped'
|
9
|
-
gem 'origin'
|
10
|
-
|
11
5
|
gemspec
|
data/README.md
CHANGED
@@ -1,5 +1,10 @@
|
|
1
|
+
|
1
2
|
# Actionable
|
2
3
|
|
4
|
+
[](https://codeclimate.com/github/dluxemburg/actionable)
|
5
|
+
|
6
|
+
<img src="https://semaphoreapp.com/api/v1/projects/d32a2e65068b7b995bd9e9e2f8beb99a64c7f98d/36068/badge.png">
|
7
|
+
|
3
8
|
TODO: Write a gem description
|
4
9
|
|
5
10
|
## Installation
|
data/Rakefile
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require 'actionable/tasks'
|
3
3
|
require 'resque/tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
4
5
|
|
5
6
|
task "resque:setup" do
|
6
7
|
puts File.expand_path('../spec/support/*.rb', __FILE__)
|
@@ -13,4 +14,9 @@ task "resque:setup" do
|
|
13
14
|
Mongoid.configure do |config|
|
14
15
|
config.connect_to('actionable_test')
|
15
16
|
end
|
16
|
-
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
RSpec::Core::RakeTask.new(:spec)
|
21
|
+
|
22
|
+
task :default => :spec
|
data/actionable.gemspec
CHANGED
@@ -27,8 +27,8 @@ Gem::Specification.new do |spec|
|
|
27
27
|
spec.add_development_dependency "rb-fsevent"
|
28
28
|
spec.add_development_dependency "terminal-notifier-guard"
|
29
29
|
|
30
|
-
spec.add_dependency "resque"
|
31
|
-
spec.add_dependency "mongoid"
|
30
|
+
spec.add_dependency "resque", "~> 2.0.0.pre.1"
|
31
|
+
spec.add_dependency "mongoid", "~> 4.0.0"
|
32
32
|
spec.add_dependency "rufus-scheduler"
|
33
33
|
|
34
34
|
|
data/lib/actionable.rb
CHANGED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'rufus/scheduler'
|
2
|
+
|
3
|
+
module Actionable
|
4
|
+
class Recurrence
|
5
|
+
|
6
|
+
include Mongoid::Document
|
7
|
+
include Actionable::Target
|
8
|
+
|
9
|
+
store_in(collection: 'recurring_actionables')
|
10
|
+
|
11
|
+
field :recurrence_string
|
12
|
+
field :job_class, type: String
|
13
|
+
field :payload, type: Hash, default: {}
|
14
|
+
|
15
|
+
after_create :schedule_next
|
16
|
+
|
17
|
+
validate :valid_recurrence_string
|
18
|
+
validate :valid_job_class
|
19
|
+
|
20
|
+
def schedule_next
|
21
|
+
schedule_actionable(next_run_time,job_class,payload)
|
22
|
+
end
|
23
|
+
|
24
|
+
def next_scheduled?
|
25
|
+
Actionable::Action.where(status: :scheduled, target_id: id).exists?
|
26
|
+
end
|
27
|
+
|
28
|
+
def ensure_scheduled
|
29
|
+
schedule_next unless next_scheduled?
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def next_run_time
|
35
|
+
Rufus::CronLine.new(recurrence_string).next_time.to_datetime
|
36
|
+
end
|
37
|
+
|
38
|
+
def job_class
|
39
|
+
unless read_attribute(:job_class).blank?
|
40
|
+
read_attribute(:job_class).constantize
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def valid_recurrence_string
|
45
|
+
begin
|
46
|
+
next_run_time
|
47
|
+
rescue => e
|
48
|
+
errors.add(:recurrence_string,e.message)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def valid_job_class
|
53
|
+
begin
|
54
|
+
job_class
|
55
|
+
rescue => e
|
56
|
+
errors.add(:job_class,e.message)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
end
|
data/lib/actionable/sweep.rb
CHANGED
data/lib/actionable/target.rb
CHANGED
data/lib/actionable/version.rb
CHANGED
@@ -1,20 +1,30 @@
|
|
1
1
|
require "spec_helper"
|
2
2
|
|
3
|
+
class TestWorker
|
4
|
+
|
5
|
+
attr_accessor :pid
|
6
|
+
|
7
|
+
def spawn
|
8
|
+
@pid = Kernel.spawn('bundle exec rake resque:work QUEUES=actionable,actionable_sweep')
|
9
|
+
end
|
10
|
+
|
11
|
+
def kill
|
12
|
+
system("kill -9 #{pid}")
|
13
|
+
end
|
3
14
|
|
4
|
-
def start_test_worker
|
5
|
-
spawn('bundle exec rake resque:work PIDFILE=resque.pid QUEUES=actionable,actionable_sweep INTERVAL=1')
|
6
15
|
end
|
7
16
|
|
8
|
-
def
|
9
|
-
|
10
|
-
|
11
|
-
|
17
|
+
def run_test_worker(id)
|
18
|
+
w = TestWorker.new
|
19
|
+
w.spawn
|
20
|
+
poll_for_finished(id)
|
21
|
+
w.kill
|
12
22
|
end
|
13
23
|
|
14
|
-
def
|
15
|
-
|
16
|
-
|
17
|
-
|
24
|
+
def poll_for_finished(id)
|
25
|
+
while TestModel.find(id).reload.number == 3
|
26
|
+
sleep(1)
|
27
|
+
end
|
18
28
|
end
|
19
29
|
|
20
30
|
describe Actionable::Job do
|
@@ -44,7 +54,7 @@ describe Actionable::Job do
|
|
44
54
|
|
45
55
|
it "should be executed by a worker in another processes" do
|
46
56
|
id = target.id
|
47
|
-
run_test_worker
|
57
|
+
run_test_worker(id)
|
48
58
|
expect(TestModel.find(id).number).to eq(6)
|
49
59
|
expect(TestModel.find(id).name).to eq('blue square')
|
50
60
|
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Actionable::Recurrence do
|
4
|
+
|
5
|
+
let(:every_five){ "*/5 * * * *" }
|
6
|
+
let(:new_args){
|
7
|
+
{ recurrence_string:every_five, job_class: RecurringTestJob }
|
8
|
+
}
|
9
|
+
|
10
|
+
let(:recurrence) {
|
11
|
+
Actionable::Recurrence.create(new_args)
|
12
|
+
}
|
13
|
+
|
14
|
+
it "should be instantiated with a recurrence string and a job class" do
|
15
|
+
r = Actionable::Recurrence.new(new_args)
|
16
|
+
expect(r).to be_valid
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should refuse to save with a bad recurrence string" do
|
20
|
+
new_args[:recurrence_string] = '* D * L * . *'
|
21
|
+
r = Actionable::Recurrence.new(new_args)
|
22
|
+
expect(r).to_not be_valid
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should refuse to save with a non-existent class" do
|
26
|
+
new_args[:job_class] = "NonExistentJob"
|
27
|
+
r = Actionable::Recurrence.new(new_args)
|
28
|
+
expect(r).to_not be_valid
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
it "should not add to the database until saved" do
|
33
|
+
r = Actionable::Recurrence.new(new_args)
|
34
|
+
expect(Actionable::Recurrence.count).to eq(0)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should add to the database afer being committed" do
|
38
|
+
r = Actionable::Recurrence.new(new_args)
|
39
|
+
r.save
|
40
|
+
expect(Actionable::Recurrence.count).to eq(1)
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should know its job class" do
|
44
|
+
expect(recurrence.send(:job_class)).to eq(RecurringTestJob)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should create an actionable upon creation" do
|
48
|
+
expect(Actionable::Action.count).to eq(0)
|
49
|
+
Actionable::Recurrence.create(new_args)
|
50
|
+
expect(Actionable::Action.count).to eq(1)
|
51
|
+
end
|
52
|
+
|
53
|
+
context "when time passes" do
|
54
|
+
|
55
|
+
before do
|
56
|
+
Resque.inline = true
|
57
|
+
recurrence.valid?
|
58
|
+
Timecop.travel(5.minutes.from_now)
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should run the first action and create a new one on sweep" do
|
62
|
+
expect(Actionable::Action.count).to eq(1)
|
63
|
+
Actionable::Sweep.perform
|
64
|
+
expect(Actionable::Action.count).to eq(2)
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when no time passes" do
|
70
|
+
|
71
|
+
before do
|
72
|
+
Resque.inline = true
|
73
|
+
recurrence.valid?
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should not run the first action or create a new one" do
|
77
|
+
expect(Actionable::Action.count).to eq(1)
|
78
|
+
Actionable::Sweep.perform
|
79
|
+
expect(Actionable::Action.count).to eq(1)
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
|
84
|
+
context "over the course of an hour" do
|
85
|
+
|
86
|
+
it "should create and perform the job 11 times" do
|
87
|
+
RecurringTestJob.should_receive(:go).exactly(12).times
|
88
|
+
Timecop.travel(1.hour.from_now.change(minutes:0))
|
89
|
+
recurrence.valid?
|
90
|
+
60.times do
|
91
|
+
Actionable::Sweep.perform
|
92
|
+
Timecop.travel(1.minute.from_now)
|
93
|
+
end
|
94
|
+
expect(Actionable::Action.count).to be_within(1).of(12)
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -19,10 +19,17 @@ end
|
|
19
19
|
|
20
20
|
RSpec.configure do |config|
|
21
21
|
|
22
|
-
config.
|
22
|
+
config.after(:each) do
|
23
23
|
Resque.purge!
|
24
24
|
Mongoid.purge!
|
25
25
|
Timecop.return
|
26
26
|
end
|
27
27
|
|
28
|
+
config.before(:all) do
|
29
|
+
Resque.purge!
|
30
|
+
Mongoid.purge!
|
31
|
+
Timecop.return
|
32
|
+
Resque.inline = false
|
33
|
+
end
|
34
|
+
|
28
35
|
end
|
data/spec/sweep_spec.rb
CHANGED
@@ -6,31 +6,35 @@ describe Actionable::Sweep do
|
|
6
6
|
TestModel.create
|
7
7
|
}
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
9
|
+
context "single jobs" do
|
10
|
+
|
11
|
+
before do
|
12
|
+
target.schedule_actionable(1.hour.from_now,TestJob)
|
13
|
+
Timecop.travel(2.hours.from_now)
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should leave one job in the to do list before being performed" do
|
17
|
+
expect(Resque.size('actionable')).to eq(0)
|
18
|
+
expect(Actionable::Action.to_do.count).to eq(1)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should move one job from the to do list to the queue after being performed" do
|
22
|
+
Actionable::Sweep.perform
|
23
|
+
expect(Actionable::Action.to_do.count).to eq(0)
|
24
|
+
expect(Resque.size('actionable')).to eq(1)
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should enqueue a job that knows the id for its target" do
|
28
|
+
Actionable::Sweep.perform
|
29
|
+
expect(Resque.queue('actionable').pop['args'].first).to eq(target.actionables.first.id.to_s)
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should update the actionable's status to 'enqueued'" do
|
33
|
+
expect(target.actionables.first.status).to eq(:scheduled)
|
34
|
+
Actionable::Sweep.perform
|
35
|
+
expect(target.actionables.first.status).to eq(:enqueued)
|
36
|
+
end
|
29
37
|
|
30
|
-
it "should update the actionable's status to 'enqueued'" do
|
31
|
-
expect(target.actionables.first.status).to eq(:scheduled)
|
32
|
-
Actionable::Sweep.perform
|
33
|
-
expect(target.actionables.first.status).to eq(:enqueued)
|
34
38
|
end
|
35
39
|
|
36
40
|
end
|
File without changes
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: actionable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Luxemburg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-04-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -126,30 +126,30 @@ dependencies:
|
|
126
126
|
name: resque
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- -
|
129
|
+
- - ~>
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version:
|
131
|
+
version: 2.0.0.pre.1
|
132
132
|
type: :runtime
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- -
|
136
|
+
- - ~>
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version:
|
138
|
+
version: 2.0.0.pre.1
|
139
139
|
- !ruby/object:Gem::Dependency
|
140
140
|
name: mongoid
|
141
141
|
requirement: !ruby/object:Gem::Requirement
|
142
142
|
requirements:
|
143
|
-
- -
|
143
|
+
- - ~>
|
144
144
|
- !ruby/object:Gem::Version
|
145
|
-
version:
|
145
|
+
version: 4.0.0
|
146
146
|
type: :runtime
|
147
147
|
prerelease: false
|
148
148
|
version_requirements: !ruby/object:Gem::Requirement
|
149
149
|
requirements:
|
150
|
-
- -
|
150
|
+
- - ~>
|
151
151
|
- !ruby/object:Gem::Version
|
152
|
-
version:
|
152
|
+
version: 4.0.0
|
153
153
|
- !ruby/object:Gem::Dependency
|
154
154
|
name: rufus-scheduler
|
155
155
|
requirement: !ruby/object:Gem::Requirement
|
@@ -184,18 +184,21 @@ files:
|
|
184
184
|
- lib/actionable/memory_store/action.rb
|
185
185
|
- lib/actionable/mongoid_store.rb
|
186
186
|
- lib/actionable/mongoid_store/action.rb
|
187
|
+
- lib/actionable/recurrence.rb
|
187
188
|
- lib/actionable/resque.rb
|
188
189
|
- lib/actionable/sweep.rb
|
189
190
|
- lib/actionable/target.rb
|
190
191
|
- lib/actionable/tasks.rb
|
191
192
|
- lib/actionable/version.rb
|
192
193
|
- spec/action_spec.rb
|
193
|
-
- spec/
|
194
|
+
- spec/job_spec.rb
|
195
|
+
- spec/recurrence_spec.rb
|
194
196
|
- spec/spec_helper.rb
|
197
|
+
- spec/support/recurring_test_job.rb
|
195
198
|
- spec/support/test_job.rb
|
196
199
|
- spec/support/test_model.rb
|
197
200
|
- spec/sweep_spec.rb
|
198
|
-
- spec/
|
201
|
+
- spec/target_spec.rb
|
199
202
|
homepage: ''
|
200
203
|
licenses:
|
201
204
|
- MIT
|
@@ -223,9 +226,11 @@ summary: Store stuff in Mongo, keep track of it in Redis, log lots of stuff, inc
|
|
223
226
|
test help
|
224
227
|
test_files:
|
225
228
|
- spec/action_spec.rb
|
226
|
-
- spec/
|
229
|
+
- spec/job_spec.rb
|
230
|
+
- spec/recurrence_spec.rb
|
227
231
|
- spec/spec_helper.rb
|
232
|
+
- spec/support/recurring_test_job.rb
|
228
233
|
- spec/support/test_job.rb
|
229
234
|
- spec/support/test_model.rb
|
230
235
|
- spec/sweep_spec.rb
|
231
|
-
- spec/
|
236
|
+
- spec/target_spec.rb
|