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 CHANGED
@@ -1,6 +1,80 @@
1
- = rmb-rails
1
+ 
2
+ = rmb-rails: RESTful Message Beans for Ruby on Rails
2
3
 
3
- Description goes here.
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 = "keburgett@gmail.com"
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.3
1
+ 0.0.6
@@ -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 controller script
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__)}/#{LD}control.rb #{action}"
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] = "#{LD}#{@hash[:key]}"
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")
@@ -7,7 +7,8 @@
7
7
 
8
8
  require 'rubygems'
9
9
  require 'logger'
10
- require 'rmb'
10
+ require 'rmb-rails'
11
+ include RMB
11
12
 
12
13
 
13
14
  #
@@ -4,7 +4,8 @@ require 'rubygems'
4
4
  #
5
5
  require 'logger'
6
6
  require 'daemons'
7
- require 'rmb'
7
+ require 'rmb-rails'
8
+ include RMB
8
9
 
9
10
 
10
11
  #
data/lib/listener_main.rb CHANGED
@@ -76,7 +76,7 @@ particular Listener instance), the initialize method sets up the entire daemon.
76
76
  end
77
77
 
78
78
  def app_name
79
- "#{LD}#{@key}"
79
+ "#{RMB::Properties.daemon_prefix}#{@key}"
80
80
  end
81
81
  end
82
82
 
@@ -11,7 +11,7 @@ module RMB
11
11
  def properties=(hash)
12
12
  super
13
13
  @hash = hash
14
- @daemon_name = "#{LD}#{hash[:key]}"
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
- # This multi-level hash contains the initial set of properties needed to launch a Listener daemon.
16
- #* Make a writable copy of this hash
17
- #* Fill in the areas denoted by <<fill>> with your values
18
- #* Add additional properties required by the Subscriber and Submitter classes used
19
- #* Pass this has as the sole parameter in listener_client = ListenerClient.new(hash) to set up the daemon
20
- #* Call listener_client.start to start the daemon.
21
- RMB_Properties = {
22
- :key => "<<fill>>",
23
- :subscriber => {
24
- :class_name => "<<fill>>" #set this to selected subclass of Subscriber
25
- # <== add more properties for your Subscriber here
26
- },
27
- :submitter => {
28
- :class_name => "<<fill>>" #set this to selected subclass of Submitter
29
- # <== add more properties for your Submitter here
30
- },
31
- :working_dir => "<<fill>>", #set this to the RAILS_ROOT directory
32
-
33
- :daemon_options => { #these options are passed directly to the class method Daemons.run(target, options)
34
- :app_name => "", #custom name for this daemon, set by ListenerClient#setup
35
- :ARGV => nil, #use the program defaults
36
- :dir_mode => :normal, #requires absolute path
37
- :dir => "<<fill>>", #this is set to "#{working_dir}/tmp/pids" by ListenerClient#setup
38
- :multiple => false, #this will allow multiple daemons to run
39
- :ontop => false, #
40
- :mode => :load,
41
- :backtrace => false,
42
- :monitor => false,
43
- :log_output => true,
44
- :keep_pid_files => true,
45
- :hard_exit => true
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
- LD = "listener_daemon_" #prefix for all listener_daemon artifacts produced: pids, log files, property files, and message files
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/submitter.rb CHANGED
@@ -1,3 +1,5 @@
1
+
2
+
1
3
  module RMB
2
4
 
3
5
  class Submitter
data/lib/subscriber.rb CHANGED
@@ -1,3 +1,4 @@
1
+
1
2
  module RMB
2
3
 
3
4
  class Subscriber
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"
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-22}
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.5
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-22 00:00:00 -07:00
12
+ date: 2009-07-23 00:00:00 -07:00
13
13
  default_executable:
14
- dependencies: []
15
-
16
- description:
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
@@ -1,4 +0,0 @@
1
- ---
2
- :major: 0
3
- :minor: 0
4
- :patch: 5
data/bounce-gem.sh DELETED
@@ -1,4 +0,0 @@
1
- cd /home/kenb/development/rmb-rails
2
- rake build
3
- cd pkg
4
- gem install rmb-rails