delayed_resque 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NjE0NjkzODhkNGI2NDNlMTgwYzAzYzBiZWI5MDlhYzkwMGNkYTM2OA==
5
+ data.tar.gz: !binary |-
6
+ NTI0MDE4ZDA4ZjZiMTM3ZjNhYzJkYjllNDRlNGM4YjYzZjRmYjNlOA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ YjliYTNkNjBjMzAzNDNmZTcwN2IwMzg4MzE0ODRkMjBiNmU0MDJmZDVjNjY4
10
+ NzhkMDdmY2NmMjEzM2Y1NzUyZjg0MDhhNGJhMWRhMmFiM2Q1Y2E5YjBiNjgx
11
+ ZDZhNDgxOTY1NGI3NmJiOWUyYzU2NTFhZDQxMjZmMzdjNDBhMmQ=
12
+ data.tar.gz: !binary |-
13
+ MTY0MTFjOGQ1NjIyYmYxMzMwZjY2ZDM2ZWEzZjVmZGM4NjEzNjJjNWM5YjRl
14
+ NmNiY2FjNmY2OTkwYjM3ZTlkNTBlNTlmMjQ4MzAwYTEwNjBhODc1NmM5Yzg2
15
+ ODY4MzRkYjAxOTVjZDVkMzMwYmE0M2FjYTZhN2EwY2U5NTFmODU=
data/README.md ADDED
@@ -0,0 +1,23 @@
1
+ delayed_resque
2
+ ==============
3
+
4
+ Provides delayed_job syntax to resque.
5
+
6
+ Call `.delay.method(params)` on any object and it will be processed in the background.
7
+
8
+ # with delayed_job
9
+ @user.delay.activate!(@device)
10
+
11
+ Parameters can be scalar values, active record instances, or classes (but not instances of non-AR objects).
12
+
13
+ The queue to use for the method can be specified on the delay method:
14
+
15
+ @user.delay(:queue => :device_activation).activate!(@device)
16
+
17
+ Credits
18
+ -------
19
+
20
+ Based on the work of https://github.com/defunkt/resque and
21
+ https://github.com/collectiveidea/delayed_job.
22
+
23
+ Special Thanks to https://github.com/k1w1 for the spec and the extracting most of the code out of delayed_job
@@ -0,0 +1,61 @@
1
+ require 'active_support/core_ext/module/aliasing'
2
+ module DelayedResque
3
+ class DelayProxy
4
+ def initialize(payload_class, target, options)
5
+ @payload_class = payload_class
6
+ @target = target
7
+ @options = {:queue => "default"}.update(options)
8
+ end
9
+
10
+ def method_missing(method, *args)
11
+
12
+ queue = @options[:queue] || @payload_class.queue
13
+ performable = @payload_class.new(@target, method.to_sym, @options, args)
14
+ if @options[:unique]
15
+ if @options[:run_at] or @options[:in]
16
+ ::Resque.remove_delayed(@payload_class, performable.store)
17
+ else
18
+ ::Resque.dequeue(@payload_class, performable.store)
19
+ end
20
+ end
21
+
22
+
23
+ if @options[:run_at]
24
+ ::Resque.enqueue_at(@options[:run_at], @payload_class, performable.store)
25
+ elsif @options[:in]
26
+ ::Resque.enqueue_in(@options[:in], @payload_class, performable.store)
27
+ else
28
+ ::Resque.enqueue_to(queue, @payload_class, performable.store)
29
+ end
30
+ end
31
+ end
32
+
33
+
34
+ module MessageSending
35
+ def delay(options = {})
36
+ DelayProxy.new(PerformableMethod, self, options)
37
+ end
38
+ alias __delay__ delay
39
+
40
+ module ClassMethods
41
+ def handle_asynchronously(method, opts = {})
42
+ aliased_method, punctuation = method.to_s.sub(/([?!=])$/, ''), $1
43
+ with_method, without_method = "#{aliased_method}_with_delay#{punctuation}", "#{aliased_method}_without_delay#{punctuation}"
44
+ define_method(with_method) do |*args|
45
+ curr_opts = opts.clone
46
+ curr_opts.each_key do |key|
47
+ if (val = curr_opts[key]).is_a?(Proc)
48
+ curr_opts[key] = if val.arity == 1
49
+ val.call(self)
50
+ else
51
+ val.call
52
+ end
53
+ end
54
+ end
55
+ delay(curr_opts).__send__(without_method, *args)
56
+ end
57
+ alias_method_chain method, :delay
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,21 @@
1
+ require 'mail'
2
+
3
+ module DelayedResque
4
+ class PerformableMailer < PerformableMethod
5
+ def self.perform(options)
6
+ PerformableMethod.perform(options).deliver
7
+ end
8
+ end
9
+
10
+ module DelayMail
11
+ def delay(options = {})
12
+ DelayProxy.new(PerformableMailer, self, options)
13
+ end
14
+ end
15
+ end
16
+
17
+ Mail::Message.class_eval do
18
+ def delay(*args)
19
+ raise RuntimeError, "Use MyMailer.delay.mailer_action(args) to delay sending of emails."
20
+ end
21
+ end
@@ -0,0 +1,71 @@
1
+ require 'active_record'
2
+
3
+ module DelayedResque
4
+ class PerformableMethod < Struct.new(:object, :method, :args)
5
+ CLASS_STRING_FORMAT = /^CLASS\:([A-Z][\w\:]+)$/
6
+ AR_STRING_FORMAT = /^AR\:([A-Z][\w\:]+)\:(\d+)$/
7
+
8
+ def initialize(object, method, options, args)
9
+ raise NoMethodError, "undefined method `#{method}' for #{object.inspect}" unless object.respond_to?(method,true)
10
+
11
+ @object = dump(object)
12
+ @method = method.to_sym
13
+ @options = options
14
+ @args = args.map { |a| dump(a) }
15
+ end
16
+
17
+ def display_name
18
+ case self.object
19
+ when CLASS_STRING_FORMAT then "#{$1}.#{method}"
20
+ when AR_STRING_FORMAT then "#{$1}##{method}"
21
+ else "Unknown##{method}"
22
+ end
23
+ end
24
+
25
+ def self.queue
26
+ "default"
27
+ end
28
+
29
+ def self.perform(options)
30
+ object = options["obj"]
31
+ method = options["method"]
32
+ args = options["args"]
33
+ self.load(object).send(method, *args.map{|a| self.load(a)})
34
+ rescue ActiveRecord::RecordNotFound
35
+ Rails.logger.warn("PerformableMethod: failed to find record for #{object.inspect}")
36
+ # We cannot do anything about objects which were deleted in the meantime
37
+ true
38
+ end
39
+
40
+ def store
41
+ {"obj" => @object, "method" => @method, "args" => @args}.merge(@options[:params] || {})
42
+ end
43
+
44
+ private
45
+
46
+ def self.load(arg)
47
+ case arg
48
+ when CLASS_STRING_FORMAT then $1.constantize
49
+ when AR_STRING_FORMAT then $1.constantize.find($2)
50
+ when Hash then ::HashWithIndifferentAccess.new(arg)
51
+ else arg
52
+ end
53
+ end
54
+
55
+ def dump(arg)
56
+ case arg
57
+ when Class, Module then class_to_string(arg)
58
+ when ActiveRecord::Base then ar_to_string(arg)
59
+ else arg
60
+ end
61
+ end
62
+
63
+ def ar_to_string(obj)
64
+ "AR:#{obj.class}:#{obj.id}"
65
+ end
66
+
67
+ def class_to_string(obj)
68
+ "CLASS:#{obj.name}"
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,17 @@
1
+ require 'resque'
2
+ require 'rails'
3
+
4
+ module Delayed
5
+ class Railtie < Rails::Railtie
6
+ initializer :after_initialize do
7
+ ActiveSupport.on_load(:action_mailer) do
8
+ ActionMailer::Base.send(:extend, DelayedResque::DelayMail)
9
+ end
10
+ end
11
+
12
+ rake_tasks do
13
+ load "resque/tasks.rb"
14
+ load "resque_scheduler/tasks.rb"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module DelayedResque
2
+ VERSION = "1.0.1"
3
+ end
@@ -0,0 +1,16 @@
1
+ require 'resque'
2
+ require 'resque_scheduler'
3
+ require 'active_support'
4
+ require 'delayed_resque/message_sending'
5
+ require 'delayed_resque/performable_method'
6
+ if defined?(ActionMailer)
7
+ require 'action_mailer/version'
8
+ require 'delayed_resque/performable_mailer' if 3 == ActionMailer::VERSION::MAJOR
9
+ end
10
+
11
+ require 'active_support/core_ext/hash/indifferent_access'
12
+ require 'delayed_resque/railtie' if defined?(Rails::Railtie)
13
+
14
+ # Support delaying class methods.
15
+ Object.send(:include, DelayedResque::MessageSending)
16
+ Module.send(:include, DelayedResque::MessageSending::ClassMethods)
@@ -0,0 +1,144 @@
1
+ require 'spec_helper'
2
+ require 'resque_spec/scheduler'
3
+ describe DelayedResque do
4
+ before do
5
+ ResqueSpec.reset!
6
+ end
7
+
8
+ class DummyObject
9
+ include DelayedResque::MessageSending
10
+ @queue = "default"
11
+
12
+ def self.first_method(param)
13
+ end
14
+ end
15
+
16
+ describe "handle_asyncronously" do
17
+ class Story
18
+ class << self ; def tell!(arg);end ; handle_asynchronously :tell! ;end
19
+ def tell!(arg);end
20
+ handle_asynchronously :tell!
21
+ end
22
+
23
+ it "aliases original method for class methid" do
24
+ expect(Story).to respond_to(:tell_without_delay!)
25
+ expect(Story).to respond_to(:tell_with_delay!)
26
+ end
27
+
28
+ it "aliases original method for instance method" do
29
+ expect(Story.new).to respond_to(:tell_without_delay!)
30
+ expect(Story.new).to respond_to(:tell_with_delay!)
31
+ end
32
+ end
33
+
34
+ context "class methods can be delayed" do
35
+ it "can delay method" do
36
+ DummyObject.delay.first_method(123)
37
+ DelayedResque::PerformableMethod.should have_queued({"obj"=>"CLASS:DummyObject", "method"=>:first_method, "args"=>[123]}).in(:default)
38
+ end
39
+
40
+ it "delayed method is called" do
41
+ DummyObject.stub(:second_method).with(123, 456)
42
+ with_resque do
43
+ DummyObject.delay.second_method(123, 456)
44
+ end
45
+ end
46
+
47
+ it "can't delay missing method" do
48
+ expect {
49
+ DummyObject.delay.non_existent_method
50
+ }.to raise_error(NoMethodError)
51
+ end
52
+
53
+ it "can pass additional params" do
54
+ DummyObject.delay(:params => {"k" => "v"}).first_method(123)
55
+ DelayedResque::PerformableMethod.should have_queued({"obj"=>"CLASS:DummyObject", "method"=>:first_method, "args"=>[123], "k" => "v"}).in(:default)
56
+ end
57
+
58
+ end
59
+
60
+ context "active record methods can be delayed" do
61
+
62
+ ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS 'dummy_models'")
63
+ ActiveRecord::Base.connection.create_table(:dummy_models) do |t|
64
+ t.integer :value
65
+ end
66
+
67
+ class DummyModel < ActiveRecord::Base
68
+ def update_value(new_value1, new_value2)
69
+ self.value = new_value1 + new_value2
70
+ save!
71
+ end
72
+
73
+ def copy_value(record)
74
+ self.value = record.value
75
+ save!
76
+ end
77
+
78
+ def tell!(args)
79
+ p "SLEEEP"
80
+ end
81
+ handle_asynchronously :tell!
82
+ end
83
+
84
+ it "can delay method" do
85
+ record = DummyModel.create(:value => 1)
86
+ with_resque do
87
+ record.delay.update_value(3, 7)
88
+ end
89
+ record.reload.value.should eq(10)
90
+ end
91
+
92
+ it "AR model can be parameter to delay" do
93
+ record1 = DummyModel.create(:value => 1)
94
+ record2 = DummyModel.create(:value => 3)
95
+ with_resque do
96
+ record1.delay.copy_value(record2)
97
+ end
98
+ record1.reload.value.should eq(3)
99
+ end
100
+
101
+ it "job are enqueue in redis" do
102
+ record1 = DummyModel.create
103
+ record1.tell!(1)
104
+ expect(Resque.size("default")).to eq(1)
105
+ end
106
+ end
107
+
108
+ context "methods can be delayed for an interval" do
109
+ it "can delay method" do
110
+ DummyObject.delay(:in => 5.minutes).first_method(123)
111
+ DelayedResque::PerformableMethod.should have_scheduled({"obj"=>"CLASS:DummyObject", "method"=>:first_method, "args"=>[123]}).in(5 * 60)
112
+ end
113
+
114
+ it "can run at specific time" do
115
+ at_time = Time.now.utc + 10.minutes
116
+ DummyObject.delay(:run_at => at_time).first_method(123)
117
+ DelayedResque::PerformableMethod.should have_scheduled({"obj"=>"CLASS:DummyObject", "method"=>:first_method, "args"=>[123]}).at(at_time)
118
+ DelayedResque::PerformableMethod.should have_schedule_size_of(1)
119
+ end
120
+ end
121
+
122
+ context "unique jobs" do
123
+ it "can remove preceeding jobs" do
124
+ DummyObject.delay.first_method(123)
125
+ DelayedResque::PerformableMethod.should have_queued({"obj"=>"CLASS:DummyObject", "method"=>:first_method, "args"=>[123]})
126
+ DelayedResque::PerformableMethod.should have_queue_size_of(1)
127
+ DummyObject.delay.first_method(124)
128
+ DelayedResque::PerformableMethod.should have_queue_size_of(2)
129
+ DummyObject.delay(:unique => true).first_method(123)
130
+ DelayedResque::PerformableMethod.should have_queue_size_of(2)
131
+ end
132
+
133
+ it "can remove preceeding delayed jobs" do
134
+ at_time = Time.now.utc + 10.minutes
135
+ DummyObject.delay(:run_at => at_time).first_method(123)
136
+ DelayedResque::PerformableMethod.should have_scheduled({"obj"=>"CLASS:DummyObject", "method"=>:first_method, "args"=>[123]}).at(at_time)
137
+ DelayedResque::PerformableMethod.should have_schedule_size_of(1)
138
+ DummyObject.delay(:run_at => at_time + 1).first_method(123)
139
+ DelayedResque::PerformableMethod.should have_schedule_size_of(2)
140
+ DummyObject.delay(:run_at => at_time + 2, :unique => true).first_method(123)
141
+ DelayedResque::PerformableMethod.should have_schedule_size_of(1)
142
+ end
143
+ end
144
+ end
@@ -0,0 +1,18 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'rspec'
4
+ require 'resque_scheduler'
5
+ require 'resque_spec'
6
+ require 'resque_spec/scheduler'
7
+ require 'delayed_resque'
8
+
9
+ Dir[File.expand_path(File.join(File.dirname(__FILE__),'support','**','*.rb'))].each {|f| require f}
10
+
11
+ root = File.expand_path(File.join(File.dirname(__FILE__), '..'))
12
+ ActiveRecord::Base.establish_connection(
13
+ :adapter => "sqlite3",
14
+ :database => "#{root}/test.db"
15
+ )
16
+
17
+ RSpec.configure do |config|
18
+ end
metadata ADDED
@@ -0,0 +1,156 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: delayed_resque
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Viren Negi
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-08-26 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 3.2.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: resque
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 1.19.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ! '>='
39
+ - !ruby/object:Gem::Version
40
+ version: 1.19.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: resque-scheduler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: 2.0.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: 2.0.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: resque_spec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: sqlite3
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec-rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: rspec
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ! '>='
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Gem design to wrap Delayed Job nicety syntax under Resque
112
+ email:
113
+ - meetme2meat@gmail.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files:
117
+ - README.md
118
+ files:
119
+ - lib/delayed_resque/message_sending.rb
120
+ - lib/delayed_resque/performable_mailer.rb
121
+ - lib/delayed_resque/performable_method.rb
122
+ - lib/delayed_resque/railtie.rb
123
+ - lib/delayed_resque/version.rb
124
+ - lib/delayed_resque.rb
125
+ - README.md
126
+ - spec/delayed_resque_spec.rb
127
+ - spec/spec_helper.rb
128
+ homepage: http://github.com/meetme2meat/delayed_resque
129
+ licenses:
130
+ - MIT
131
+ metadata: {}
132
+ post_install_message:
133
+ rdoc_options: []
134
+ require_paths:
135
+ - lib
136
+ - MIT-LICENSE
137
+ required_ruby_version: !ruby/object:Gem::Requirement
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ required_rubygems_version: !ruby/object:Gem::Requirement
143
+ requirements:
144
+ - - ! '>='
145
+ - !ruby/object:Gem::Version
146
+ version: '0'
147
+ requirements: []
148
+ rubyforge_project:
149
+ rubygems_version: 2.0.5
150
+ signing_key:
151
+ specification_version: 4
152
+ summary: Delayed Job Syntax mapped over Resque
153
+ test_files:
154
+ - spec/delayed_resque_spec.rb
155
+ - spec/spec_helper.rb
156
+ has_rdoc: