nebulous_stomp 1.1.5

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bbf8f806ca67f277f05c1f79b35367b60ddca32e
4
+ data.tar.gz: fc74eef0d2815b4e71ea82416aec54d67805f2cc
5
+ SHA512:
6
+ metadata.gz: 268e71ae7696273b5e02eeffdf6e16365c4f9b467fa7fec54f3f0a2454510312da99d9a08e136ff16d054dbc02cb9b828f5052ec2b7cf936f774460b6f377db4
7
+ data.tar.gz: 4a81d73c3bdc947cc3c0d6b4d59a94b780c56b18d6ece2685077d10ea5f98b94e523d9a3ad3becc96b3d96b83e4924a9d49ac8e8eaa472456a70d77f6f9c4f9f
data/.hgignore ADDED
@@ -0,0 +1,19 @@
1
+ syntax: regexp
2
+
3
+ ^\.bundle/
4
+ ^Gemfile.lock
5
+ ^_yardoc/
6
+ ^coverage/
7
+ ^doc/
8
+ ^pkg/
9
+ ^spec/reports/
10
+ ^tmp/
11
+
12
+ syntax: glob
13
+ *.bundle
14
+ *.so
15
+ *.o
16
+ *.a
17
+ mkmf.log
18
+ .ruby-*
19
+ .Project
data/.hgtags ADDED
@@ -0,0 +1,16 @@
1
+ b3bba0a8b1f87257bd0ce2635483fa855f71185e 0.0.3
2
+ 40dd1093014ca0752093e5ab59080d88ed8ebd69 0.1.0
3
+ 90fdec53c9539cef35a51b9c45569097c6170037 1.0.0
4
+ 90fdec53c9539cef35a51b9c45569097c6170037 1.0.0
5
+ 0000000000000000000000000000000000000000 1.0.0
6
+ 0000000000000000000000000000000000000000 1.0.0
7
+ 6b772affe7030b28200354909dbd85cb81d4c161 1.0.0
8
+ 4c2c5c72dde17de6f99aa07c13627354d04fdb81 1.1.0
9
+ 9fff41d765e8ff837c0322024a8780e294c37945 1.1.1
10
+ 33554a6d9a5d9ef2891524f53603b49180a6112e 1.1.2
11
+ fc012c81f38ba6c26f47caacd3f29eb27f7d28b1 1.1.3
12
+ cff04defabb0895ff2f032087b97ea9616bee6a9 1.14
13
+ cff04defabb0895ff2f032087b97ea9616bee6a9 1.14
14
+ 0000000000000000000000000000000000000000 1.14
15
+ 8ad003b30ff2d61d481343ba8d822ff2ffafaabb 1.1.4
16
+ 6ddf180c9a2fc1cef55f5d128faa4d5df28d1353 1.1.5
data/.rspec ADDED
@@ -0,0 +1,8 @@
1
+ --color
2
+ --require spec_helper
3
+ #--require doc_no_pending
4
+ --require nebulous
5
+ -I .
6
+
7
+ #--fail-fast
8
+ #--format documentation
data/.travis.yml ADDED
@@ -0,0 +1,3 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
data/.yardoc/checksums ADDED
@@ -0,0 +1,10 @@
1
+ lib/nebulous.rb abf6f1206a030a8af2e0baab1372b4a9b489c2d1
2
+ lib/nebulous/param.rb 2d2f933b93717eebff5a539c0e0577aaed060093
3
+ lib/nebulous/version.rb e7ad88512b8b20bc8a0e3254f5a10ed9cb58feb9
4
+ lib/nebulous/message.rb cf705fae7bd51c78bb26835bb1b1bc8d142a4f9b
5
+ lib/nebulous/nebrequest.rb 79dd0a25e6a05f352dd7cfa9e6398c95f68ae830
6
+ lib/nebulous/redis_handler.rb 5c85af0402ba532f978794cb6cf2ce9076e5230c
7
+ lib/nebulous/stomp_handler.rb 6d7e4cc0663948dc4cd10de9f570f745275be18a
8
+ lib/nebulous/nebrequest_null.rb 5d2fc0f5c408a3d076005f8c13d5e56c6ac15259
9
+ lib/nebulous/stomp_handler_null.rb aa8ece8831fb2bc5739746ef16de5fd7c0a2e6ff
10
+ lib/nebulous/redis_handler_null.rb c9dbad237ddc70d6ff48a26f8c63939a5e0aeddd
Binary file
Binary file
Binary file
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in nebulous.gemspec
4
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,17 @@
1
+ clearing :on
2
+
3
+ guard :rspec, cmd: 'rspec' do
4
+
5
+ watch( %r{^lib/*(.+).rb$} ) do |m|
6
+ "spec/#{m[1]}_spec.rb"
7
+ end
8
+
9
+ watch( %r{^lib/nebulous/*(.+).rb$} ) do |m|
10
+ "spec/#{m[1]}_spec.rb"
11
+ end
12
+
13
+ watch( %r{^spec/*(.+).rb$} ) do |m|
14
+ "spec/#{m[1]}.rb"
15
+ end
16
+
17
+ end
data/README.md ADDED
@@ -0,0 +1,38 @@
1
+ Nebulous
2
+ ========
3
+
4
+ A little module that implements the Nebulous Protocol, a way of passing data
5
+ over STOMP between different systems. We also support message cacheing via
6
+ Redis.
7
+
8
+ There are two use cases:
9
+
10
+ First, sending a request for information and waiting for a response, which
11
+ might come from a cache of previous responses, if you allow it. To do
12
+ this you should create a Nebulous::NebRequest, which will return a
13
+ Nebulous::Message.
14
+
15
+ Second, the other end of the deal: hanging around waiting for requests and
16
+ sending responses. To do this, you need to use the Nebulous::StompHandler
17
+ class, which will again furnish Nebulous::Meessage objects, and allow you to
18
+ create them.
19
+
20
+ Some configuratuion is required: see Nebulous.init, Nebulous.add_target &
21
+ Nebulous.add_logger.
22
+
23
+ Since you are setting the Redis connection details as part of initialisation,
24
+ you can also use it to connect to Redis, if you want. See
25
+ Nebulous::RedisHandler.
26
+
27
+ a complete list of classes & modules:
28
+
29
+ * Nebulous
30
+ * Nebulous::Param
31
+ * Nebulous::NebRequest
32
+ * Nebulous::NebRequestNull
33
+ * Nebulous::Message
34
+ * Nebulous::StompHandler
35
+ * Nebulous::StompHandlerNull
36
+ * Nebulous::RedisHandler
37
+ * Nebulous::RedisHandlerNull
38
+
data/Rakefile ADDED
@@ -0,0 +1,31 @@
1
+ #require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+ require 'rdoc/task'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ namespace :rdoc do
8
+ desc "Generate local docs"
9
+ RDoc::Task.new do |rdoc|
10
+ rdoc.main = "md/README.md"
11
+ rdoc.rdoc_files.include("lib/*", "md/*")
12
+ rdoc.rdoc_dir = "doc"
13
+ end
14
+
15
+ desc "Push doc to HARS"
16
+ task :hars do
17
+ sh "rsync -aP --delete doc/ /home/hars/hars/public/nebulous"
18
+ end
19
+ end
20
+
21
+ desc "Start Guard"
22
+ task :guard do
23
+ sh "bundle exec guard"
24
+ end
25
+
26
+ desc "Update vim tag data"
27
+ task :retag do
28
+ sh "ripper-tags -R"
29
+ end
30
+
31
+
@@ -0,0 +1,368 @@
1
+ # COding: UTF-8
2
+
3
+ require 'json'
4
+
5
+ require_relative 'stomp_handler'
6
+
7
+
8
+ module Nebulous
9
+
10
+
11
+ ##
12
+ # A class to encapsulate a Nebulous message (which is built on top of a
13
+ # STOMP message)
14
+ #
15
+ # Note that this class REPLACES the old NebResponse class from 0.1.0. There
16
+ # are differences:
17
+ # * response.body -> message.stomp_body
18
+ # * response.headers -> message.stomp_headers
19
+ # * to_cache now returns a Hash, not a JSON string
20
+ #
21
+ class Message
22
+
23
+ # Might be nil: parsed from incoming messages; set by StompHandler on send
24
+ attr_accessor :reply_id
25
+
26
+ # Might be nil: only caught on messages that came directly from STOMP
27
+ attr_reader :stomp_headers, :stomp_body
28
+
29
+ # The content type of the message
30
+ attr_reader :content_type
31
+
32
+ # The Nebulous Protocol
33
+ # Note that if you happen to pass an array as @params, it's actually
34
+ # writable, which is not ideal.
35
+ attr_reader :verb, :params, :desc
36
+ attr_reader :reply_to, :in_reply_to
37
+
38
+
39
+ class << self
40
+
41
+
42
+ ##
43
+ # Build a Message from its components.
44
+ #
45
+ # Note that we assume a content type of JSON. But if we are building a
46
+ # message by hand in Ruby, this is only reasonable.
47
+ #
48
+ # You must pass a verb; you can pass nil for all the other values.
49
+ #
50
+ # * replyTo - the queue to reply to if this is a Request
51
+ # * inReplyTo - the reply ID if this is a Response
52
+ # * verb, params, desc - The Protocol; the message to pass
53
+ #
54
+ def from_parts(replyTo, inReplyTo, verb, params, desc)
55
+ raise ArgumentError, "missing parts" unless verb
56
+
57
+ Nebulous.logger.debug(__FILE__){ "New message from parts" }
58
+
59
+ p =
60
+ case params
61
+ when NilClass then nil
62
+ when Array then params.dup
63
+ else params.to_s
64
+ end
65
+
66
+ self.new( replyTo: replyTo.to_s,
67
+ inReplyTo: inReplyTo.to_s,
68
+ verb: verb.to_s,
69
+ params: p,
70
+ desc: desc.nil? ? nil : desc.to_s,
71
+ contentType: 'application/json' )
72
+
73
+ end
74
+
75
+
76
+ ##
77
+ # Build a Message that replies to an existing Message
78
+ #
79
+ # * msg - the Nebulous::Message that you are replying to
80
+ # * verb, params, desc - the new message Protocol
81
+ #
82
+ def in_reply_to(msg, verb, params=nil, desc=nil, replyTo=nil)
83
+ raise ArgumentError, 'bad message' unless msg.kind_of? Message
84
+
85
+ Nebulous.logger.debug(__FILE__){ "New message reply" }
86
+
87
+ p =
88
+ case params
89
+ when NilClass then nil
90
+ when Array then params.dup
91
+ else params.to_s
92
+ end
93
+
94
+ m = msg.clone
95
+ self.new( replyTo: replyTo.to_s,
96
+ verb: verb.to_s,
97
+ params: p,
98
+ desc: desc.to_s,
99
+ inReplyTo: m.reply_id,
100
+ contentType: m.content_type )
101
+
102
+ end
103
+
104
+
105
+ ##
106
+ # Build a Message from a (presumably incoming) STOMP message
107
+ #
108
+ def from_stomp(stompMsg)
109
+ raise ArgumentError, 'not a stomp message' \
110
+ unless stompMsg.kind_of? Stomp::Message
111
+
112
+ Nebulous.logger.debug(__FILE__){ "New message from STOMP" }
113
+
114
+ s = Marshal.load( Marshal.dump(stompMsg) )
115
+ obj = self.new( stompHeaders: s.headers,
116
+ stompBody: s.body )
117
+
118
+ end
119
+
120
+
121
+ ##
122
+ # To build a Nebmessage from a record in the Redis cache
123
+ #
124
+ # See #to_cache for details of the hash that Redis should be storing
125
+ #
126
+ def from_cache(json)
127
+ raise ArgumentError, "That can't be JSON, it's not a string" \
128
+ unless json.kind_of? String
129
+
130
+ Nebulous.logger.debug(__FILE__){ "New message from cache" }
131
+
132
+ # Note that the message body at this point, for a JSON message, is
133
+ # actually encoded to JSON *twice* - the second time was when the cache
134
+ # hash as a whole was encoded for store in Redis. the JSON gem copes
135
+ # with this so long as the whole string is not double-encoded.
136
+ hash = JSON.parse(json, :symbolize_names => true)
137
+ raise ArgumentError, 'Empty cache entry' if hash == {}
138
+
139
+ # So now if the content type is JSON then the body is still JSON now.
140
+ # It's only the rest of the cache hash that is a now a hash. Confused?
141
+ # Now join us for this weeks' episode...
142
+ self.new( hash.clone )
143
+
144
+ rescue JSON::ParserError => err
145
+ raise ArgumentError, "Bad JSON: #{err.message}"
146
+ end
147
+
148
+ end
149
+ ##
150
+
151
+
152
+ alias :parameters :params
153
+ alias :description :desc
154
+
155
+
156
+ def to_s
157
+ "<Message[#{@reply_id}] to:#{@reply_to} r-to:#{@in_reply_to} " \
158
+ << "v:#{@verb}>"
159
+
160
+ end
161
+
162
+
163
+ ##
164
+ # true if the content type appears to be JSON-y
165
+ #
166
+ def content_is_json?
167
+ @content_type =~ /json$/i ? true : false
168
+ end
169
+
170
+
171
+ ##
172
+ # Output a hash for serialization to the cache.
173
+ #
174
+ # Currently this looks like:
175
+ # { stompHeaders: @stomp_headers,
176
+ # stompBody: @stomp_body,
177
+ # verb: @verb,
178
+ # params: @params,
179
+ # desc: @desc,
180
+ # replyTo: @reply_to,
181
+ # replyId: @reply_id,
182
+ # inReplyTo: @in_reply_to,
183
+ # contentType: @content_type }
184
+ #
185
+ def to_cache
186
+ { stompHeaders: @stomp_headers,
187
+ stompBody: @stomp_body,
188
+ verb: @verb,
189
+ params: @params.kind_of?(Enumerable) ? @params.dup : @params,
190
+ desc: @desc,
191
+ replyTo: @reply_to,
192
+ replyId: @reply_id,
193
+ inReplyTo: @in_reply_to,
194
+ contentType: @content_type }
195
+
196
+ end
197
+
198
+
199
+ ##
200
+ # Return the hash of additional headers for the Stomp gem
201
+ #
202
+ def headers_for_stomp
203
+ headers = { "content-type" => @content_type,
204
+ "neb-reply-id" => @reply_id }
205
+
206
+ headers["neb-reply-to"] = @reply_to if @reply_to
207
+ headers["neb-in-reply-to"] = @in_reply_to if @in_reply_to
208
+
209
+ headers
210
+ end
211
+
212
+
213
+ ##
214
+ # Return a body object for the Stomp gem
215
+ #
216
+ def body_for_stomp
217
+ hash = protocol_hash
218
+
219
+ if content_is_json?
220
+ hash.to_json
221
+ else
222
+ hash.map {|k,v| "#{k}: #{v}" }.join("\n") << "\n\n"
223
+ end
224
+ end
225
+
226
+
227
+ ##
228
+ # :call-seq:
229
+ # message.body_to_h -> (Hash || nil)
230
+ #
231
+ # If the body is in JSON, return a hash.
232
+ # If body is nil, or is not JSON, then return nil; don't raise an exception
233
+ #
234
+ def body_to_h
235
+ x = StompHandler.body_to_hash(stomp_headers, stomp_body, @content_type)
236
+ x == {} ? nil : x
237
+ end
238
+
239
+
240
+ ##
241
+ # Return the message body formatted for The Protocol, in JSON.
242
+ #
243
+ # Raise an exception if the message body doesn't follow the protocol.
244
+ #
245
+ # (We use this as the key for the Redis cache)
246
+ #
247
+ def protocol_json
248
+ raise NebulousError, "no protocol in this message!" unless @verb
249
+ protocol_hash.to_json
250
+ end
251
+
252
+
253
+ ##
254
+ # Make a new 'success verb' message in response to this one
255
+ #
256
+ # returns [queue, message] so you can just pass it to
257
+ # stomphandler.send_message.
258
+ #
259
+ def respond_success
260
+ raise NebulousError, "Don''t know who to reply to" unless @reply_to
261
+
262
+ Nebulous.logger.info(__FILE__) do
263
+ "Responded to #{self} with 'success' verb"
264
+ end
265
+
266
+ [ @reply_to, Message.in_reply_to(self, 'success') ]
267
+ end
268
+
269
+
270
+ ##
271
+ # Make a new 'error verb' message in response to this one
272
+ #
273
+ # err can be a string or an exception
274
+ #
275
+ # returns [queue, message] so you can just pass it to
276
+ # stomphandler.send_message.
277
+ #
278
+ def respond_error(err,fields=[])
279
+ raise NebulousError, "Don''t know who to reply to" unless @reply_to
280
+
281
+ Nebulous.logger.info(__FILE__) do
282
+ "Responded to #{self} with 'error': #{err}"
283
+ end
284
+
285
+ reply = Message.in_reply_to(self, 'error', fields, err.to_s)
286
+
287
+ [ @reply_to, reply ]
288
+ end
289
+
290
+
291
+ private
292
+
293
+
294
+ ##
295
+ # Create a record -- note that you can't call this directly on the class;
296
+ # you have to call Message.from_parts, .from_stomp, .from_cache or
297
+ # .in_reply_to.
298
+ #
299
+ def initialize(hash)
300
+ @stomp_headers = hash[:stompHeaders]
301
+ @stomp_body = hash[:stompBody]
302
+
303
+ @verb = hash[:verb]
304
+ @params = hash[:params]
305
+ @desc = hash[:desc]
306
+ @reply_to = hash[:replyTo]
307
+ @reply_id = hash[:replyId]
308
+ @in_reply_to = hash[:inReplyTo]
309
+ @content_type = hash[:contentType]
310
+
311
+ fill_from_message
312
+ end
313
+
314
+
315
+ ##
316
+ # Return The Protocol of the message as a hash.
317
+ #
318
+ def protocol_hash
319
+ h = {verb: @verb}
320
+ h[:parameters] = @params unless @params.nil?
321
+ h[:description] = @desc unless @desc.nil?
322
+
323
+ h
324
+ end
325
+
326
+
327
+ ##
328
+ # Fill all the other attributes, if you can, from @stomp_headers and
329
+ # @stomp_body.
330
+ #
331
+ def fill_from_message
332
+
333
+ if @stomp_headers
334
+ @content_type ||= @stomp_headers['content-type']
335
+ @reply_id ||= @stomp_headers['neb-reply-id']
336
+ @reply_to ||= @stomp_headers['neb-reply-to']
337
+ @in_reply_to ||= @stomp_headers['neb-in-reply-to']
338
+ end
339
+
340
+ # decode the body, which should either be a JSON string or a series of
341
+ # text fields. And use the body to set Protocol attributes.
342
+ if @stomp_body && !@stomp_body.empty?
343
+
344
+ raise "body is not a string, something is very wrong here!" \
345
+ unless @stomp_body.kind_of? String
346
+
347
+ h = StompHandler.body_to_hash( @stomp_headers,
348
+ @stomp_body,
349
+ @content_type )
350
+
351
+ @verb ||= h["verb"]
352
+ @params ||= h["parameters"] || h["params"]
353
+ @desc ||= h["description"] || h["desc"]
354
+
355
+ # Assume that if verb is missing, the other two are just part of a
356
+ # response which has nothing to do with the protocol
357
+ @params = @desc = nil unless @verb
358
+ end
359
+
360
+ self
361
+ end
362
+
363
+ end
364
+ ##
365
+
366
+
367
+ end
368
+