actionable 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 35e414933341959d1347c1cb41e4e2bae53e880d
4
- data.tar.gz: 19d6241afa6343d0a0e69033fafe1600fa54655e
3
+ metadata.gz: 45a9bd1c736c4e4a5ad2b7346e96604fe762252f
4
+ data.tar.gz: afd9fa6c64ff5ca69285019c34605ed46c3abbdd
5
5
  SHA512:
6
- metadata.gz: 4e3ca8e2f1875fd0a5301f96e6293ca62c60dcb835cd21442779995489730c0056ea2d14d81dad33110ea450964707c459d0bc22f179ce59b94b80bd37d1fb56
7
- data.tar.gz: 9815bef64be1a7d64100144278bf6d8404551915f618f9215e4f5910178c77b736de4eefb6f69b44f5741bd99646cdc6d1e312f0cee3d9507b4a0f0611944302
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: 'defunkt/resque', branch: 'master'
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
+ [![Code Climate](https://codeclimate.com/github/dluxemburg/actionable.png)](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
@@ -12,6 +12,8 @@ require "actionable/mongoid_store/action"
12
12
  require "actionable/target"
13
13
  require "actionable/sweep"
14
14
  require "actionable/job"
15
+ require "actionable/recurrence"
16
+
15
17
 
16
18
  module Actionable
17
19
  def self.store
@@ -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
@@ -9,6 +9,7 @@ module Actionable
9
9
 
10
10
  def self.perform
11
11
  Actionable::Action.to_do.each(&:enqueue)
12
+ Actionable::Recurrence.all.each(&:ensure_scheduled)
12
13
  end
13
14
 
14
15
  end
@@ -16,7 +16,6 @@ module Actionable
16
16
  @actionable_errors
17
17
  end
18
18
 
19
-
20
19
  def schedule_actionable(execution_time,job_class_name,payload={})
21
20
 
22
21
  # force memoized values to refresh
@@ -1,3 +1,3 @@
1
1
  module Actionable
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -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 kill_test_worker
9
- pid = File.new(File.expand_path("../../resque.pid",__FILE__)).read
10
- system("kill -9 #{pid}")
11
- system("rm resque.pid")
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 run_test_worker
15
- start_test_worker
16
- sleep(5)
17
- kill_test_worker
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.before(:each) do
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
@@ -0,0 +1,12 @@
1
+ class RecurringTestJob < Actionable::Job
2
+
3
+ def perform
4
+ RecurringTestJob.go
5
+ end
6
+
7
+ def self.go
8
+
9
+ end
10
+
11
+
12
+ end
data/spec/sweep_spec.rb CHANGED
@@ -6,31 +6,35 @@ describe Actionable::Sweep do
6
6
  TestModel.create
7
7
  }
8
8
 
9
- before do
10
- target.schedule_actionable(1.hour.from_now,TestJob)
11
- Timecop.travel(2.hours.from_now)
12
- end
13
-
14
- it "should leave one job in the to do list before being performed" do
15
- expect(Resque.size('actionables')).to eq(0)
16
- expect(Actionable::Action.to_do.count).to eq(1)
17
- end
18
-
19
- it "should move one job from the to do list to the queue after being performed" do
20
- Actionable::Sweep.perform
21
- expect(Actionable::Action.to_do.count).to eq(0)
22
- expect(Resque.size('actionables')).to eq(1)
23
- end
24
-
25
- pending "should enqueue a job that knows the id for its target" do
26
- Actionable::Sweep.perform
27
- expect(Resque.queue('actionables').pop['args'].first).to eq(target.actionables.first.id.to_s)
28
- end
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.1
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-03-23 00:00:00.000000000 Z
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: '0'
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: '0'
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: '0'
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: '0'
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/schedule_spec.rb
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/work_spec.rb
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/schedule_spec.rb
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/work_spec.rb
236
+ - spec/target_spec.rb