ruote-resque 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NmE2NDVmYTU0NzNmNWZkZTk5MjgxYWZlOGQ4YTk0MDNjOWFmMDZmNg==
5
+ data.tar.gz: !binary |-
6
+ M2M3YTFmMmM2YmQ3NTgwNDg4MDhlMzE2ODNhMDI5YzgyOGI2NDBlMA==
7
+ !binary "U0hBNTEy":
8
+ metadata.gz: !binary |-
9
+ YTUwNjUzMzA5N2MzOGYzZjY5NWYwOGZjZTk2YTY5ZjNkM2U1M2E4NGQwMTE4
10
+ OTQ1YWE0YmJhYTA5ODkyZjEzYmM3Y2ZkY2JjYWRjZmVkMjliNmJkNTMyOGEx
11
+ OWVkZGYyYTc1ZjA4OWY3YTY0MDQ1MDA1ODU0ODYwOTE2YzU5ZDE=
12
+ data.tar.gz: !binary |-
13
+ NGZiMTY5MjU1ZmY1NWQ1ZjVlNDYyYjJmNzAyZDFjMDgyY2Y2NTE2YmQ0MWYy
14
+ NzlhMTU5MDY4NTQ3NzY0ZGYwODViZjFjNTE3YjM3N2QzNmZhODNiYTM4OGY4
15
+ NWE2ZGRjZDVhMmVmYmI5ZTVjZDQ5MzgxYTVlNmU0NDYwZjIxNTc=
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
@@ -0,0 +1,4 @@
1
+ LineLength:
2
+ Enabled: false
3
+ HashSyntax:
4
+ Enabled: false
@@ -0,0 +1,14 @@
1
+ language: ruby
2
+ bundler_args: --without development
3
+ rvm:
4
+ - "1.8.7"
5
+ - "1.9.2"
6
+ - "1.9.3"
7
+ - "2.0.0"
8
+ - jruby-18mode
9
+ - jruby-19mode
10
+ - rbx-18mode
11
+ - rbx-19mode
12
+ script: bundle exec rspec spec
13
+ services:
14
+ - redis-server
@@ -0,0 +1,4 @@
1
+ --markup=markdown
2
+ --files LICENSE
3
+ --title ruote-resque
4
+ --readme README.md
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ruote-resque.gemspec
4
+ gemspec
5
+
6
+ gem "ruote", :git => 'git://github.com/jmettraux/ruote.git'
7
+ gem "json"
8
+
9
+ group :test do
10
+ gem 'rake'
11
+ gem 'rspec'
12
+ gem 'coveralls', :require => false
13
+ end
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Adrien Kohlbecker
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,156 @@
1
+ [![Build Status](https://travis-ci.org/adrienkohlbecker/ruote-resque.png)](https://travis-ci.org/adrienkohlbecker/ruote-resque) [![Coverage Status](https://coveralls.io/repos/adrienkohlbecker/ruote-resque/badge.png?branch=master)](https://coveralls.io/r/adrienkohlbecker/ruote-resque) [![Code Climate](https://codeclimate.com/github/adrienkohlbecker/ruote-resque.png)](https://codeclimate.com/github/adrienkohlbecker/ruote-resque) [![Dependency Status](https://gemnasium.com/adrienkohlbecker/ruote-resque.png)](https://gemnasium.com/adrienkohlbecker/ruote-resque)
2
+
3
+ # Ruote::Resque
4
+
5
+ - **Homepage**: [github.com/adrienkohlbecker/ruote-resque](https://github.com/adrienkohlbecker/ruote-resque)
6
+ - **Rdoc**: [rdoc.info/gems/ruote-resque](http://rdoc.info/gems/ruote-resque)
7
+ - **Author**: Adrien Kohlbecker
8
+ - **License**: MIT License
9
+ - **Latest Version**: 0.1.0
10
+ - **Release Date**: June 23, 2013
11
+
12
+ ## Synopsis
13
+
14
+ Ruote::Resque allows a Ruote engine to delegates the work of it's participants to Resque workers.
15
+
16
+ Common use cases include:
17
+
18
+ - You run a lot of jobs and need the reliability of Resque to process your jobs
19
+ - You have a Resque system you need to integrate with
20
+ - You want a separation of concerns between process orchestration and actual execution
21
+
22
+ ## How it works
23
+
24
+ - The library uses a specific Resque queue for message passing.
25
+ - Participants are empty shells that just queue jobs to Resque, and the job itself replies (via an after_perform hook) with the mutated workitem.
26
+ - Under the hood, a thread polls the reply queue every five seconds for new replies.
27
+ - When a reply is received, the process continues with the new workitem.
28
+
29
+ Note that:
30
+
31
+ - Ruote is not needed as a dependency on the Resque side.
32
+ - Error handling is supported on both sides (exceptions show up both on the Resque failure backend and in ruote-kit)
33
+
34
+ ## Usage
35
+
36
+ ### Set Up
37
+
38
+ #### Inside your Ruote instance
39
+
40
+ Add to your Ruote instance gemfile :
41
+
42
+ ```ruby
43
+ # The version released on RubyGems is old and not supported.
44
+ # Note that ruote is not a hard dependency of ruote-resque,
45
+ # to allow for lightweight use as a client (see below)
46
+ gem 'ruote', :git => 'git://github.com/jmettraux/ruote.git'
47
+ gem 'ruote-resque'
48
+ ```
49
+
50
+ Then when booting your engine do this :
51
+
52
+ ```ruby
53
+ require 'ruote/resque'
54
+
55
+ # Run the poller thread.
56
+ Ruote::Resque::Receiver.new(dashboard)
57
+ ```
58
+
59
+ #### Inside your Resque worker instance
60
+
61
+ Add to your Resque worker instance gemfile :
62
+
63
+ ```ruby
64
+ gem 'ruote-resque', :require => false
65
+ ```
66
+
67
+ Then when booting your worker do this :
68
+
69
+ ```ruby
70
+ # You should not require the full library inside your worker, the client will suffice
71
+ require 'ruote/resque/client'
72
+ ```
73
+
74
+ ### Participants
75
+
76
+ ```ruby
77
+ # Inside your worker, add Ruote::Resque::Job to your jobs
78
+ class MyAwesomeJob
79
+ extend Ruote::Resque::Job
80
+
81
+ @queue = :my_queue
82
+
83
+ def self.perform(workitem)
84
+ workitem['fields']['be_awesome'] = true
85
+ end
86
+ end
87
+
88
+ # Inside your Ruote instance
89
+ Ruote::Resque.register(dashboard) do
90
+ be_awesome 'MyAwesomeJob', :my_queue
91
+ end
92
+ ```
93
+
94
+ There are two other ways to register participants:
95
+
96
+ ```ruby
97
+ Ruote::Resque.register(dashboard) do
98
+ participant 'be_awesome', 'MyAwesomeJob', :my_queue
99
+ end
100
+
101
+ # OR (not recommended)
102
+
103
+ dashboard.register_participant 'be_awesome', Ruote::Resque::Participant, :class => 'MyAwesomeJob', :queue => :my_queue
104
+ ```
105
+
106
+ Note that you can pass options to the participant:
107
+
108
+ ```ruby
109
+ Ruote::Resque.register(dashboard) do
110
+ be_awesome 'MyAwesomeJob', :my_queue, :forget => true
111
+ end
112
+ ```
113
+
114
+ ### Configuration
115
+
116
+ ```ruby
117
+ # Configure the library (optional, default values shown)
118
+ # You should duplicate this configuration on both
119
+ # your Ruote instance and your Resque instance
120
+ Ruote::Resque.configure do |config|
121
+ config.reply_queue = :ruote_replies
122
+ config.logger = Logger.new(STDOUT).tap { |log| log.level = Logger::INFO }
123
+ config.interval = 5
124
+ end
125
+
126
+ # Override the default error handler (optional, but recommended. Default is to log at ERROR level)
127
+ # Do this in your Ruote instance
128
+ class Ruote::Resque::Receiver
129
+ def handle_error(e)
130
+ MyErrorHandler.handle(e)
131
+ end
132
+ end
133
+ ```
134
+
135
+ ## Requirements
136
+
137
+ A functional installation of [Ruote](http://ruote.rubyforge.org) and [Resque](http://github.com/resque/resque) is needed.
138
+ Note that at the time of writing the release of Ruote on rubygems is very old and not supported. Use it from git.
139
+
140
+ ruote-resque has been tested on Ruby 1.8+.
141
+
142
+ ## Contributing
143
+
144
+ 1. Fork it
145
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
146
+ 3. Do some changes
147
+ 4. Run the tests (`bundle exec rspec`)
148
+ 5. Commit your changes (`git commit -am 'Add some feature'`)
149
+ 6. Push to the branch (`git push origin my-new-feature`)
150
+ 7. Create new Pull Request
151
+
152
+ ## Changelog
153
+
154
+ - **June 23, 2013**: 0.1.0 release
155
+ - Initial release
156
+
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,40 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'ruote'
4
+ require 'ruote/resque/client'
5
+ require 'ruote/resque/participant'
6
+ require 'ruote/resque/participant_registrar'
7
+ require 'ruote/resque/receiver'
8
+
9
+ module Ruote
10
+ # Ruote::Resque allows a Ruote engine to delegates the work of it's participants
11
+ # to Resque workers.
12
+ #
13
+ # Common use cases include:
14
+ #
15
+ # - You run a lot of jobs and need the reliability of Resque to process your jobs
16
+ # - You have a Resque system you need to integrate with
17
+ # - You want a separation of concerns between process orchestration and actual execution
18
+ #
19
+ # See the {file:README} for usage instructions
20
+ module Resque
21
+
22
+ # Registers resque participants using a DSL.
23
+ # @example Using the dsl
24
+ # Ruote::Resque.register dashboard do
25
+ # be_awesome MyAwesomeJob, :my_queue
26
+ # be_really_awesome 'MyReallyAwesomeJob', :my_queue, :forget => true
27
+ # end
28
+ # @example Using the participant method
29
+ # Ruote::Resque.register dashboard do
30
+ # participant /be_.*/, BeSomething, :my_queue
31
+ # end
32
+ # @param [Ruote::Dashboard] dashboard the ruote dashboard
33
+ # @return [void]
34
+ def self.register(dashboard, &block)
35
+ registrar = Ruote::Resque::ParticipantRegistrar.new(dashboard)
36
+ registrar.instance_eval(&block)
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,62 @@
1
+ # encoding: UTF-8
2
+
3
+ require 'logger'
4
+ require 'ruote/resque/job'
5
+ require 'ruote/resque/reply_job'
6
+ require 'ruote/resque/version'
7
+
8
+ module Ruote
9
+ module Resque
10
+
11
+ # A basic configuration object
12
+ # @example Setting up ruote-resque (default values shown)
13
+ # Ruote::Resque.configure do |config|
14
+ # config.reply_queue = :ruote_replies
15
+ # config.logger = Logger.new(STDOUT).tap { |log| log.level = Logger::INFO }
16
+ # config.interval = 5
17
+ # end
18
+ class Configuration
19
+ # The queue used for message passing between Resque jobs and Ruote (defaults to `:ruote_replies`)
20
+ attr_accessor :reply_queue
21
+ # The logger used (defaults to STDOUT with log level INFO)
22
+ attr_accessor :logger
23
+ # The interval used by {Receiver} when polling Resque
24
+ attr_accessor :interval
25
+ end
26
+
27
+ class << self
28
+
29
+ # Returns the current {Configuration}
30
+ attr_accessor :configuration
31
+
32
+ # Enqueues a ReplyJob with the given arguments.
33
+ # @return true if the job was queued, nil if the job was rejected by a before_enqueue hook.
34
+ # @example
35
+ # Ruote::Resque.reply(workitem)
36
+ def reply(*args)
37
+ ::Resque.enqueue(Ruote::Resque::ReplyJob, *args)
38
+ end
39
+
40
+ # @return [Logger] the logger to be used inside ruote-resque
41
+ def logger
42
+ configuration.logger
43
+ end
44
+
45
+ # This method allows you to customize the ruote-resque configuration.
46
+ # @see Configuration
47
+ # @yield [Configuration]
48
+ # @return [void]
49
+ def configure
50
+ self.configuration ||= Ruote::Resque::Configuration.new
51
+ yield(configuration) if block_given?
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ # setup default configuration
58
+ Ruote::Resque.configure do |config|
59
+ config.reply_queue = :ruote_replies
60
+ config.logger = Logger.new(STDOUT).tap { |log| log.level = Logger::INFO }
61
+ config.interval = 5
62
+ end
@@ -0,0 +1,47 @@
1
+ # encoding: UTF-8
2
+
3
+ module Ruote
4
+ module Resque
5
+
6
+ # Include this module inside your Resque jobs to enable
7
+ # them to respond to Ruote.
8
+ #
9
+ # - Note that the arity should be 1 for the `self.perform` method.
10
+ # - The workitem will be sent as a Hash. (via `Ruote::Workitem#to_h`)
11
+ #
12
+ # @example
13
+ # class MyAwesomeJob
14
+ # extend Ruote::Resque::Job
15
+ #
16
+ # def self.perform(workitem)
17
+ # workitem['fields']['awesome'] = true
18
+ # end
19
+ # end
20
+ module Job
21
+
22
+ # after_perform hook to send a reply to the Ruote process.
23
+ # @param [Hash] workitem the workitem sent to the current Job
24
+ # @return [void]
25
+ def after_perform_reply_to_ruote(workitem)
26
+ Ruote::Resque.reply(workitem)
27
+ end
28
+
29
+ # on_failure hook to send a reply to the Ruote process.
30
+ # Will collect the exception details and send them along.
31
+ # @param [Exception] exception the raised exception
32
+ # @param [Hash] workitem the workitem sent to the current Job.
33
+ # TODO: this may be mutated from the original workitem, handle it.
34
+ # @return [void]
35
+ def on_failure_reply_to_ruote(exception, workitem)
36
+
37
+ klass = exception.class.to_s
38
+ message = exception.message
39
+ backtrace = exception.backtrace
40
+
41
+ Ruote::Resque.reply(klass, message, backtrace, workitem)
42
+ end
43
+
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,86 @@
1
+ # encoding: UTF-8
2
+
3
+ # A module to be included in your Resque jobs
4
+ # to be able to register them as participants.
5
+ #
6
+ # Note that the participant should have a queue, either via the `@queue` variable
7
+ # when the class is accessible to Ruote, or via the `:queue` option on registration
8
+ #
9
+ # @example Register a participant
10
+ # # This is defined on a remote Resque worker
11
+ # # You do not need to extend Participant in this case
12
+ # class MyAwesomeJob
13
+ # extend Ruote::Resque::Job
14
+ # @queue = :my_queue
15
+ #
16
+ # def self.perform(workitem)
17
+ # workitem['fields']['awesome'] = true
18
+ # end
19
+ # end
20
+ #
21
+ # # Use it like this in your Ruote process
22
+ # engine.register_participant 'be_awesome', Ruote::Resque::Participant, :class => 'MyAwesomeJob', :queue => :my_queue
23
+ # # Or register it va the DSL
24
+ # Ruote::Resque.register(dashboard) do
25
+ # be_awesome 'MyAwesomeJob', :my_queue
26
+ # end
27
+ # A resque participant implementation.
28
+ class Ruote::Resque::Participant
29
+ include Ruote::LocalParticipant
30
+
31
+ # Called with the options on `engine.register_participant`
32
+ # @param [Hash] opts
33
+ # @option opts [#to_s] :class (self.class) the job class to enqueue when called
34
+ # @option opts [#to_s] :queue (Resque.queue_from_class(class)) the queue to enqueue the job in
35
+ # @option opts [Boolean] :forget (false) wait for the worker's reply if false
36
+ def initialize(opts = {})
37
+
38
+ @job_klass = opts.delete('class')
39
+ @job_queue = opts.delete('queue')
40
+ @should_forget = opts.delete('forget') || false
41
+
42
+ # Called here to raise eventual exceptions on initialization
43
+ ::Resque.validate(@job_klass, @job_queue)
44
+
45
+ end
46
+
47
+ # Called when the participant is handed a workitem.
48
+ # Enqueues the job to Resque
49
+ # @return [void]
50
+ def on_workitem
51
+
52
+ payload = encode_workitem(workitem)
53
+ ::Resque::Job.create(@job_queue, @job_klass, payload)
54
+
55
+ reply if @should_forget
56
+
57
+ end
58
+
59
+ # Called when Ruote has to cancel an active workitem for this participant.
60
+ # Destroys the job from the Resque queue.
61
+ #
62
+ # Note that if the job is being processed by the worker or if the job has been processed but the reply has not,
63
+ # this method will do nothing.
64
+ # @return [Boolean] wether the job was deleted or not.
65
+ def on_cancel
66
+
67
+ payload = encode_workitem(applied_workitem)
68
+ ::Resque::Job.destroy(@job_queue, @job_klass, payload)
69
+
70
+ end
71
+
72
+ # Returns a representation of a workitem that is suitable for use in Resque.
73
+ # @param [Ruote::Workitem] workitem
74
+ # @return [Hash] the workitem as a hash
75
+ def encode_workitem(workitem)
76
+
77
+ workitem.to_h
78
+
79
+ end
80
+
81
+ # Returns true because enqueing a job in Resque is sufficiently fast to happen in the main thread.
82
+ # @return [true]
83
+ def do_not_thread
84
+ true
85
+ end
86
+ end