reliable-msg 1.0.1 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -28,7 +28,7 @@ The latest version of Reliable Messaging can be found at
28
28
 
29
29
  For more information
30
30
 
31
- * http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/RubyReliableMessaging
31
+ * http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/Ruby/ReliableMessaging
32
32
 
33
33
 
34
34
  == Installation
@@ -113,6 +113,10 @@ Stop the queue manager
113
113
 
114
114
  queues manager stop
115
115
 
116
+ == Change log
117
+
118
+ :include: changelog.txt
119
+
116
120
 
117
121
  == License
118
122
 
data/Rakefile CHANGED
@@ -1,30 +1,35 @@
1
1
  # Adapted from the rake Rakefile.
2
2
 
3
- require 'rubygems'
3
+ require "rubygems"
4
4
  Gem::manage_gems
5
- require 'rake/testtask'
6
- require 'rake/rdoctask'
7
- require 'rake/gempackagetask'
5
+ require "rake/testtask"
6
+ require "rake/rdoctask"
7
+ require "rake/gempackagetask"
8
8
 
9
9
 
10
10
  desc "Default Task"
11
11
  task :default => [:tests, :rdoc]
12
12
 
13
13
 
14
- desc "Run test case for UUID generator"
15
- Rake::TestTask.new :test_uuid do |test|
14
+ desc "Run test case for Queue API"
15
+ Rake::TestTask.new :test_queue do |test|
16
16
  test.verbose = true
17
- test.test_files = ['test/test-uuid.rb']
17
+ test.test_files = ["test/test-queue.rb"]
18
18
  end
19
- desc "Run test case for Queue API generator"
20
- Rake::TestTask.new :test_queue do |test|
19
+ desc "Run test case for Topic API"
20
+ Rake::TestTask.new :test_topic do |test|
21
+ test.verbose = true
22
+ test.test_files = ["test/test-topic.rb"]
23
+ end
24
+ desc "Run test case for Rails integration"
25
+ Rake::TestTask.new :test_rails do |test|
21
26
  test.verbose = true
22
- test.test_files = ['test/test-queue.rb']
27
+ test.test_files = ["test/test-rails.rb"]
23
28
  end
24
29
  desc "Run all test cases"
25
- Rake::TestTask.new :tests=>[:test_uuid, :test_queue] do |test|
30
+ Rake::TestTask.new :tests do |test|
26
31
  test.verbose = true
27
- test.test_files = []
32
+ test.test_files = ["test/*.rb"]
28
33
  #test.warning = true
29
34
  end
30
35
 
@@ -33,7 +38,7 @@ end
33
38
  Rake::RDocTask.new do |rdoc|
34
39
  rdoc.main = "README"
35
40
  rdoc.rdoc_files.include("README", "lib/**/*.rb")
36
- rdoc.title = 'Reliable Messaging'
41
+ rdoc.title = "Reliable Messaging"
37
42
  end
38
43
 
39
44
  # Handle version number.
@@ -43,7 +48,7 @@ class Version
43
48
 
44
49
  def initialize file, new_version
45
50
  @file = file
46
- @version = File.open @file, 'r' do |file|
51
+ @version = File.open @file, "r" do |file|
47
52
  version = nil
48
53
  file.each_line do |line|
49
54
  match = line.match PATTERN
@@ -78,7 +83,7 @@ class Version
78
83
  input.each_line do |line|
79
84
  match = line.match PATTERN
80
85
  if match
81
- output.puts "#{match[1]}VERSION = '#{@new_version}'"
86
+ output.puts "#{match[1]}VERSION = \"#{@new_version}\""
82
87
  else
83
88
  output.puts line
84
89
  end
@@ -90,12 +95,12 @@ class Version
90
95
  end
91
96
 
92
97
  end
93
- version = Version.new 'lib/reliable-msg.rb', ENV['version']
98
+ version = Version.new "lib/reliable-msg.rb", ENV["version"]
94
99
 
95
100
 
96
101
  # Create the GEM package.
97
102
  gem_spec = Gem::Specification.new do |spec|
98
- spec.name = 'reliable-msg'
103
+ spec.name = "reliable-msg"
99
104
  spec.version = version.next
100
105
  spec.summary = "Reliable messaging and persistent queues for building asynchronous applications in Ruby"
101
106
  spec.description = <<-EOF
@@ -108,18 +113,20 @@ gem_spec = Gem::Specification.new do |spec|
108
113
  EOF
109
114
  spec.author = "Assaf Arkin"
110
115
  spec.email = "assaf@labnotes.org"
111
- spec.homepage = "http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/RubyReliableMessaging"
116
+ spec.homepage = "http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/Ruby/ReliableMessaging"
112
117
 
113
- spec.files = FileList["{bin,test,lib,docs}/**/*", "README", "MIT-LICENSE", "Rakefile"].to_a
118
+ spec.files = FileList["{bin,test,lib,docs}/**/*", "README", "MIT-LICENSE", "Rakefile", "changelog.txt"].to_a
114
119
  spec.require_path = "lib"
115
- spec.autorequire = 'reliable-msg.rb'
120
+ spec.autorequire = "reliable-msg.rb"
116
121
  spec.bindir = "bin"
117
122
  spec.executables = ["queues"]
118
123
  spec.default_executable = "queues"
119
124
  spec.requirements << "MySQL for database store, otherwise uses the file system"
120
125
  spec.has_rdoc = true
121
- spec.rdoc_options << '--main' << 'README' << '--title' << 'Reliable Messaging for Ruby' << '--line-numbers'
126
+ spec.rdoc_options << "--main" << "README" << "--title" << "Reliable Messaging for Ruby" << "--line-numbers"
122
127
  spec.extra_rdoc_files = ["README"]
128
+ spec.rubyforge_project = "reliable-msg"
129
+ spec.add_dependency(%q<uuid>, [">= 1.0.0"])
123
130
  end
124
131
 
125
132
  gem = Rake::GemPackageTask.new(gem_spec) do |pkg|
@@ -130,7 +137,7 @@ end
130
137
 
131
138
  desc "Look for TODO and FIXME tags in the code"
132
139
  task :todo do
133
- FileList['**/*.rb'].egrep /#.*(FIXME|TODO|TBD)/
140
+ FileList["**/*.rb"].egrep /#.*(FIXME|TODO|TBD)/
134
141
  end
135
142
 
136
143
 
@@ -148,7 +155,7 @@ task :release => [:tests, :prerelease, :clobber, :update_version, :package] do
148
155
  end
149
156
 
150
157
  task :prerelease do
151
- if !version.changed? && ENV['reuse'] != version.number
158
+ if !version.changed? && ENV["reuse"] != version.number
152
159
  fail "Current version is #{version.number}, must specify reuse=ver to reuse existing version"
153
160
  end
154
161
  end
@@ -0,0 +1,32 @@
1
+ Release 1.1.0 (Nov 26, 2005)
2
+
3
+ * Added: Topic class for publishing messages on a topic.
4
+ * Added: Can set delivery option when creating a queue.
5
+ * Added: Queue.name() returns the queue's name.
6
+ * Added: Command line options to list or delete all messages
7
+ in named queue.
8
+ * Added: Rails integration for easily accessing queues/topics
9
+ from a Rails controller.
10
+ * Changed: Quque and Topic both extend the base class Client.
11
+ * Changed: Cannot start two queue managers in the same process.
12
+ * Changed: Each message has a created header indicating date/time
13
+ of creation. Received header no longer exists.
14
+ * Changed: Header retry renamed to delivered, to prevent clash with
15
+ reserved Ruby keyword.
16
+ * Changed: Selectors are now executed in the client process.
17
+ Selectors can rely on client variables, methods and constants.
18
+ * Changed: Specify maximum delivery attempts with the header
19
+ max_deliveries; get the redelivery attempt from the header
20
+ redelivery; the later is only set on the first redelivery attempt.
21
+ * Fixed: Documentation errors in Queue.
22
+ * Removed: Cannot associate default selector with queue.
23
+
24
+
25
+ Release 1.0.1 (Nov 10, 2005)
26
+
27
+ * Added: Test cases test put/get in memory and by reloading.
28
+ * Fixed: Messages not retrieved in order after queue manager
29
+ recovers when using MySQL message store.
30
+ * Fixed: Queue manager fails if stopped and then started again.
31
+
32
+
@@ -1,12 +1,17 @@
1
- $:.unshift(File.dirname(__FILE__)) unless
2
- $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
3
 
4
4
  module ReliableMsg
5
5
 
6
6
  PACKAGE = "reliable-msg"
7
- VERSION = '1.0.1'
7
+ VERSION = "1.1.0"
8
8
 
9
9
  end
10
10
 
11
- require 'reliable-msg/queue'
12
- require 'reliable-msg/cli'
11
+ require "reliable-msg/queue"
12
+ require "reliable-msg/topic"
13
+ require "reliable-msg/cli"
14
+ begin
15
+ require "reliable-msg/rails"
16
+ rescue LoadError
17
+ end
@@ -2,12 +2,11 @@
2
2
  # = cli.rb - Reliable messaging command-line interface
3
3
  #
4
4
  # Author:: Assaf Arkin assaf@labnotes.org
5
- # Documentation:: http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/RubyReliableMessaging
5
+ # Documentation:: http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/Ruby/ReliableMessaging
6
6
  # Copyright:: Copyright (c) 2005 Assaf Arkin
7
7
  # License:: MIT and/or Creative Commons Attribution-ShareAlike
8
8
  #
9
9
  #--
10
- # Changes:
11
10
  #++
12
11
 
13
12
 
@@ -43,6 +42,12 @@ Available commands:
43
42
  manager stop
44
43
  Stop a running queue manager.
45
44
 
45
+ list <queue>
46
+ List headers of messages in the named queue.
47
+
48
+ empty <queue>
49
+ Empty (delete all messages from) the named queue.
50
+
46
51
  install disk [<path>]
47
52
  Configure queue manager to use disk-based message store
48
53
  using the specified directory. Uses 'queues' by default.
@@ -68,7 +73,9 @@ Available options:
68
73
 
69
74
  EOF
70
75
 
71
- class InvalidUsage < Exception
76
+ MAX_STRING = 50
77
+
78
+ class InvalidUsage < Exception #:nodoc:
72
79
  end
73
80
 
74
81
  def initialize
@@ -149,6 +156,43 @@ EOF
149
156
  raise InvalidUsage
150
157
  end
151
158
 
159
+ when 'list'
160
+ queue = args[1]
161
+ raise InvalidUsage unless queue
162
+ begin
163
+ qm = queue_manager(config_file)
164
+ list = qm.list(:queue=>queue)
165
+ puts "Found #{list.size} messages in queue #{queue}"
166
+ list.each do |headers|
167
+ puts "Message #{headers[:id]}"
168
+ headers.each do |name, value|
169
+ unless name==:id
170
+ case value
171
+ when String
172
+ value = value[0..MAX_STRING - 3] << "..." if value.length > MAX_STRING
173
+ value = '"' << value.gsub('"', '\"') << '"'
174
+ when Symbol
175
+ value = ":#{value.to_s}"
176
+ end
177
+ puts " :#{name} => #{value}"
178
+ end
179
+ end
180
+ end
181
+ rescue DRb::DRbConnError =>error
182
+ puts "Cannot access queue manager: is it running?"
183
+ end
184
+
185
+ when 'empty'
186
+ queue = args[1]
187
+ raise InvalidUsage unless queue
188
+ begin
189
+ qm = queue_manager(config_file)
190
+ while msg = qm.enqueue(:queue=>queue)
191
+ end
192
+ rescue DRb::DRbConnError =>error
193
+ puts "Cannot access queue manager: is it running?"
194
+ end
195
+
152
196
  else
153
197
  raise InvalidUsage
154
198
  end
@@ -0,0 +1,213 @@
1
+ #
2
+ # = client.rb - Base class for queue/topic API
3
+ #
4
+ # Author:: Assaf Arkin assaf@labnotes.org
5
+ # Documentation:: http://trac.labnotes.org/cgi-bin/trac.cgi/wiki/Ruby/ReliableMessaging
6
+ # Copyright:: Copyright (c) 2005 Assaf Arkin
7
+ # License:: MIT and/or Creative Commons Attribution-ShareAlike
8
+ #
9
+ #--
10
+ #++
11
+
12
+ require 'drb'
13
+ require 'reliable-msg/selector'
14
+
15
+
16
+ module ReliableMsg
17
+
18
+ # Base class for both Queue and Topic client APIs.
19
+ class Client
20
+
21
+ ERROR_INVALID_SELECTOR = "Selector must be message identifier (String), set of header name/value pairs (Hash), Selector object, or nil" # :nodoc:
22
+
23
+ ERROR_INVALID_TX_TIMEOUT = "Invalid transaction timeout: must be a non-zero positive integer" # :nodoc:
24
+
25
+ ERROR_INVALID_CONNECT_COUNT = "Invalid connection count: must be a non-zero positive integer" # :nodoc:
26
+
27
+ ERROR_SELECTOR_VALUE_OR_BLOCK = "You can either pass a Selector object, or use a block" # :nodoc:
28
+
29
+ ERROR_INVALID_INIT_OPTION = "Unrecognized initialization option %s" #:nodoc:
30
+
31
+ # The default DRb port used to connect to the queue manager.
32
+ DRB_PORT = 6438
33
+
34
+ DEFAULT_DRB_URI = "druby://localhost:#{DRB_PORT}" #:nodoc:
35
+
36
+ # Number of times to retry a connecting to the queue manager.
37
+ DEFAULT_CONNECT_RETRY = 5
38
+
39
+ # Default transaction timeout.
40
+ DEFAULT_TX_TIMEOUT = 120
41
+
42
+ # Thread.current entry for queue transaction.
43
+ THREAD_CURRENT_TX = :reliable_msg_tx #:nodoc:
44
+
45
+ # The name of the dead letter queue (<tt>DLQ</tt>). Messages that expire or fail
46
+ # to process are automatically sent to the dead letter queue.
47
+ DLQ = DEAD_LETTER_QUEUE = "$dlq"
48
+
49
+ # DRb URI for queue manager. You can override this to change the URI globally,
50
+ # for all Queue objects that are not instantiated with an alternative URI.
51
+ @@drb_uri = DEFAULT_DRB_URI
52
+
53
+ # Reference to the local queue manager. Defaults to a DRb object, unless
54
+ # the queue manager is running locally.
55
+ @@qm = nil #:nodoc:
56
+
57
+ # Cache of queue managers referenced by their URI.
58
+ @@qm_cache = {} #:nodoc:
59
+
60
+ # Returns the transaction timeout (in seconds).
61
+ def tx_timeout
62
+ @tx_timeout || DEFAULT_TX_TIMEOUT
63
+ end
64
+
65
+ # Sets the transaction timeout (in seconds). Affects future transactions started
66
+ # by Queue.get. Use +nil+ to restore the default timeout.
67
+ def tx_timeout= timeout
68
+ if timeout
69
+ raise ArgumentError, ERROR_INVALID_TX_TIMEOUT unless timeout.instance_of?(Integer) and timeout > 0
70
+ @tx_timeout = timeout
71
+ else
72
+ @tx_timeout = nil
73
+ end
74
+ end
75
+
76
+ # Returns the number of connection attempts, before operations fail.
77
+ def connect_count
78
+ @connect_count || DEFAULT_CONNECT_RETRY
79
+ end
80
+
81
+ # Sets the number of connection attempts, before operations fail. The minimum is one.
82
+ # Use +nil+ to restore the default connection count.
83
+ def connect_count= count
84
+ if count
85
+ raise ArgumentError, ERROR_INVALID_CONNECT_COUNT unless count.instance_of?(Integer) and count > 0
86
+ @connect_count = count
87
+ else
88
+ @connect_count = nil
89
+ end
90
+ end
91
+
92
+ # Create and return a new selector based on the block expression. Same as
93
+ # Selector.new. For example:
94
+ # selector = Queue.selector { priority >= 2 and received > Time.new.to_i - 60 }
95
+ def self.selector &block
96
+ raise ArgumentError, ERROR_NO_SELECTOR_BLOCK unless block
97
+ Selector.new &block
98
+ end
99
+
100
+ # Create and return a new selector based on the block expression. Same as
101
+ # Selector.new. For example:
102
+ # selector = Queue.selector { priority >= 2 and received > Time.new.to_i - 60 }
103
+ def selector & block
104
+ raise ArgumentError, ERROR_NO_SELECTOR_BLOCK unless block
105
+ Selector.new &block
106
+ end
107
+
108
+ private
109
+
110
+ # Returns the active queue manager. You can override this method to implement
111
+ # load balancing.
112
+ def qm
113
+ if uri = @drb_uri
114
+ # Queue specifies queue manager's URI: use that queue manager.
115
+ @@qm_cache[uri] ||= DRbObject.new(nil, uri)
116
+ else
117
+ # Use the same queue manager for all queues, and cache it.
118
+ # Create only the first time.
119
+ @@qm ||= DRbObject.new(nil, @@drb_uri || DEFAULT_DRB_URI)
120
+ end
121
+ end
122
+
123
+ # Called to execute the operation repeatedly and avoid connection failures. This only
124
+ # makes sense if we have a load balancing algorithm.
125
+ def repeated &block
126
+ count = connect_count
127
+ begin
128
+ block.call qm
129
+ rescue DRb::DRbConnError=>error
130
+ warn error
131
+ warn error.backtrace
132
+ retry if (count -= 1) > 0
133
+ raise error
134
+ end
135
+ end
136
+
137
+ class << self
138
+ private
139
+ # Sets the active queue manager. Used when the queue manager is running in the
140
+ # same process to bypass DRb calls.
141
+ def qm= qm
142
+ @@qm = qm
143
+ end
144
+ end
145
+
146
+ end
147
+
148
+
149
+ # == Retrieved Message
150
+ #
151
+ # Returned from Queue.get holding the last message retrieved from the
152
+ # queue and providing access to the message identifier, headers and object.
153
+ #
154
+ # For example:
155
+ # while queue.get do |msg|
156
+ # print "Message #{msg.id}"
157
+ # print "Headers: #{msg[:created]}"
158
+ # print "Headers: #{msg.headers.inspect}"
159
+ # print msg.object
160
+ # true
161
+ # end
162
+ class Message
163
+
164
+ def initialize id, headers, object # :nodoc:
165
+ @id, @object, @headers = id, object, headers
166
+ end
167
+
168
+ # Returns the message identifier.
169
+ #
170
+ # :call-seq:
171
+ # msg.id -> id
172
+ #
173
+ def id
174
+ @id
175
+ end
176
+
177
+ # Returns the message object.
178
+ #
179
+ # :call-seq:
180
+ # msg.object -> obj
181
+ #
182
+ def object
183
+ @object
184
+ end
185
+
186
+ # Returns the message headers.
187
+ #
188
+ # :call-seq:
189
+ # msg.headers -> hash
190
+ #
191
+ def headers
192
+ @headers
193
+ end
194
+
195
+ # Returns the message header.
196
+ #
197
+ # :call-seq:
198
+ # msg[symbol] -> obj
199
+ #
200
+ def [] symbol
201
+ @headers[symbol]
202
+ end
203
+
204
+ private
205
+ def method_missing symbol, *args, &block
206
+ raise ArgumentError, "Wrong number of arguments (#{args.length} for 0)" unless args.empty?
207
+ @headers[symbol]
208
+ end
209
+
210
+ end
211
+
212
+ end
213
+