delayed_resque 1.0.1

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