nebulous_stomp 2.0.2 → 3.0.0
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 +4 -4
- data/.hgignore +2 -0
- data/.hgtags +1 -0
- data/README.md +225 -28
- data/feature/connection_example.yaml +24 -0
- data/feature/feature_test_spec.rb +247 -0
- data/feature/gimme.rb +91 -0
- data/lib/nebulous_stomp/listener.rb +107 -0
- data/lib/nebulous_stomp/message.rb +132 -265
- data/lib/nebulous_stomp/msg/body.rb +169 -0
- data/lib/nebulous_stomp/msg/header.rb +98 -0
- data/lib/nebulous_stomp/param.rb +16 -35
- data/lib/nebulous_stomp/redis_handler.rb +19 -29
- data/lib/nebulous_stomp/redis_handler_null.rb +12 -11
- data/lib/nebulous_stomp/redis_helper.rb +110 -0
- data/lib/nebulous_stomp/request.rb +212 -0
- data/lib/nebulous_stomp/stomp_handler.rb +30 -96
- data/lib/nebulous_stomp/stomp_handler_null.rb +8 -22
- data/lib/nebulous_stomp/target.rb +52 -0
- data/lib/nebulous_stomp/version.rb +1 -1
- data/lib/nebulous_stomp.rb +63 -50
- data/md/LICENSE.txt +20 -2
- data/md/nebulous_protocol.md +25 -18
- data/spec/listener_spec.rb +104 -0
- data/spec/message_spec.rb +227 -116
- data/spec/nebulous_spec.rb +44 -9
- data/spec/param_spec.rb +16 -33
- data/spec/redis_handler_null_spec.rb +0 -2
- data/spec/redis_handler_spec.rb +0 -2
- data/spec/redis_helper_spec.rb +107 -0
- data/spec/request_spec.rb +249 -0
- data/spec/stomp_handler_null_spec.rb +33 -34
- data/spec/stomp_handler_spec.rb +1 -74
- data/spec/target_spec.rb +97 -0
- metadata +20 -11
- data/lib/nebulous_stomp/nebrequest.rb +0 -259
- data/lib/nebulous_stomp/nebrequest_null.rb +0 -37
- data/spec/nebrequest_null_spec.rb +0 -219
- data/spec/nebrequest_spec.rb +0 -239
- data/spec/through_test_spec.rb +0 -80
@@ -0,0 +1,169 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
require_relative '../stomp_handler'
|
4
|
+
|
5
|
+
|
6
|
+
module NebulousStomp
|
7
|
+
module Msg
|
8
|
+
|
9
|
+
|
10
|
+
##
|
11
|
+
# A class to encapsulate a Nebulous message body - helper class for Message
|
12
|
+
#
|
13
|
+
class Body
|
14
|
+
|
15
|
+
# Might be nil: only caught on messages that came directly from STOMP.
|
16
|
+
attr_reader :stomp_body
|
17
|
+
|
18
|
+
# The Nebulous Protocol
|
19
|
+
attr_reader :verb, :params, :desc
|
20
|
+
|
21
|
+
# Will be a hash for messages that follow The Protocol; anything at all otherwise.
|
22
|
+
attr_reader :body
|
23
|
+
|
24
|
+
##
|
25
|
+
# is_json should be a boolean, true if the body is JSON-encoded.
|
26
|
+
# If it is false then we assume that we are coded like STOMP headers, in lines of text.
|
27
|
+
#
|
28
|
+
def initialize(is_json, hash)
|
29
|
+
@is_json = !!is_json
|
30
|
+
@stomp_body = hash[:stompBody]
|
31
|
+
@body = hash[:body]
|
32
|
+
@verb = hash[:verb]
|
33
|
+
@params = hash[:parameters] || hash[:params]
|
34
|
+
@desc = hash[:description] || hash[:desc]
|
35
|
+
|
36
|
+
fill_from_stomp
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Output a the body part of the hash for serialization to the cache.
|
41
|
+
#
|
42
|
+
# Since the body could be quite large we only set :body if there is no @stomp_body. We
|
43
|
+
# recreate the one from the other anyway.
|
44
|
+
#
|
45
|
+
def to_h
|
46
|
+
{ stompBody: @stomp_body,
|
47
|
+
body: @stomp_body ? nil : body,
|
48
|
+
verb: @verb,
|
49
|
+
params: @params.kind_of?(Enumerable) ? @params.dup : @params,
|
50
|
+
desc: @desc }
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
##
|
55
|
+
# Return a body object for the Stomp gem
|
56
|
+
#
|
57
|
+
def body_for_stomp
|
58
|
+
|
59
|
+
case
|
60
|
+
when @is_json
|
61
|
+
@body.to_json
|
62
|
+
when @body.is_a?(Hash)
|
63
|
+
@body.map{|k,v| "#{k}: #{v}" }.join("\n") << "\n\n"
|
64
|
+
else
|
65
|
+
@body.to_s
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
##
|
71
|
+
# Return the message body formatted for The Protocol, in JSON.
|
72
|
+
#
|
73
|
+
# Raise an exception if the message body doesn't follow the protocol.
|
74
|
+
#
|
75
|
+
# (We use this as the key for the Redis cache)
|
76
|
+
#
|
77
|
+
def protocol_json
|
78
|
+
fail NebulousError, "no protocol in this message!" unless @verb
|
79
|
+
protocol_hash.to_json
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
|
84
|
+
##
|
85
|
+
# Return The Protocol of the message as a hash.
|
86
|
+
#
|
87
|
+
def protocol_hash
|
88
|
+
{ verb: @verb,
|
89
|
+
parameters: @params,
|
90
|
+
description: @desc }.delete_if{|_,v| v.nil? }
|
91
|
+
|
92
|
+
end
|
93
|
+
|
94
|
+
##
|
95
|
+
# Fill all the other attributes, if we can.
|
96
|
+
#
|
97
|
+
# Note that we confusingly store @verb, @params, & @desc (The Protocol, which if present is
|
98
|
+
# the message body); @body (the message body); and @stomp_body (the original body from the
|
99
|
+
# stomp message if we were created from one). Any of these could be passed in the initialize
|
100
|
+
# hash.
|
101
|
+
#
|
102
|
+
# The rule is that we always prioritize them in that order. If we are passed a @verb, then
|
103
|
+
# the Protocol fields go into @body; otherwise if set we take @body as it stands; otherwise
|
104
|
+
# we try and decode @stomp_body and set that in @body.
|
105
|
+
#
|
106
|
+
# NB: We never overwrite @stomp_body. If not passed to us, it stays nil. It's only stored in
|
107
|
+
# case we can't decode it, as a fallback.
|
108
|
+
#
|
109
|
+
def fill_from_stomp
|
110
|
+
|
111
|
+
if @verb
|
112
|
+
@body = protocol_hash
|
113
|
+
elsif @body.nil? || @body.respond_to?(:empty?) && @body.empty?
|
114
|
+
@body = parse_stomp_body
|
115
|
+
end
|
116
|
+
|
117
|
+
parse_body
|
118
|
+
|
119
|
+
# Assume that if verb is missing, the other two are just part of a
|
120
|
+
# response which has nothing to do with the protocol
|
121
|
+
@params = @desc = nil unless @verb
|
122
|
+
|
123
|
+
self
|
124
|
+
end
|
125
|
+
|
126
|
+
def parse_stomp_body
|
127
|
+
case
|
128
|
+
when @stomp_body.nil? then nil
|
129
|
+
when @is_json then stomp_body_from_json
|
130
|
+
else
|
131
|
+
stomp_body_from_text
|
132
|
+
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def stomp_body_from_json
|
137
|
+
JSON.parse(@stomp_body)
|
138
|
+
rescue JSON::ParserError, TypeError
|
139
|
+
# If we can't parse it, fine, take it as a text blob
|
140
|
+
@stomp_body.to_s
|
141
|
+
end
|
142
|
+
|
143
|
+
def stomp_body_from_text
|
144
|
+
lines = @stomp_body.to_s.split("\n").reject{|s| s =~ /^\s*$/ }
|
145
|
+
hash = {}
|
146
|
+
|
147
|
+
lines.each do |line|
|
148
|
+
k,v = line.split(':', 2).each{|x| x.strip! }
|
149
|
+
hash[k] = v if line.include?(':')
|
150
|
+
end
|
151
|
+
|
152
|
+
# If there are any lines we could not parse, forget the whole thing and return a text blob
|
153
|
+
(lines.size > hash.keys.size) ? @stomp_body.to_s : hash
|
154
|
+
end
|
155
|
+
|
156
|
+
def parse_body
|
157
|
+
if @body.is_a?(Hash)
|
158
|
+
@verb ||= @body["verb"]
|
159
|
+
@params ||= @body["parameters"] || @body["params"]
|
160
|
+
@desc ||= @body["description"] || @body["desc"]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
|
165
|
+
end # Message::Body
|
166
|
+
|
167
|
+
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
require_relative '../stomp_handler'
|
4
|
+
|
5
|
+
|
6
|
+
module NebulousStomp
|
7
|
+
module Msg
|
8
|
+
|
9
|
+
|
10
|
+
##
|
11
|
+
# A class to encapsulate a Nebulous message header - a helper class for Message
|
12
|
+
#
|
13
|
+
class Header
|
14
|
+
|
15
|
+
# Might be nil: parsed from incoming messages; set by StompHandler on send
|
16
|
+
attr_accessor :reply_id
|
17
|
+
|
18
|
+
# Might be nil: only caught on messages that came directly from STOMP
|
19
|
+
attr_reader :stomp_headers
|
20
|
+
|
21
|
+
# The content type of the message
|
22
|
+
attr_reader :content_type
|
23
|
+
|
24
|
+
# From The Nebulous Protocol
|
25
|
+
attr_reader :reply_to, :in_reply_to
|
26
|
+
|
27
|
+
##
|
28
|
+
#
|
29
|
+
def initialize(hash)
|
30
|
+
@stomp_headers = hash[:stompHeaders]
|
31
|
+
@reply_to = hash[:replyTo]
|
32
|
+
@reply_id = hash[:replyId]
|
33
|
+
@in_reply_to = hash[:inReplyTo]
|
34
|
+
@content_type = hash[:contentType]
|
35
|
+
|
36
|
+
# If we have no stomp headers then we (probably correctly) assume that this is a user
|
37
|
+
# created message, and default the content type to JSON.
|
38
|
+
@content_type = 'application/json' if @stomp_headers.nil? && @content_type.nil?
|
39
|
+
|
40
|
+
fill_from_stomp
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# true if the content type appears to be JSON-y
|
45
|
+
#
|
46
|
+
def content_is_json?
|
47
|
+
@content_type =~ /json$/i ? true : false
|
48
|
+
end
|
49
|
+
|
50
|
+
##
|
51
|
+
# Output a the header part of the hash for serialization to the cache.
|
52
|
+
#
|
53
|
+
def to_h
|
54
|
+
{ stompHeaders: @stomp_headers,
|
55
|
+
replyTo: @reply_to,
|
56
|
+
replyId: @reply_id,
|
57
|
+
inReplyTo: @in_reply_to,
|
58
|
+
contentType: @content_type }
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
##
|
63
|
+
# Return the hash of additional headers for the Stomp gem
|
64
|
+
#
|
65
|
+
def headers_for_stomp
|
66
|
+
{ "content-type" => @content_type,
|
67
|
+
"neb-reply-id" => @reply_id,
|
68
|
+
"neb-reply-to" => @reply_to,
|
69
|
+
"neb-in-reply-to" => @in_reply_to }
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
private
|
74
|
+
|
75
|
+
##
|
76
|
+
# Fill all the other attributes, if you can, from @stomp_headers
|
77
|
+
#
|
78
|
+
def fill_from_stomp
|
79
|
+
return unless @stomp_headers
|
80
|
+
|
81
|
+
type = @stomp_headers["content-type"] || @stomp_headers[:'content-type'] \
|
82
|
+
|| @stomp_headers["content_type"] || @stomp_headers[:content_type] \
|
83
|
+
|| @stomp_headers["contentType"] || @stomp_headers[:contentType]
|
84
|
+
|
85
|
+
@content_type ||= type
|
86
|
+
@reply_id ||= @stomp_headers['neb-reply-id']
|
87
|
+
@reply_to ||= @stomp_headers['neb-reply-to']
|
88
|
+
@in_reply_to ||= @stomp_headers['neb-in-reply-to']
|
89
|
+
|
90
|
+
self
|
91
|
+
end
|
92
|
+
|
93
|
+
end # of Header
|
94
|
+
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
data/lib/nebulous_stomp/param.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require_relative 'target'
|
2
|
+
|
3
|
+
|
1
4
|
module NebulousStomp
|
2
5
|
|
3
6
|
|
@@ -7,7 +10,6 @@ module NebulousStomp
|
|
7
10
|
module Param
|
8
11
|
extend self
|
9
12
|
|
10
|
-
|
11
13
|
# Default parameters hash
|
12
14
|
ParamDefaults = { stompConnectHash: {},
|
13
15
|
redisConnectHash: {},
|
@@ -21,66 +23,50 @@ module NebulousStomp
|
|
21
23
|
receiveQueue: nil,
|
22
24
|
messageTimeout: nil }
|
23
25
|
|
24
|
-
|
25
26
|
##
|
26
|
-
# Set the initial parameter string. This also has the effect of resetting
|
27
|
-
# everything.
|
27
|
+
# Set the initial parameter string. This also has the effect of resetting everything.
|
28
28
|
#
|
29
|
-
# Parameters default to Param::ParamDefaults. keys passed in parameter p to
|
30
|
-
#
|
29
|
+
# Parameters default to Param::ParamDefaults. keys passed in parameter p to override those
|
30
|
+
# defaults must match, or a NebulousError will result.
|
31
31
|
#
|
32
32
|
# This method is only called by Nebulous::init().
|
33
33
|
#
|
34
34
|
def set(p={})
|
35
|
-
|
35
|
+
fail NebulousError, "Invalid initialisation hash" unless p.kind_of?(Hash)
|
36
36
|
|
37
37
|
validate(ParamDefaults, p, "Unknown initialisation hash")
|
38
38
|
|
39
39
|
@params = ParamDefaults.merge(p)
|
40
40
|
end
|
41
41
|
|
42
|
-
|
43
42
|
##
|
44
43
|
# Add a Nebulous target. Raises NebulousError if anything looks screwy.
|
45
44
|
#
|
46
45
|
# Parameters:
|
47
|
-
#
|
48
|
-
# t -- hash, which must follow the pattern set by Param::TargetDefaults
|
46
|
+
# * t -- a Target
|
49
47
|
#
|
50
48
|
# Used only by Nebulous::init
|
51
49
|
#
|
52
|
-
def add_target(
|
53
|
-
|
54
|
-
|
55
|
-
validate(TargetDefaults, t, "Unknown target hash")
|
56
|
-
|
57
|
-
raise NebulousError, "Config Problem - Target missing 'send'" \
|
58
|
-
if t[:sendQueue].nil?
|
59
|
-
|
60
|
-
raise NebulousError, "Config Problem - Target missing 'receive'" \
|
61
|
-
if t[:receiveQueue].nil?
|
50
|
+
def add_target(t)
|
51
|
+
fail NebulousError, "Invalid target" unless t.kind_of?(Target)
|
62
52
|
|
63
53
|
@params ||= ParamDefaults
|
64
|
-
@params[:targets][
|
54
|
+
@params[:targets][t.name.to_sym] = t
|
65
55
|
end
|
66
56
|
|
67
|
-
|
68
|
-
|
69
57
|
##
|
70
58
|
# Set a logger instance
|
71
59
|
#
|
72
60
|
def set_logger(lg)
|
73
|
-
|
61
|
+
fail NebulousError unless lg.kind_of?(Logger) || lg.nil?
|
74
62
|
@logger = lg
|
75
63
|
end
|
76
64
|
|
77
|
-
|
78
65
|
##
|
79
66
|
# Get the logger instance
|
80
67
|
#
|
81
68
|
def get_logger; @logger; end
|
82
69
|
|
83
|
-
|
84
70
|
##
|
85
71
|
# Get the whole parameter hash. Probably only useful for testing.
|
86
72
|
#
|
@@ -88,7 +74,6 @@ module NebulousStomp
|
|
88
74
|
@params
|
89
75
|
end
|
90
76
|
|
91
|
-
|
92
77
|
##
|
93
78
|
# Get a the value of the parameter with the key p.
|
94
79
|
#
|
@@ -97,18 +82,15 @@ module NebulousStomp
|
|
97
82
|
@params[p.to_sym]
|
98
83
|
end
|
99
84
|
|
100
|
-
|
101
85
|
##
|
102
|
-
# Given a target name, return the corresponding
|
86
|
+
# Given a target name, return the corresponding Target object
|
103
87
|
#
|
104
88
|
def get_target(name)
|
105
89
|
t = Param.get(:targets)
|
106
|
-
|
107
|
-
|
108
|
-
raise NebulousError, "Config problem - unknown target #{name}" if x.nil?
|
109
|
-
return x
|
90
|
+
(t && t.kind_of?(Hash)) ? t[name.to_s.to_sym] : nil
|
110
91
|
end
|
111
92
|
|
93
|
+
private
|
112
94
|
|
113
95
|
##
|
114
96
|
# Raise an exception if a hash has any keys not found in an exemplar
|
@@ -117,11 +99,10 @@ module NebulousStomp
|
|
117
99
|
#
|
118
100
|
def validate(exemplar, hash, message)
|
119
101
|
hash.each_key do |k|
|
120
|
-
|
102
|
+
fail NebulousError, "#{message} key '#{k}'" unless exemplar.include?(k)
|
121
103
|
end
|
122
104
|
end
|
123
105
|
|
124
|
-
|
125
106
|
##
|
126
107
|
# reset all parameters -- probably only useful for testing
|
127
108
|
#
|
@@ -6,48 +6,44 @@ module NebulousStomp
|
|
6
6
|
|
7
7
|
##
|
8
8
|
# A class to deal with talking to Redis via the Redis gem
|
9
|
+
#
|
10
|
+
# You shouldn't need to instantiate this yourself. If you want to use NebulousStomp to talk to
|
11
|
+
# redis directly, try NebulousStomp::RedisHelper.
|
9
12
|
#
|
10
13
|
class RedisHandler
|
11
14
|
|
12
|
-
# This is most likely useless to anything except spec/redis_handler_spec --
|
13
|
-
#
|
15
|
+
# This is most likely useless to anything except spec/redis_handler_spec -- at least, I hope
|
16
|
+
# so...
|
14
17
|
attr_reader :redis
|
15
18
|
|
16
|
-
|
17
19
|
##
|
18
20
|
# Initialise an instance of the handler by passing it the connection hash
|
19
21
|
#
|
20
22
|
# If no hash is passed, we try and get it from Nebulous::Params
|
21
23
|
#
|
22
|
-
# We use the optional testRedis parameter to mock connections to Redis, for
|
23
|
-
#
|
24
|
+
# We use the optional testRedis parameter to mock connections to Redis, for testing. It's
|
25
|
+
# probably of no use to anyone else.
|
24
26
|
#
|
25
27
|
def initialize(connectHash=nil, testRedis=nil)
|
26
|
-
@redis_hash
|
27
|
-
@redis_hash ||= Param.get(:redisConnectHash)
|
28
|
-
|
28
|
+
@redis_hash = connectHash ? connectHash.dup : nil
|
29
29
|
@test_redis = testRedis
|
30
30
|
@redis = nil
|
31
31
|
end
|
32
32
|
|
33
|
-
|
34
33
|
##
|
35
|
-
# Connect to the Redis key/value store. Raise Nebulous error if connection
|
36
|
-
# fails.
|
34
|
+
# Connect to the Redis key/value store. Raise Nebulous error if connection fails.
|
37
35
|
#
|
38
36
|
def connect
|
39
37
|
@redis = @test_redis || Redis.new(@redis_hash.dup)
|
40
38
|
|
41
39
|
@redis.client.connect
|
42
|
-
|
40
|
+
fail ConnectionError, "Redis Connection failed" unless @redis.connected?
|
43
41
|
|
44
42
|
self
|
45
|
-
|
46
43
|
rescue => err
|
47
44
|
raise ConnectionError, err.to_s
|
48
45
|
end
|
49
46
|
|
50
|
-
|
51
47
|
##
|
52
48
|
# :call-seq:
|
53
49
|
# handler.connected? -> (boolean)
|
@@ -60,7 +56,6 @@ module NebulousStomp
|
|
60
56
|
self
|
61
57
|
end
|
62
58
|
|
63
|
-
|
64
59
|
##
|
65
60
|
# return whether we are connected to Redis.
|
66
61
|
#
|
@@ -68,38 +63,33 @@ module NebulousStomp
|
|
68
63
|
@redis && @redis.connected?
|
69
64
|
end
|
70
65
|
|
71
|
-
|
72
66
|
##
|
73
67
|
# :call-seq:
|
74
68
|
# handler.redis_on? -> (boolean)
|
75
69
|
#
|
76
|
-
# Return whether the Redis is turned "on" in the connect hash, the config
|
77
|
-
# file.
|
70
|
+
# Return whether the Redis is turned "on" in the connect hash, the config file.
|
78
71
|
#
|
79
|
-
# The rest of nebulous should just let RedisHandler worry about this
|
80
|
-
# detail.
|
72
|
+
# The rest of nebulous should just let RedisHandler worry about this detail.
|
81
73
|
#
|
82
74
|
def redis_on?
|
83
75
|
@redis_hash && !@redis_hash.empty?
|
84
76
|
end
|
85
77
|
|
86
|
-
|
87
|
-
|
88
78
|
##
|
89
|
-
# Cover all the other methods on @redis that we are basically forwarding to
|
90
|
-
#
|
91
|
-
#
|
79
|
+
# Cover all the other methods on @redis that we are basically forwarding to it. I could use
|
80
|
+
# Forwardable here -- except that would not allow us to raise Nebulous::ConnectionError if
|
81
|
+
# @redis.nil?
|
82
|
+
#
|
83
|
+
# Possible candidates for future inclusion: exists, expire, ping
|
92
84
|
#
|
93
85
|
def method_missing(meth, *args)
|
94
86
|
super unless [:set,:get,:del].include?(meth)
|
95
|
-
|
96
|
-
raise ConnectionError, "Redis not connected, sent #{meth}" \
|
97
|
-
unless connected?
|
87
|
+
fail ConnectionError, "Redis not connected, sent #{meth}" unless connected?
|
98
88
|
|
99
89
|
@redis.__send__(meth, *args)
|
100
90
|
end
|
101
91
|
|
102
|
-
end
|
92
|
+
end # RedisHandler
|
103
93
|
|
104
94
|
|
105
95
|
end
|
@@ -6,7 +6,6 @@ require_relative 'redis_handler'
|
|
6
6
|
|
7
7
|
module NebulousStomp
|
8
8
|
|
9
|
-
|
10
9
|
##
|
11
10
|
# Behaves just like RedisHandler, except, does nothing and expects no
|
12
11
|
# connection to Redis.
|
@@ -17,41 +16,43 @@ module NebulousStomp
|
|
17
16
|
|
18
17
|
attr_reader :fake_pair
|
19
18
|
|
20
|
-
|
21
19
|
def initialize(connectHash={})
|
22
20
|
super
|
23
21
|
@fake_pair = {}
|
24
22
|
end
|
25
23
|
|
26
|
-
|
27
24
|
def insert_fake(key, value)
|
28
25
|
@fake_pair = { key => value }
|
29
26
|
end
|
30
27
|
|
31
|
-
|
32
28
|
def connect
|
33
29
|
@redis = true
|
34
30
|
self
|
35
31
|
end
|
36
32
|
|
37
|
-
|
38
33
|
def quit
|
39
34
|
@redis = nil
|
40
35
|
self
|
41
36
|
end
|
42
|
-
|
43
37
|
|
44
38
|
def connected?
|
45
39
|
@fake_pair != {}
|
46
40
|
end
|
47
41
|
|
42
|
+
def set(key, value, hash=nil)
|
43
|
+
insert_fake(key, value)
|
44
|
+
"OK"
|
45
|
+
end
|
48
46
|
|
49
|
-
def
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
47
|
+
def del(key)
|
48
|
+
x = @fake_pair.empty? ? 0 : 1
|
49
|
+
@fake_pair = {}
|
50
|
+
x
|
51
|
+
end
|
54
52
|
|
53
|
+
def get(key)
|
54
|
+
@fake_pair.values.first
|
55
|
+
end
|
55
56
|
|
56
57
|
end
|
57
58
|
|
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
require_relative '../nebulous_stomp'
|
4
|
+
|
5
|
+
|
6
|
+
module NebulousStomp
|
7
|
+
|
8
|
+
|
9
|
+
##
|
10
|
+
# A class to help out users who want to talk to Redis themselves; the "Redis use case".
|
11
|
+
#
|
12
|
+
# redis = NebulousStomp::RedisHelper.new
|
13
|
+
#
|
14
|
+
# redis.set(:thing, "thingy")
|
15
|
+
# redes.set(:gone_in_30_seconds, "thingy", 30)
|
16
|
+
#
|
17
|
+
# value = redis.get(:thing)
|
18
|
+
#
|
19
|
+
# redis.del(:thing)
|
20
|
+
#
|
21
|
+
class RedisHelper
|
22
|
+
|
23
|
+
# For testing only
|
24
|
+
attr_writer :redis_handler
|
25
|
+
|
26
|
+
##
|
27
|
+
# :call-seq:
|
28
|
+
# RedisHelper.new -> (helper)
|
29
|
+
#
|
30
|
+
def initialize
|
31
|
+
@param_hash = Param.get(:redisConnectHash)
|
32
|
+
|
33
|
+
fail NebulousError, "NebulousStomp.init has not been called or Redis not configured" \
|
34
|
+
if @param_hash.nil? || @param_hash.empty?
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
##
|
39
|
+
# :call-seq:
|
40
|
+
# redis.set(key, value)
|
41
|
+
# redis.set(key, value, timeout)
|
42
|
+
#
|
43
|
+
# Set a value in the store.
|
44
|
+
#
|
45
|
+
def set(key, value, timeout=nil)
|
46
|
+
rtimeout = (Integer(timeout.to_s, 10) rescue nil)
|
47
|
+
rvalue = value_to_json(value)
|
48
|
+
fail ArgumentError, "Timeout must be a number" if timeout && rtimeout.nil?
|
49
|
+
ensure_connected
|
50
|
+
|
51
|
+
if timeout
|
52
|
+
redis_handler.set(key.to_s, rvalue, ex: rtimeout)
|
53
|
+
else
|
54
|
+
redis_handler.set(key.to_s, rvalue)
|
55
|
+
end
|
56
|
+
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# :call-seq:
|
62
|
+
# redis.get(key) -> value
|
63
|
+
#
|
64
|
+
# Get a string value from the store. Return nil if there is none.
|
65
|
+
#
|
66
|
+
def get(key)
|
67
|
+
ensure_connected
|
68
|
+
json_to_value(redis_handler.get key.to_s)
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# :call-seq:
|
73
|
+
# redis.del(key)
|
74
|
+
#
|
75
|
+
# Remove a value from the store. Raise an exception if there is none.
|
76
|
+
#
|
77
|
+
def del(key)
|
78
|
+
ensure_connected
|
79
|
+
num = redis_handler.del(key.to_s)
|
80
|
+
fail ArgumentError, "Unknown key, cannot delete" if num == 0
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
def redis_handler
|
86
|
+
@redis_handler ||= RedisHandler.new(Param.get :redisConnectHash)
|
87
|
+
end
|
88
|
+
|
89
|
+
def ensure_connected
|
90
|
+
redis_handler.connect unless redis_handler.connected?
|
91
|
+
end
|
92
|
+
|
93
|
+
def value_to_json(value)
|
94
|
+
{ value: value}.to_json
|
95
|
+
end
|
96
|
+
|
97
|
+
def json_to_value(json)
|
98
|
+
return nil if json.nil?
|
99
|
+
hash = JSON.parse(json, symbolize_names: true)
|
100
|
+
|
101
|
+
hash.is_a?(Hash) ? hash[:value] : nil
|
102
|
+
rescue JSON::ParserError
|
103
|
+
return nil
|
104
|
+
end
|
105
|
+
|
106
|
+
end # RedisHelper
|
107
|
+
|
108
|
+
|
109
|
+
end
|
110
|
+
|