actionable 0.1.0 → 0.1.1

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: b187586cc793a33104a2f5c1710daefae5d25023
4
- data.tar.gz: 77797479469c0474aa59cc8c545160086ba50033
3
+ metadata.gz: c16c458ffbb1eb2bbbfde2247d0d86fbfe298bad
4
+ data.tar.gz: 8b991e118cc9296f7e5a672a50f099b67bb64778
5
5
  SHA512:
6
- metadata.gz: 535ef546df907bbdc4eb4b1564bf8272185cd8724842394f216f25c98f0dd77c65111add75475a412ebc88f945453f2e4a11d39659da394d3b144472a7479ec1
7
- data.tar.gz: dd8b4beee3770047d760f8f126907358407931d072e9eca60825b2cc43d177e5c229ca2d7ab90b50e6e5a40eca5e7ac481f1cc388f17b4e9f9aa0e9b5c15ddcb
6
+ metadata.gz: ea836f60b3bf4e816216e2134110d9682042793e7f3ea3a90745ddd43c0ec40191e1808ece4a67c793757c05a1ca6c97b2d4d278559f29d4bad1aadd195c3b41
7
+ data.tar.gz: 79f948b69c68e3c3e9b3a85ab57048d33755a3d323d378ba9707cf315c8ca8a279221f570519cf43f079a7487430c7d686370d30cf70329ee989ea0b381766cf
data/Rakefile CHANGED
@@ -4,19 +4,22 @@ require 'resque/tasks'
4
4
  require 'rspec/core/rake_task'
5
5
 
6
6
  task "resque:setup" do
7
- puts File.expand_path('../spec/support/*.rb', __FILE__)
8
- Dir[File.expand_path('../spec/support/*.rb', __FILE__)].each do |f|
9
- puts f
7
+
8
+ Dir[File.expand_path('../spec/support/*.rb', __FILE__)].each do |f|
10
9
  require f
11
10
  end
12
11
 
13
- puts TestJob.class.to_s
14
12
  Mongoid.configure do |config|
15
13
  config.connect_to('actionable_test')
16
14
  end
15
+
17
16
  end
18
17
 
19
18
 
20
- RSpec::Core::RakeTask.new(:spec)
19
+ task :spec do
20
+ Dir['spec/*_spec.rb'] .each do |f|
21
+ system("rspec #{f}")
22
+ end
23
+ end
21
24
 
22
- task :default => :spec
25
+ task :default => :spec
@@ -1,22 +1,49 @@
1
1
  module Actionable
2
2
  class Job
3
-
3
+
4
4
  attr_reader :actionable, :target, :payload
5
5
 
6
+ def self.logger
7
+ defined?(Rails) ? Rails.logger : Resque.logger
8
+ end
9
+
10
+ def logger
11
+ self.class.logger
12
+ end
13
+
6
14
  def self.queue
7
15
  @queue ||= 'actionable'
8
16
  end
9
17
 
10
- # def self.after_enqueue(id)
11
- # actionable = Actionable::Action.find(id)
12
- # actionable.update_attributes(status: :enqueued)
13
- # end
18
+ def self.before_enqueue(id)
19
+ actionable = Actionable::Action.find(id)
20
+ actionable.update_attributes(status: :enqueued)
21
+ true
22
+ end
23
+
24
+ def self.on_failure(err,id)
25
+ begin
26
+ actionable = Actionable::Action.find(id)
27
+ actionable.update_attributes({
28
+ status: :failed,
29
+ exception_message: err.message,
30
+ exception_backtrace: err.backtrace
31
+ })
32
+ job = new(actionable.target,actionable.payload,actionable)
33
+ job.on_failure(err) if job.respond_to?(:on_failure)
34
+ rescue => e
35
+ puts "Error in 'on_failure' handler: #{e.message}"
36
+ logger.error("Error in 'on_failure' handler: #{e.message}")
37
+ end
38
+ end
14
39
 
15
40
  def self.perform(id)
16
41
  actionable = Actionable::Action.find(id)
17
42
  actionable.update_attributes(status: :working)
18
- new(actionable.target,actionable.payload,actionable).perform
43
+ job = new(actionable.target,actionable.payload,actionable)
44
+ job.perform
19
45
  actionable.update_attributes(status: :complete)
46
+ job.on_success if job.respond_to?(:on_success)
20
47
  end
21
48
 
22
49
  def initialize(target,payload={},actionable=nil)
@@ -24,6 +51,6 @@ module Actionable
24
51
  @actionable = actionable
25
52
  @payload = HashWithIndifferentAccess.new(payload)
26
53
  end
27
-
54
+
28
55
  end
29
- end
56
+ end
@@ -9,15 +9,17 @@ module Actionable
9
9
  store_in(collection: 'actionables')
10
10
 
11
11
  after_initialize :flag_late
12
-
12
+
13
13
  field :execution_time, type: Time
14
14
  field :payload, type: Hash
15
15
  field :job_class_name, type: String
16
16
  field :status, type: Symbol, default: :new
17
17
  field :target_id, type: Moped::BSON::ObjectId
18
18
  field :target_class_name, type: String
19
+ field :exception_message, type: String
20
+ field :exception_backtrace, type: Array
19
21
 
20
- scope :scheduled_for_between, ->(from,to){
22
+ scope :scheduled_for_between, ->(from,to){
21
23
  between(execution_time:[from,to])
22
24
  }
23
25
  scope :scheduled_for_before, ->(to){ where(:execution_time.lt => to) }
@@ -1,3 +1,3 @@
1
1
  module Actionable
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -0,0 +1,86 @@
1
+ require "spec_helper"
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("killall resque-1.24.1: Waiting for actionable,actionable_sweep")
13
+ end
14
+
15
+ end
16
+
17
+ def run_test_worker(id)
18
+ w = TestWorker.new
19
+ w.spawn
20
+ poll_for_finished(id)
21
+ w.kill
22
+ end
23
+
24
+ def poll_for_finished(id)
25
+ until TestModel.find(id).actionables.first.reload.status == :failed
26
+ sleep(1)
27
+ end
28
+ end
29
+
30
+ describe "Failing Actionable::Job" do
31
+
32
+ before do
33
+ system('killall resque-1.24.1: Waiting for actionable,actionable_sweep')
34
+ end
35
+
36
+ after do
37
+ system('killall resque-1.24.1: Waiting for actionable,actionable_sweep')
38
+ end
39
+
40
+
41
+ context "scheduled for one minute ago" do
42
+
43
+ it "should be executed by a worker manually" do
44
+ Resque.inline = true
45
+ target = TestModel.create({
46
+ color:'blue',
47
+ shape:'square',
48
+ number:3
49
+ })
50
+ target.schedule_actionable(1.minute.ago,TestFailureJob,{number:2})
51
+ id = target.id
52
+ expect(TestFailureJob).to receive(:on_fail)
53
+ # expect(Resque::Failure).to receive(:create)
54
+ expect {
55
+ Actionable::Sweep.perform
56
+ }.to raise_error(StandardError)
57
+ expect(TestModel.find(id).number).to_not eq(6)
58
+ expect(TestModel.find(id).name).to_not eq('blue square')
59
+ expect(Actionable::Action.last.status).to eq(:failed)
60
+ expect(Actionable::Action.last.exception_message).to eq('This is my error')
61
+ Resque.inline = false
62
+ end
63
+
64
+ it "should be executed by a worker in another processes", :no do
65
+ target = TestModel.create({
66
+ color:'blue',
67
+ shape:'square',
68
+ number:3
69
+ })
70
+ target.schedule_actionable(1.minute.ago,TestFailureJob,{number:2})
71
+ id = target.id
72
+ run_test_worker(id)
73
+ expect(TestModel.find(id).number).to_not eq(6)
74
+ expect(TestModel.find(id).name).to_not eq('blue square')
75
+ expect(Actionable::Action.last.status).to eq(:failed)
76
+ expect(Actionable::Action.last.exception_message).to eq('This is my error')
77
+
78
+ error_json = Redis.new.rpop('resque:failed')
79
+ error_hash = JSON.load(error_json)
80
+ expect(error_hash.fetch('error')).to eq("This is my error")
81
+ expect(error_hash.fetch('payload').fetch('args').first.to_s).to eq(Actionable::Action.last.id.to_s)
82
+ end
83
+
84
+ end
85
+
86
+ end
@@ -9,7 +9,7 @@ class TestWorker
9
9
  end
10
10
 
11
11
  def kill
12
- system("kill -9 #{pid}")
12
+ system('killall resque-1.24.1: Waiting for actionable,actionable_sweep')
13
13
  end
14
14
 
15
15
  end
@@ -29,34 +29,53 @@ end
29
29
 
30
30
  describe Actionable::Job do
31
31
 
32
+ before do
33
+ system('killall resque-1.24.1: Waiting for actionable,actionable_sweep')
34
+ end
35
+
36
+ after do
37
+ system('killall resque-1.24.1: Waiting for actionable,actionable_sweep')
38
+ end
39
+
32
40
  context "scheduled for one minute ago" do
33
41
 
34
- let(:target) {
35
- TestModel.create({
42
+ it "should be executed by a worker manually" do
43
+ DOUBLE = double()
44
+ expect(DOUBLE).to receive(:success)
45
+
46
+ def TestJob.on_success(s)
47
+ DOUBLE.success
48
+ end
49
+
50
+ Resque.inline = true
51
+ target = TestModel.create({
36
52
  color:'blue',
37
53
  shape:'square',
38
54
  number:3
39
55
  })
40
- }
41
-
42
- before do
43
- target.schedule_actionable(1.minute.ago,TestJob,{number:2})
44
- end
45
-
46
- it "should be executed by a worker manually" do
47
- Resque.inline = true
56
+ target.schedule_actionable(1.minute.ago,TestJob,{number:2})
48
57
  id = target.id
58
+
49
59
  Actionable::Sweep.perform
50
60
  expect(TestModel.find(id).number).to eq(6)
51
61
  expect(TestModel.find(id).name).to eq('blue square')
62
+ expect(Actionable::Action.last.status).to eq(:complete)
52
63
  Resque.inline = false
53
64
  end
54
65
 
55
66
  it "should be executed by a worker in another processes", :no do
67
+ target = TestModel.create({
68
+ color:'blue',
69
+ shape:'square',
70
+ number:3
71
+ })
72
+ target.schedule_actionable(1.minute.ago,TestJob,{number:2})
56
73
  id = target.id
57
74
  run_test_worker(id)
58
75
  expect(TestModel.find(id).number).to eq(6)
59
76
  expect(TestModel.find(id).name).to eq('blue square')
77
+
78
+
60
79
  end
61
80
 
62
81
  end
@@ -1,26 +1,34 @@
1
1
  require "bundler"
2
2
  Bundler.require(:default,:development)
3
-
3
+
4
4
  Dir[File.expand_path('../support/*.rb', __FILE__)].each {|f| require f}
5
5
 
6
6
  Mongoid.configure do |config|
7
7
  config.connect_to('actionable_test')
8
8
  end
9
9
 
10
- Resque.redis = Redis.new
11
- Resque.redis.namespace = "resque:actionable_test:"
12
-
13
10
  module Resque
14
11
  def self.purge!
15
12
  self.redis.keys('*')
16
13
  self.redis.del(*keys) unless keys.count == 0
17
14
  end
15
+ # def failure_count
16
+ # Resque.redis = Redis.new
17
+ # count = Resque::Failure.count
18
+ # Resque.redis.namespace = "resque:actionable_test:"
19
+ # count
20
+ # end
18
21
  end
19
22
 
20
23
  RSpec.configure do |config|
21
24
 
22
25
  config.treat_symbols_as_metadata_keys_with_true_values = true
23
26
 
27
+ config.before(:each) do
28
+ Resque.redis = Redis.new
29
+ Resque.redis.namespace = "resque:actionable_test:"
30
+ end
31
+
24
32
  config.after(:each) do
25
33
  Resque.purge!
26
34
  Mongoid.purge!
@@ -0,0 +1,15 @@
1
+ class TestFailureJob < Actionable::Job
2
+
3
+ def perform
4
+ target.name = target.say_something
5
+ raise StandardError.new('This is my error')
6
+ target.number = target.number * payload[:number]
7
+ target.save
8
+ end
9
+
10
+ def on_failure(err)
11
+ self.class.on_fail
12
+ end
13
+
14
+
15
+ end
@@ -6,5 +6,8 @@ class TestJob < Actionable::Job
6
6
  target.save
7
7
  end
8
8
 
9
+ def on_success
10
+ self.class.on_success(self)
11
+ end
9
12
 
10
- end
13
+ end
@@ -8,8 +8,8 @@ class TestModel
8
8
  field :shape
9
9
  field :number
10
10
 
11
- def say_something
11
+ def say_something(arg = nil)
12
12
  return "#{color} #{shape}"
13
13
  end
14
14
 
15
- end
15
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: actionable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Daniel Luxemburg
@@ -205,10 +205,12 @@ files:
205
205
  - lib/actionable/tasks.rb
206
206
  - lib/actionable/version.rb
207
207
  - spec/action_spec.rb
208
+ - spec/failure_spec.rb
208
209
  - spec/job_spec.rb
209
210
  - spec/recurrence_spec.rb
210
211
  - spec/spec_helper.rb
211
212
  - spec/support/recurring_test_job.rb
213
+ - spec/support/test_failure_job.rb
212
214
  - spec/support/test_job.rb
213
215
  - spec/support/test_model.rb
214
216
  - spec/sweep_spec.rb
@@ -240,10 +242,12 @@ summary: Store stuff in Mongo, keep track of it in Redis, log lots of stuff, inc
240
242
  test help
241
243
  test_files:
242
244
  - spec/action_spec.rb
245
+ - spec/failure_spec.rb
243
246
  - spec/job_spec.rb
244
247
  - spec/recurrence_spec.rb
245
248
  - spec/spec_helper.rb
246
249
  - spec/support/recurring_test_job.rb
250
+ - spec/support/test_failure_job.rb
247
251
  - spec/support/test_job.rb
248
252
  - spec/support/test_model.rb
249
253
  - spec/sweep_spec.rb