stomp_job 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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.rspec +3 -0
- data/.travis.yml +5 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +96 -0
- data/Rakefile +5 -0
- data/bin/console +14 -0
- data/bin/stomper +4 -0
- data/examples/scream_worker.rb +24 -0
- data/lib/stomp_job/cli.rb +89 -0
- data/lib/stomp_job/configuration.rb +71 -0
- data/lib/stomp_job/connection.rb +58 -0
- data/lib/stomp_job/connection_exception.rb +3 -0
- data/lib/stomp_job/extensions/active_job_adapter.rb +47 -0
- data/lib/stomp_job/railtie.rb +11 -0
- data/lib/stomp_job/version.rb +3 -0
- data/lib/stomp_job/worker.rb +60 -0
- data/lib/stomp_job/workers.rb +23 -0
- data/lib/stomp_job.rb +11 -0
- data/lib/tasks/stomp_job.rake +6 -0
- data/stomp_job.gemspec +45 -0
- metadata +182 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 4ced1be56df00236a971e4e4ac9cdc0d04c3f034
|
4
|
+
data.tar.gz: 8bece1325d3180278e3a471d5714e13204e45eff
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 15d232c0feaed9b4f018aac3721175b1510021be11bde0c2efae23025612ca0fa0f33e5e9217fac90da9e16b8304b5b1f611d817aa6c2d8388d963aed45a0b6a
|
7
|
+
data.tar.gz: 62701ae6dcb3c6de832f35b7de84a872fefdd290101a4a373ae3351a0cbba92c4f5ad876a4e0f645d2328a71775251b4e13c37d1f71a7db7630b65b452c3e3e2
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at abraman@mgage.com. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Adam Braman
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,96 @@
|
|
1
|
+
# StompJob
|
2
|
+
|
3
|
+
StompJob gives a Resque-like interface for message brokers using the STOMP protocol. It uses https://github.com/stompgem/stomp under the hood.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'stomp_job'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install stomp_job
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
To create a worker class, simply add `include StompJob::Worker` to your class, configure it with the `stomp_options` class method, and define a `perform` method.
|
24
|
+
|
25
|
+
To enqueue a message, call `YourWorker.enqueue(*args)`, where YourWorker is your worker class, and *args has the same arity as your perform method.
|
26
|
+
|
27
|
+
To run your workers, run `./bin/stomper`. Make sure that stomper has your workers loaded by passing --require flags. e.g.
|
28
|
+
```
|
29
|
+
./bin/stomper --require=./examples/scream_worker
|
30
|
+
```
|
31
|
+
|
32
|
+
See the examples/ directory for example usage.
|
33
|
+
|
34
|
+
### Rails
|
35
|
+
|
36
|
+
If you're working with rails, start the workers by running `./bin/stomper --rails` instead.
|
37
|
+
|
38
|
+
Additionally, there is an ActiveJob adapter included with this gem. To use it, simply add
|
39
|
+
```
|
40
|
+
require 'stomp_job/extensions/active_job_adapter'
|
41
|
+
config.active_job.queue_adapter = :stomp_job
|
42
|
+
```
|
43
|
+
to your config/application.rb file.
|
44
|
+
|
45
|
+
### STOMP Broker Connection
|
46
|
+
|
47
|
+
You can configure the connection to your STOMP broker as follows:
|
48
|
+
```
|
49
|
+
StompJob::Configuration.setup do |config|
|
50
|
+
config[:hosts] = [
|
51
|
+
{login: "your_username", passcode: "your_password", host: "example.com", port: 61613}
|
52
|
+
]
|
53
|
+
config[:reliable] = true
|
54
|
+
config[:initial_reconnect_delay] = 0.01
|
55
|
+
# config[:some_option] = some_value
|
56
|
+
end
|
57
|
+
```
|
58
|
+
|
59
|
+
Alternatively you can call
|
60
|
+
```
|
61
|
+
StompJob::Configuration.load_config!(file="config/stomp_job.yml")
|
62
|
+
```
|
63
|
+
to load connection information out of a config file. This is the recommended method for loading config from a rails app.
|
64
|
+
|
65
|
+
## Testing
|
66
|
+
|
67
|
+
Any contributions should include tests. There is a dockerized ArtemisMQ instance in spec/activemq-artemis/ that you can test against. Credit for the Docker stuff goes to https://github.com/vromero/activemq-artemis-docker.
|
68
|
+
|
69
|
+
Testing running the ArtemisMQ instance are NOT run by default, and can be run with
|
70
|
+
```
|
71
|
+
rspec --tag integration
|
72
|
+
```
|
73
|
+
|
74
|
+
Make sure you have the ArtemisMQ docker instance running locally when you run this or else tests will hang. Assuming a sane docker setup, the docker instance can be launched with
|
75
|
+
```
|
76
|
+
docker run -d --name artemis -p 61613:61613 vromero/activemq-artemis
|
77
|
+
```
|
78
|
+
|
79
|
+
## TODO
|
80
|
+
|
81
|
+
The below are some current issues/features that could still be implemented for this gem:
|
82
|
+
* Set up some form of logging. Logging should be configurable, e.g. in rails, you should be able to say something like `StompJob.logger = Rails.logger`
|
83
|
+
* Write an enqueue_at method for ActiveJob::QueueAdapters::StompJobAdapter.
|
84
|
+
* Exceptions die too quietly. Can they raise an exception in the main thread? Can we implement some sort of retry functionality?
|
85
|
+
* Implement a more graceful shutdown. We could at least unsubscribe the STOMP connections when shutting down.
|
86
|
+
* Clean up ArtemisMQ spec? Can we reasonably (read: without hacks) make that a part of the rspec suite proper?
|
87
|
+
|
88
|
+
## Contributing
|
89
|
+
|
90
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/mGageTechOps/stomp_job. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
91
|
+
|
92
|
+
|
93
|
+
## License
|
94
|
+
|
95
|
+
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
96
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "stomp_job"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start
|
data/bin/stomper
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'stomp_job'
|
2
|
+
|
3
|
+
# Setup example to use dockerized Artemis instance. See spec/support/activemq-artemis-docker
|
4
|
+
StompJob::Configuration.setup do |config|
|
5
|
+
config[:hosts] = [
|
6
|
+
{login: "artemis", passcode: "simetraehcapa", host: "localhost", port: 61613}
|
7
|
+
]
|
8
|
+
end
|
9
|
+
|
10
|
+
# ScreamWorker capitalizes any message it receives and writes it to STDOUT
|
11
|
+
class ScreamWorker
|
12
|
+
include StompJob::Worker
|
13
|
+
stomp_options queue_name: 'screams' # enqueues to "jms.queue.screams"
|
14
|
+
|
15
|
+
def perform message
|
16
|
+
puts message.upcase
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
ScreamWorker.enqueue 'hello world!'
|
21
|
+
# running `./bin/stomper --require=./examples/scream_worker`
|
22
|
+
# should output "HELLO WORLD!"
|
23
|
+
#
|
24
|
+
# If a worker like this is in a rails app, run `./bin/stomper --rails instead.`
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'stomp_job'
|
3
|
+
|
4
|
+
module StompJob
|
5
|
+
class Cli < Thor
|
6
|
+
BANNER = <<-EOF
|
7
|
+
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
|
8
|
+
MMMMMMMMMMMMMMMMM STOMPJOB MMMMMMMMMMMMMMMMMMM
|
9
|
+
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM
|
10
|
+
MMMMMMMMMMMMMMMMMMNmddmNMMMMMMMMMMMMMMMMMMMMMMMMMM
|
11
|
+
MMMMMMMMMMMMmy+:. `/NMMMMMMMMMMMMMMMMMMMMMMM
|
12
|
+
MMMMMMMMMmo. `+. `+mMMMMMMMMMMMMMMMMMMMMMMM
|
13
|
+
MMMMMMMMd -smmydNhMMMMMMMMMMMMMMMMMMMMMMMM
|
14
|
+
MMMMMMMMM. .odNydNhMMMMMMMMMMMMMMMMMMMMMMMM
|
15
|
+
MMMMMMMMMo `o: `sMMMMMMMMMMMMMMMMMMMMMMMM
|
16
|
+
MMMMMMMMMm ::` `+mMMMMMMMMMMMMMMMMMMMMMMMM
|
17
|
+
MMMMMMMMMM- ohNdhMhmMMMMMMMMMMMMMMMMMMMMMMMM
|
18
|
+
MMMMMMMMMMo :yNdsdNMMMMMMMMMMMMMMMMMMMMMMMMM
|
19
|
+
MMMMMMMMMMm +o. -hmhso++oymMMMMMMMMMMMMMMMM
|
20
|
+
MMMMMMMMMMM- `//` .oNMMMMMMMMMMMMM
|
21
|
+
MMMMMMMMMMMs .mMMMMMMMMMMMM
|
22
|
+
MMMMMMMMMMMN .MMMMMMMMMMMM
|
23
|
+
MMMMMMMMMMMMNdyo+:-.` `-:ohNNMMMMMMMMMMM
|
24
|
+
MMMMMMMMMMMM`.:+oydMNNNNmmmmmNNNmhyo:`+MMMMMMMMMMM
|
25
|
+
MMMMh:``dMMMso+/:-mMN: `` `.:+smMMMy``/dMMMM
|
26
|
+
MMds//- ./oshdmNNMMMMMMNNNmNNNMNNmdhso/. -//sdMM
|
27
|
+
MMMMMm- `/o- ```......`` :o:` -NMMMMM
|
28
|
+
MMMMd/sdMMM/ `oNM/ :md: :MNo` +MMMds/dMMMM
|
29
|
+
MMMMMMMMMMM``sNMMMy oMMMMs` oMMMNs``MMMMMMMMMMM
|
30
|
+
MMMMMMMMMMdsMMMMMMN .dMMMMMMm- yMMMMMNsmMMMMMMMMMM
|
31
|
+
MMMMMMMMMMMMMMMMMMMsNMMMMMMMMMomMMMMMMMMMMMMMMMMMM
|
32
|
+
EOF
|
33
|
+
|
34
|
+
default_task :work
|
35
|
+
|
36
|
+
|
37
|
+
desc "work", "Runs all registered workers"
|
38
|
+
method_option :require,
|
39
|
+
aliases: ['-r'],
|
40
|
+
banner: 'REQUIRED_WORKERS',
|
41
|
+
desc: 'REQUIRED_WORKERS is a comma separated list of files to require'
|
42
|
+
method_option :rails,
|
43
|
+
desc: 'Make StompJob try to load the rails environment.'
|
44
|
+
|
45
|
+
def work
|
46
|
+
say BANNER
|
47
|
+
say
|
48
|
+
require_files if options['require']
|
49
|
+
load_rails if options['rails']
|
50
|
+
|
51
|
+
say "Starting Stomper with workers:"
|
52
|
+
say pretty_print_workers, :yellow
|
53
|
+
say
|
54
|
+
|
55
|
+
Workers.listen!
|
56
|
+
end
|
57
|
+
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def require_files
|
62
|
+
options['require'].split(',').each do |require_file|
|
63
|
+
require require_file
|
64
|
+
end
|
65
|
+
rescue Exception => e
|
66
|
+
die(e)
|
67
|
+
end
|
68
|
+
|
69
|
+
def load_rails
|
70
|
+
require './config/application'
|
71
|
+
::Rails.application.eager_load!
|
72
|
+
rescue Exception => e
|
73
|
+
die(e, "Are you running this from a rails app?\nStompJob expects to be able to `require ./config/application`")
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
def die(exception, debug_help=nil)
|
78
|
+
say "StompJob encountered the following error and is quitting:\n\t#{exception.message}", :red
|
79
|
+
say
|
80
|
+
say debug_help, :red if debug_help
|
81
|
+
|
82
|
+
exit 1
|
83
|
+
end
|
84
|
+
|
85
|
+
def pretty_print_workers
|
86
|
+
Workers.registered_workers.map{|worker| "\t#{worker.name}\n"}.join('')
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
module StompJob
|
2
|
+
class Configuration
|
3
|
+
DEFAULT_CLIENT_CONFIG = {
|
4
|
+
hosts: [
|
5
|
+
{login: "", passcode: "", host: "localhost", port: 61613}
|
6
|
+
],
|
7
|
+
reliable: true,
|
8
|
+
initial_reconnect_delay: 0.01,
|
9
|
+
max_reconnect_delay: 30.0,
|
10
|
+
use_exponential_back_off: true,
|
11
|
+
back_off_multiplier: 2,
|
12
|
+
max_reconnect_attempts: 0,
|
13
|
+
randomize: false,
|
14
|
+
connect_timeout: 0,
|
15
|
+
connect_headers: {
|
16
|
+
"accept-version" => "1.1",
|
17
|
+
"host" => "localhost",
|
18
|
+
"heart-beat" => "60000,60000"
|
19
|
+
},
|
20
|
+
parse_timeout: 5,
|
21
|
+
logger: nil,
|
22
|
+
dmh: false,
|
23
|
+
closed_check: true,
|
24
|
+
hbser: true,
|
25
|
+
stompconn: false,
|
26
|
+
usecrlf: false,
|
27
|
+
max_hbread_fails: 0,
|
28
|
+
max_hbrlck_fails: 0,
|
29
|
+
fast_hbs_adjust: 20.0,
|
30
|
+
tcp_nodelay: true,
|
31
|
+
start_timeout: 0,
|
32
|
+
sslctx_newparm: nil,
|
33
|
+
ssl_post_conn_check: true
|
34
|
+
}
|
35
|
+
|
36
|
+
|
37
|
+
def self.configuration
|
38
|
+
@configuration ||= DEFAULT_CLIENT_CONFIG
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.reset_configuration!
|
42
|
+
@configuration = DEFAULT_CLIENT_CONFIG
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.setup
|
46
|
+
yield configuration
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.load_config!(file="config/stomp_job.yml")
|
50
|
+
raise 'YAML undefined! You must require YAML to use the StompJob::Configuration.load_config! method!' unless defined?(YAML)
|
51
|
+
reset_configuration!
|
52
|
+
configuration.merge! deep_symbolize_keys(YAML.load_file(file))
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def self.deep_symbolize_keys(object)
|
58
|
+
case
|
59
|
+
when object.is_a?(Array)
|
60
|
+
object.map { |element| deep_symbolize_keys(element) }
|
61
|
+
when object.is_a?(Hash)
|
62
|
+
object.inject({}) do |memo, (key,value)|
|
63
|
+
memo[key.to_sym] = deep_symbolize_keys(value)
|
64
|
+
memo
|
65
|
+
end
|
66
|
+
else
|
67
|
+
object
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require "stomp"
|
2
|
+
|
3
|
+
module StompJob
|
4
|
+
class Connection
|
5
|
+
|
6
|
+
def self.from(stomp_configuration={})
|
7
|
+
new(new_client( Configuration.configuration.merge(stomp_configuration) ))
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.default_connection
|
11
|
+
@default_connection ||= refresh_default_connection
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.refresh_default_connection
|
15
|
+
@default_connection = new
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
# Using a marshaller allows us to send complex messages over stomp.
|
21
|
+
# Using Ruby's built-in `Marshal` by default.
|
22
|
+
def publish(queue_name, *args)
|
23
|
+
@connection.publish(safe_queue_name(queue_name), marshaller.dump(args))
|
24
|
+
end
|
25
|
+
|
26
|
+
def subscribe(worker_klass)
|
27
|
+
@connection.subscribe( safe_queue_name(worker_klass.queue_name) ) do |msg|
|
28
|
+
worker_klass.new.perform( *marshaller.load(msg.body) )
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def self.new_client(config)
|
36
|
+
Stomp::Client.new(config)
|
37
|
+
rescue RuntimeError => exception # All STOMP gem errors subclass RuntimeError
|
38
|
+
# We wrap the STOMP exception with our own to provide an extra layer of
|
39
|
+
# indirection.
|
40
|
+
raise ConnectionException.new(exception)
|
41
|
+
end
|
42
|
+
|
43
|
+
def initialize(connection=nil)
|
44
|
+
@connection = connection || self.class.new_client( Configuration.configuration )
|
45
|
+
end
|
46
|
+
|
47
|
+
# The STOMP gem can break the interpreter if the queue name is invalid.
|
48
|
+
# This method ensures that queue_names are properly formatted.
|
49
|
+
def safe_queue_name(queue_name)
|
50
|
+
queue_name.match(/\Ajms\.queue/) ? queue_name : "jms.queue.#{queue_name}"
|
51
|
+
end
|
52
|
+
|
53
|
+
def marshaller
|
54
|
+
Marshal
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'stomp_job'
|
2
|
+
|
3
|
+
module ActiveJob
|
4
|
+
module QueueAdapters
|
5
|
+
# == StompJob adapter for Active Job
|
6
|
+
#
|
7
|
+
# StompJob is an ActiveJob adapter for working with any message broker that
|
8
|
+
# accepts the STOMP protocol.
|
9
|
+
#
|
10
|
+
# To use StompJob set the queue_adapter config to +:stomp_job+.
|
11
|
+
#
|
12
|
+
# Rails.application.config.active_job.queue_adapter = :stomp_job
|
13
|
+
class StompJobAdapter
|
14
|
+
def enqueue(job)
|
15
|
+
StompJobAdapter.stomp_worker_from_aj(job.class).enqueue(*job.arguments)
|
16
|
+
end
|
17
|
+
|
18
|
+
def enqueue_at(job, timestamp)
|
19
|
+
raise NotImplementedError, "Scheduling jobs is not currently supported by the StopmJob queueing backend."
|
20
|
+
end
|
21
|
+
|
22
|
+
|
23
|
+
def self.stomp_worker_from_aj(aj_klass)
|
24
|
+
stomp_job_klasses[aj_klass] ||= Class.new do
|
25
|
+
include StompJob::Worker
|
26
|
+
stomp_options queue_name: aj_klass.queue_name
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.stomp_job_klasses
|
31
|
+
@stomp_job_klasses ||= {}
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
class Base
|
37
|
+
# It's necessary to populate StompJobAdapter.stomp_job_klasses when the jobs
|
38
|
+
# are created, not when they're enqueued, or else listeners won't know that
|
39
|
+
# the anonymous StompJob classes exist.
|
40
|
+
#
|
41
|
+
# Overriding queue_as allows us to do this.
|
42
|
+
def self.queue_as(*args)
|
43
|
+
super
|
44
|
+
QueueAdapters::StompJobAdapter.stomp_worker_from_aj(self)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# Any class including this module will be able to process jobs. A
|
2
|
+
# developer can include this module in their class and configure
|
3
|
+
# it with `stomp_options`. e.g.
|
4
|
+
#
|
5
|
+
# class MyWorker
|
6
|
+
# include StompJob::Worker
|
7
|
+
# stomp_options queue_name: 'my_queue', other_opts: '...'
|
8
|
+
|
9
|
+
# def perform(args)
|
10
|
+
# # do some work
|
11
|
+
# end
|
12
|
+
# end
|
13
|
+
|
14
|
+
module StompJob
|
15
|
+
module Worker
|
16
|
+
|
17
|
+
def self.included(base_worker)
|
18
|
+
base_worker.extend ClassMethods
|
19
|
+
Workers.register( base_worker )
|
20
|
+
end
|
21
|
+
|
22
|
+
module ClassMethods
|
23
|
+
attr_accessor :queue_name, :stomp_connection
|
24
|
+
|
25
|
+
# stomp_options controls parameters for the worker and the
|
26
|
+
# queue. All supported options are:
|
27
|
+
#
|
28
|
+
# queue_name (required) - The name of the queue to use on
|
29
|
+
# the queue broker. Will be created
|
30
|
+
# if it doesn't already exist.
|
31
|
+
# stomp_connection - The STOMP connection to use for
|
32
|
+
# the worker. An instance of
|
33
|
+
# StompJob::Connection. By default,
|
34
|
+
# all workers will use the same
|
35
|
+
# STOMP connection.
|
36
|
+
def stomp_options(opts={})
|
37
|
+
opts = opts.with_indifferent_access if opts.respond_to?(:with_indifferent_access)
|
38
|
+
@queue_name = opts[:queue_name]
|
39
|
+
@stomp_connection = opts[:stomp_connection]
|
40
|
+
end
|
41
|
+
|
42
|
+
# Enqueue a payload for a worker
|
43
|
+
def enqueue(*args)
|
44
|
+
stomp_connection.publish(queue_name, *args)
|
45
|
+
end
|
46
|
+
|
47
|
+
# Make the current connection listen for messages that
|
48
|
+
# should be handled by this worker.
|
49
|
+
# Typically invoked by the Consumer.
|
50
|
+
def listen!
|
51
|
+
stomp_connection.subscribe(self)
|
52
|
+
end
|
53
|
+
|
54
|
+
def stomp_connection
|
55
|
+
@stomp_connection ||= Connection.default_connection
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module StompJob
|
2
|
+
class Workers
|
3
|
+
|
4
|
+
class << self
|
5
|
+
def registered_workers
|
6
|
+
@registered_workers ||= []
|
7
|
+
end
|
8
|
+
|
9
|
+
def register( worker_klass )
|
10
|
+
registered_workers << worker_klass unless registered_workers.include?(worker_klass)
|
11
|
+
registered_workers
|
12
|
+
end
|
13
|
+
|
14
|
+
# Tells main thread to sleep, and tells all workers to listen for
|
15
|
+
# and process any messages they receive.
|
16
|
+
def listen!
|
17
|
+
registered_workers.each(&:listen!)
|
18
|
+
sleep
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
data/lib/stomp_job.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'stomp_job/configuration'
|
2
|
+
require 'stomp_job/connection'
|
3
|
+
require 'stomp_job/connection_exception'
|
4
|
+
require 'stomp_job/version'
|
5
|
+
require 'stomp_job/worker'
|
6
|
+
require 'stomp_job/workers'
|
7
|
+
|
8
|
+
require 'stomp_job/railtie' if defined?(::Rails)
|
9
|
+
|
10
|
+
module StompJob
|
11
|
+
end
|
data/stomp_job.gemspec
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'stomp_job/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "stomp_job"
|
8
|
+
spec.version = StompJob::VERSION
|
9
|
+
spec.authors = ["Adam Braman", "Harsh Singh"]
|
10
|
+
spec.email = ["abraman@mgage.com", "hsingh@mGage.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Resque-like job library for message brokers implementing the STOMP protocol}
|
13
|
+
# spec.description = %q{TODO: Write a longer description or delete this line.}
|
14
|
+
spec.homepage = "https://github.com/mGageTechOps"
|
15
|
+
spec.license = "MIT"
|
16
|
+
|
17
|
+
# Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
|
18
|
+
# to allow pushing to a single host or delete this section to allow pushing to any host.
|
19
|
+
# if spec.respond_to?(:metadata)
|
20
|
+
# spec.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
|
21
|
+
# else
|
22
|
+
# raise "RubyGems 2.0 or newer is required to protect against " \
|
23
|
+
# "public gem pushes."
|
24
|
+
# end
|
25
|
+
|
26
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
27
|
+
f.match(%r{^(test|spec|features)/})
|
28
|
+
end
|
29
|
+
spec.require_paths = ["lib"]
|
30
|
+
|
31
|
+
spec.bindir = 'bin'
|
32
|
+
spec.executables << 'stomper'
|
33
|
+
|
34
|
+
spec.add_dependency 'stomp', '~> 1.4.0'
|
35
|
+
spec.add_dependency 'thor', '~> 0.19'
|
36
|
+
|
37
|
+
spec.add_development_dependency "bundler", "~> 1.13"
|
38
|
+
spec.add_development_dependency "rake"
|
39
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
40
|
+
spec.add_development_dependency "rspec-its", "~> 1.2.0"
|
41
|
+
spec.add_development_dependency "byebug", "~> 9.0.6"
|
42
|
+
|
43
|
+
# Used to test ActiveJob integration
|
44
|
+
spec.add_development_dependency "rails", "~> 5.0"
|
45
|
+
end
|
metadata
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: stomp_job
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Adam Braman
|
8
|
+
- Harsh Singh
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2017-01-10 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: stomp
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: 1.4.0
|
21
|
+
type: :runtime
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: 1.4.0
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: thor
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '0.19'
|
35
|
+
type: :runtime
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '0.19'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: bundler
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '1.13'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '1.13'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: rake
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - ">="
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rspec
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - "~>"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '3.0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - "~>"
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '3.0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: rspec-its
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - "~>"
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: 1.2.0
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - "~>"
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: 1.2.0
|
98
|
+
- !ruby/object:Gem::Dependency
|
99
|
+
name: byebug
|
100
|
+
requirement: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - "~>"
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: 9.0.6
|
105
|
+
type: :development
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - "~>"
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: 9.0.6
|
112
|
+
- !ruby/object:Gem::Dependency
|
113
|
+
name: rails
|
114
|
+
requirement: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - "~>"
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '5.0'
|
119
|
+
type: :development
|
120
|
+
prerelease: false
|
121
|
+
version_requirements: !ruby/object:Gem::Requirement
|
122
|
+
requirements:
|
123
|
+
- - "~>"
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '5.0'
|
126
|
+
description:
|
127
|
+
email:
|
128
|
+
- abraman@mgage.com
|
129
|
+
- hsingh@mGage.com
|
130
|
+
executables:
|
131
|
+
- stomper
|
132
|
+
extensions: []
|
133
|
+
extra_rdoc_files: []
|
134
|
+
files:
|
135
|
+
- ".gitignore"
|
136
|
+
- ".rspec"
|
137
|
+
- ".travis.yml"
|
138
|
+
- CODE_OF_CONDUCT.md
|
139
|
+
- Gemfile
|
140
|
+
- LICENSE.txt
|
141
|
+
- README.md
|
142
|
+
- Rakefile
|
143
|
+
- bin/console
|
144
|
+
- bin/stomper
|
145
|
+
- examples/scream_worker.rb
|
146
|
+
- lib/stomp_job.rb
|
147
|
+
- lib/stomp_job/cli.rb
|
148
|
+
- lib/stomp_job/configuration.rb
|
149
|
+
- lib/stomp_job/connection.rb
|
150
|
+
- lib/stomp_job/connection_exception.rb
|
151
|
+
- lib/stomp_job/extensions/active_job_adapter.rb
|
152
|
+
- lib/stomp_job/railtie.rb
|
153
|
+
- lib/stomp_job/version.rb
|
154
|
+
- lib/stomp_job/worker.rb
|
155
|
+
- lib/stomp_job/workers.rb
|
156
|
+
- lib/tasks/stomp_job.rake
|
157
|
+
- stomp_job.gemspec
|
158
|
+
homepage: https://github.com/mGageTechOps
|
159
|
+
licenses:
|
160
|
+
- MIT
|
161
|
+
metadata: {}
|
162
|
+
post_install_message:
|
163
|
+
rdoc_options: []
|
164
|
+
require_paths:
|
165
|
+
- lib
|
166
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
167
|
+
requirements:
|
168
|
+
- - ">="
|
169
|
+
- !ruby/object:Gem::Version
|
170
|
+
version: '0'
|
171
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
172
|
+
requirements:
|
173
|
+
- - ">="
|
174
|
+
- !ruby/object:Gem::Version
|
175
|
+
version: '0'
|
176
|
+
requirements: []
|
177
|
+
rubyforge_project:
|
178
|
+
rubygems_version: 2.6.8
|
179
|
+
signing_key:
|
180
|
+
specification_version: 4
|
181
|
+
summary: Resque-like job library for message brokers implementing the STOMP protocol
|
182
|
+
test_files: []
|