nebulous_stomp 1.1.5

Sign up to get free protection for your applications and to get access to all the features.
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
+