ruote-resque 0.1.0

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