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 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