explainer-rmb-rails 0.0.5 → 0.0.6
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.
- data/README.rdoc +76 -2
- data/Rakefile +43 -3
- data/VERSION +1 -1
- data/lib/listener_client.rb +3 -3
- data/lib/listener_daemon.rb +2 -1
- data/lib/listener_daemon_control.rb +2 -1
- data/lib/listener_main.rb +1 -1
- data/lib/mechanize_submitter.rb +1 -1
- data/lib/rmb-rails.rb +43 -52
- data/lib/submitter.rb +2 -0
- data/lib/subscriber.rb +1 -0
- data/rmb-rails.gemspec +12 -4
- metadata +34 -7
- data/VERSION.yml +0 -4
- data/bounce-gem.sh +0 -4
data/README.rdoc
CHANGED
@@ -1,6 +1,80 @@
|
|
1
|
-
|
1
|
+
|
2
|
+
= rmb-rails: RESTful Message Beans for Ruby on Rails
|
2
3
|
|
3
|
-
|
4
|
+
= Introduction
|
5
|
+
|
6
|
+
During a lengthy career in software development, and later as an enterprise architect, I saw a lot of message broker deployments in many
|
7
|
+
large corporations. These systems were used primarily to knit together disparate computing 'silos' into a single fabric, as primarily
|
8
|
+
an integration tool. The implementations varied significantly, some were well engineered and managed, others were basket cases of
|
9
|
+
poor planning. One significant problem that was common in many of these deployments was the requirement to add broker subscriber
|
10
|
+
behavior inside existing applications. Many of these applications were single threaded (often written in COBOL) and had no real
|
11
|
+
ability to construct a thread to wait on some subscriber.receive method. This issue was resolved by a variety of means, many of
|
12
|
+
which involved polling the subscriber queue periodically, picking up some or all of the messages waiting, and processing them
|
13
|
+
through the application. Polling does not scale well. If the application had many worker instances, they all tended to compete
|
14
|
+
with one another and together, consumed a lot of CPU time in polling loops. If you slowed down the polling frequency, then you
|
15
|
+
got latency problems where messages sat in the queue when the readers were sleeping. The real solution was to construct some form
|
16
|
+
of subscriber adapter that could inject messages into the applications' standard incoming data stream. The Enterprise Java Message
|
17
|
+
Bean is a good example of this kind of adapter. Message arrival on a designated queue or topic caused the activation of a Message Bean
|
18
|
+
within the EJB container, allowing full participation in transactions within the container.
|
19
|
+
|
20
|
+
There is a real parallel between a Java EJB application and a Ruby-on-Rails application, and the Rails environment needs a method to
|
21
|
+
get incoming message broker subscriptions injected directly into the main Rails data stream. So, how can we convert an MQ message
|
22
|
+
into an http request? The rubygem rmb-rails addresses this issue.
|
23
|
+
|
24
|
+
= Theory of Operation
|
25
|
+
|
26
|
+
As stated above, experience shows that polling a queue to pick up messages waiting there does not scale well. I decided to create an
|
27
|
+
infrastructure where daemon processes, called listeners, can connect to a specified message broker, receive messages from that broker,
|
28
|
+
and then forward them on to a standard Rails controller using a standard Rails protocol. In other words, the daemon listener processes
|
29
|
+
act as browsers doing a form of file upload. The receiving rails controller is built on standard REST principles, and fits easily into
|
30
|
+
any Rails application.
|
31
|
+
|
32
|
+
= Component classes
|
33
|
+
|
34
|
+
* module RMB
|
35
|
+
The rmb-rails gem is packaged within a single module, called RMB. The component classes are described below. See the rdoc for each class for a full description.
|
36
|
+
* class RMB::Properties
|
37
|
+
The primary interface between the gem and the calling rails app consists of a multilevel hash. The hash is partially complete, but requires that several key value pairs be supplied.
|
38
|
+
* class RMB::ListenerClient
|
39
|
+
This class implements the interface between the calling application and the daemon listeners.
|
40
|
+
* class RMB::ListenerMain
|
41
|
+
This class, along with concrete Subscriber and Submitter subclasses, makes up the daemon listener process.
|
42
|
+
* class RMB::Subscriber
|
43
|
+
This is an abstract superclass for all Subscriber classes. Every daemon has an instance of a Subscriber class acting as the 'front end'
|
44
|
+
of the daemon, listening and blocking on the broker.
|
45
|
+
* class RMB::Submitter
|
46
|
+
This is an abstract superclass for all Submitter classes. Every daemon has an instance of a Submitter class acting as the
|
47
|
+
forwarding agent, taking messages received by the subscriber, packaging the message as an http request, and posts it to the specified controller/action pair.
|
48
|
+
* class RMB::StompSubscriber
|
49
|
+
This is a concrete subclass of Subscriber, which uses the Stomp protocol to listen to an ActiveMQ message broker.
|
50
|
+
* class RMB::MechanizeSubmitter
|
51
|
+
This is a concrete subclass of Submitter, which uses the WWW:Mechanize gem to package the message as a document and submit it to the controller.
|
52
|
+
|
53
|
+
= Installation
|
54
|
+
|
55
|
+
You will need an ActiveMQ message broker to use this gem. See http://activemq.apache.org/ for download and configuration instructions.
|
56
|
+
|
57
|
+
$ gem sources -a http://gems.github.com (you only have to do this once)
|
58
|
+
$ sudo gem install explainer-rmb-rails
|
59
|
+
|
60
|
+
or
|
61
|
+
|
62
|
+
$ sudo gem install rmb-rails (to get the same gem from rubyforge)
|
63
|
+
|
64
|
+
= Usage
|
65
|
+
|
66
|
+
Add the following require statement to your code to get access to this gem:
|
67
|
+
|
68
|
+
require 'rmb-rails'
|
69
|
+
|
70
|
+
Create an instance of RMB:ListenerClient to start/stop each listener daemon used. Get the
|
71
|
+
RESTful-Message-Beans(http://github.com/explainer/RESTful-Message-Beans/tree/master) rails app, which provides a complete
|
72
|
+
demo rails application using this gem.
|
73
|
+
|
74
|
+
== Credits
|
75
|
+
{ActiveMQ}[http://activemq.apache.org/]
|
76
|
+
{Enterprise Recipes with Ruby and Rails}[http://www.pragprog.com/titles/msenr/enterprise-recipes-with-ruby-and-rails]
|
77
|
+
{Jeweler gem}[http://technicalpickles.github.com/jeweler/]
|
4
78
|
|
5
79
|
== Copyright
|
6
80
|
|
data/Rakefile
CHANGED
@@ -4,13 +4,53 @@ require 'rake'
|
|
4
4
|
begin
|
5
5
|
require 'jeweler'
|
6
6
|
Jeweler::Tasks.new do |gem|
|
7
|
+
|
8
|
+
gem.description = <<-EOF
|
9
|
+
...something interesting here...
|
10
|
+
EOF
|
11
|
+
|
7
12
|
gem.name = "rmb-rails"
|
8
13
|
gem.summary = %Q{RESTful Message Beans for Rails}
|
9
|
-
gem.email =
|
10
|
-
gem.homepage = "http://github.com/explainer/rmb-rails"
|
14
|
+
gem.email = %Q{keburgett@gmail.com}
|
11
15
|
gem.authors = ["Ken Burgett"]
|
12
|
-
gem.rubyforge_project = "rmb-rails"
|
13
16
|
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
17
|
+
|
18
|
+
gem.has_rdoc = true
|
19
|
+
gem.homepage = %q{http://github.com/explainer/rmb-rails}
|
20
|
+
gem.rdoc_options = ["--charset=UTF-8"]
|
21
|
+
gem.require_paths = ["lib"]
|
22
|
+
gem.rubyforge_project = %q{rmb-rails}
|
23
|
+
gem.rubygems_version = %q{1.3.1}
|
24
|
+
|
25
|
+
gem.add_dependency(%q<stomp>, [">= 1.1"])
|
26
|
+
gem.add_dependency(%q<mechanize>, ["= 0.9.2"])
|
27
|
+
gem.add_dependency(%q<daemons>, [">= 1.1.10"])
|
28
|
+
|
29
|
+
gem.files = [
|
30
|
+
".document",
|
31
|
+
".gitignore",
|
32
|
+
"LICENSE",
|
33
|
+
"README.rdoc",
|
34
|
+
"Rakefile",
|
35
|
+
"VERSION",
|
36
|
+
"lib/listener_client.rb",
|
37
|
+
"lib/listener_daemon.rb",
|
38
|
+
"lib/listener_daemon_control.rb",
|
39
|
+
"lib/listener_main.rb",
|
40
|
+
"lib/mechanize_submitter.rb",
|
41
|
+
"lib/rmb-rails.rb",
|
42
|
+
"lib/stomp_subscriber.rb",
|
43
|
+
"lib/submitter.rb",
|
44
|
+
"lib/subscriber.rb",
|
45
|
+
"rmb-rails.gemspec",
|
46
|
+
"test/rmb-rails_test.rb",
|
47
|
+
"test/test_helper.rb"
|
48
|
+
]
|
49
|
+
|
50
|
+
gem.extra_rdoc_files = [
|
51
|
+
"LICENSE",
|
52
|
+
"README.rdoc"
|
53
|
+
]
|
14
54
|
end
|
15
55
|
|
16
56
|
Jeweler::RubyforgeTasks.new
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.6
|
data/lib/listener_client.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
module RMB
|
2
2
|
|
3
3
|
#
|
4
|
-
# This code is called from the client application. It starts/stops the daemon
|
4
|
+
# This code is called from the client application. It starts/stops the daemon via a control script
|
5
5
|
#
|
6
6
|
class ListenerClient
|
7
7
|
|
@@ -19,7 +19,7 @@ module RMB
|
|
19
19
|
|
20
20
|
def control(action)
|
21
21
|
setup
|
22
|
-
control_script = "ruby #{File.dirname(__FILE__)}/#{
|
22
|
+
control_script = "ruby #{File.dirname(__FILE__)}/#{RMB::Properties.daemon_prefix}control.rb #{action}"
|
23
23
|
control_params = "#{@hash[:working_dir]} #{@hash[:key]}"
|
24
24
|
system("#{control_script} #{control_params} -- #{control_params}")
|
25
25
|
end
|
@@ -40,7 +40,7 @@ private
|
|
40
40
|
|
41
41
|
def setup
|
42
42
|
# add derived values to the daemon_options hash
|
43
|
-
@hash[:daemon_options][:app_name] = "#{
|
43
|
+
@hash[:daemon_options][:app_name] = "#{RMB::Properties.daemon_prefix}#{@hash[:key]}"
|
44
44
|
@hash[:daemon_options][:dir] = File.join("#{@hash[:working_dir]}", "tmp", "pids")
|
45
45
|
# Ensure the properties folder is present
|
46
46
|
properties_dir = File.join("#{@hash[:working_dir]}", "tmp", "properties")
|
data/lib/listener_daemon.rb
CHANGED
data/lib/listener_main.rb
CHANGED
data/lib/mechanize_submitter.rb
CHANGED
@@ -11,7 +11,7 @@ module RMB
|
|
11
11
|
def properties=(hash)
|
12
12
|
super
|
13
13
|
@hash = hash
|
14
|
-
@daemon_name = "#{
|
14
|
+
@daemon_name = "#{RMB::Properties.daemon_prefix}#{hash[:key]}"
|
15
15
|
submitter = hash[:submitter]
|
16
16
|
@user = submitter[:user] || ""
|
17
17
|
@password = submitter[:password] || ""
|
data/lib/rmb-rails.rb
CHANGED
@@ -12,58 +12,49 @@ require 'submitter'
|
|
12
12
|
require 'subscriber'
|
13
13
|
|
14
14
|
module RMB
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
#*
|
20
|
-
#*
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
15
|
+
|
16
|
+
class Properties
|
17
|
+
|
18
|
+
# This multi-level hash contains the initial set of properties needed to launch a Listener daemon.
|
19
|
+
#* Use this class method to get writable copy of this hash
|
20
|
+
#* Fill in the areas denoted by <<fill>> with your values
|
21
|
+
#* Add additional properties required by the Subscriber and Submitter classes used
|
22
|
+
#* Pass this has as the sole parameter in listener_client = ListenerClient.new(hash) to set up the daemon
|
23
|
+
#* Call listener_client.start to start the daemon, and listener_client.stop to stop the daemon
|
24
|
+
#
|
25
|
+
def self.properties
|
26
|
+
hash = {
|
27
|
+
:key => "<<fill>>",
|
28
|
+
:subscriber => {
|
29
|
+
:class_name => "<<fill>>" #set this to selected subclass of Subscriber
|
30
|
+
# <== add more properties for your Subscriber here
|
31
|
+
},
|
32
|
+
:submitter => {
|
33
|
+
:class_name => "<<fill>>" #set this to selected subclass of Submitter
|
34
|
+
# <== add more properties for your Submitter here
|
35
|
+
},
|
36
|
+
:working_dir => "<<fill>>", #set this to the RAILS_ROOT directory
|
37
|
+
|
38
|
+
:daemon_options => { #these options are passed directly to the class method Daemons.run(target, options)
|
39
|
+
:app_name => "", #custom name for this daemon, set by ListenerClient#setup
|
40
|
+
:ARGV => nil, #use the program defaults
|
41
|
+
:dir_mode => :normal, #requires absolute path
|
42
|
+
:dir => "<<fill>>", #this is set to "#{working_dir}/tmp/pids" by ListenerClient#setup
|
43
|
+
:multiple => false, #this will allow multiple daemons to run
|
44
|
+
:ontop => false, #
|
45
|
+
:mode => :load,
|
46
|
+
:backtrace => false,
|
47
|
+
:monitor => false,
|
48
|
+
:log_output => true,
|
49
|
+
:keep_pid_files => true,
|
50
|
+
:hard_exit => true
|
51
|
+
}
|
52
|
+
}
|
53
|
+
end
|
48
54
|
|
49
|
-
|
50
|
-
|
55
|
+
def self.daemon_prefix
|
56
|
+
"listener_daemon_" #prefix for all listener_daemon artifacts produced: pids, log files, property files, and message files
|
57
|
+
end
|
58
|
+
end
|
51
59
|
end
|
52
60
|
|
53
|
-
if __FILE__ == $0
|
54
|
-
hash = RMB::ListenerClient.properties('inventory', RAILS_ROOT, 'StompSubscriber', 'MechanizeSubmitter')
|
55
|
-
hash[:daemon_options][:dir] = File.join("#{hash[:working_dir]}", "tmp", "pids")
|
56
|
-
front = hash[:subscriber]
|
57
|
-
front[:url] = '/topic/inventory'
|
58
|
-
front[:host] = 'localhost'
|
59
|
-
front[:port] = 61613
|
60
|
-
front[:user] = nil
|
61
|
-
front[:password] = nil
|
62
|
-
|
63
|
-
back = hash[:submitter]
|
64
|
-
back[:login_url] = 'http://localhost:3000/login'
|
65
|
-
back[:delivery_url] = 'http://localhost:3000/documents/new'
|
66
|
-
lc = RMB::ListenerClient.new(hash)
|
67
|
-
puts lc.inspect
|
68
|
-
lc.start
|
69
|
-
end
|
data/lib/subscriber.rb
CHANGED
data/rmb-rails.gemspec
CHANGED
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
Gem::Specification.new do |s|
|
4
4
|
s.name = %q{rmb-rails}
|
5
|
-
s.version = "0.0.
|
5
|
+
s.version = "0.0.6"
|
6
6
|
|
7
7
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
8
|
s.authors = ["Ken Burgett"]
|
9
|
-
s.date = %q{2009-07-
|
9
|
+
s.date = %q{2009-07-23}
|
10
|
+
s.description = %q{...something interesting here...}
|
10
11
|
s.email = %q{keburgett@gmail.com}
|
11
12
|
s.extra_rdoc_files = [
|
12
13
|
"LICENSE",
|
@@ -19,8 +20,6 @@ Gem::Specification.new do |s|
|
|
19
20
|
"README.rdoc",
|
20
21
|
"Rakefile",
|
21
22
|
"VERSION",
|
22
|
-
"VERSION.yml",
|
23
|
-
"bounce-gem.sh",
|
24
23
|
"lib/listener_client.rb",
|
25
24
|
"lib/listener_daemon.rb",
|
26
25
|
"lib/listener_daemon_control.rb",
|
@@ -51,8 +50,17 @@ Gem::Specification.new do |s|
|
|
51
50
|
s.specification_version = 2
|
52
51
|
|
53
52
|
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
53
|
+
s.add_runtime_dependency(%q<stomp>, [">= 1.1"])
|
54
|
+
s.add_runtime_dependency(%q<mechanize>, ["= 0.9.2"])
|
55
|
+
s.add_runtime_dependency(%q<daemons>, [">= 1.1.10"])
|
54
56
|
else
|
57
|
+
s.add_dependency(%q<stomp>, [">= 1.1"])
|
58
|
+
s.add_dependency(%q<mechanize>, ["= 0.9.2"])
|
59
|
+
s.add_dependency(%q<daemons>, [">= 1.1.10"])
|
55
60
|
end
|
56
61
|
else
|
62
|
+
s.add_dependency(%q<stomp>, [">= 1.1"])
|
63
|
+
s.add_dependency(%q<mechanize>, ["= 0.9.2"])
|
64
|
+
s.add_dependency(%q<daemons>, [">= 1.1.10"])
|
57
65
|
end
|
58
66
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: explainer-rmb-rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ken Burgett
|
@@ -9,11 +9,40 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-07-
|
12
|
+
date: 2009-07-23 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: stomp
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: "1.1"
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: mechanize
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.9.2
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: daemons
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: 1.1.10
|
44
|
+
version:
|
45
|
+
description: ...something interesting here...
|
17
46
|
email: keburgett@gmail.com
|
18
47
|
executables: []
|
19
48
|
|
@@ -29,8 +58,6 @@ files:
|
|
29
58
|
- README.rdoc
|
30
59
|
- Rakefile
|
31
60
|
- VERSION
|
32
|
-
- VERSION.yml
|
33
|
-
- bounce-gem.sh
|
34
61
|
- lib/listener_client.rb
|
35
62
|
- lib/listener_daemon.rb
|
36
63
|
- lib/listener_daemon_control.rb
|
data/VERSION.yml
DELETED
data/bounce-gem.sh
DELETED