pubcontrol 1.0.2 → 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/format.rb +8 -0
- data/lib/item.rb +21 -0
- data/lib/pcccbhandler.rb +19 -0
- data/lib/pubcontrol.rb +26 -0
- data/lib/pubcontrolclient.rb +70 -15
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2b9cbc03efff0d5b151b06fe26ae636a3bd2b051
|
4
|
+
data.tar.gz: 080f28bded46f251eaf25d259f3628c6142759bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 35d714d4f918aadfd8ce7b59be9f66a98e1a1a85344e284e55185db13ea2330063c31af682fcb67b672a4ddc56d19433b3e8b734d0ec4373170551a813d01334
|
7
|
+
data.tar.gz: cb0649c9d4ad939ab10daedcf68783a0f0e1d08f0d977846ae01a55cd8169511ab183d22a18d53ce110a69b8dc72e697fc6f642e77dbd0d2d718985725a5573d
|
data/lib/format.rb
CHANGED
@@ -5,11 +5,19 @@
|
|
5
5
|
# :copyright: (c) 2015 by Fanout, Inc.
|
6
6
|
# :license: MIT, see LICENSE for more details.
|
7
7
|
|
8
|
+
# The Format class is provided as a base class for all publishing
|
9
|
+
# formats that are included in the Item class. Examples of format
|
10
|
+
# implementations include JsonObjectFormat and HttpStreamFormat.
|
8
11
|
class Format
|
12
|
+
|
13
|
+
# The name of the format which should return a string. Examples
|
14
|
+
# include 'json-object' and 'http-response'
|
9
15
|
def name
|
10
16
|
raise NotImplementedError
|
11
17
|
end
|
12
18
|
|
19
|
+
# The export method which should return a format-specific hash
|
20
|
+
# containing the required format-specific data.
|
13
21
|
def export
|
14
22
|
raise NotImplementedError
|
15
23
|
end
|
data/lib/item.rb
CHANGED
@@ -7,7 +7,17 @@
|
|
7
7
|
|
8
8
|
require_relative 'format.rb'
|
9
9
|
|
10
|
+
# The Item class is a container used to contain one or more format
|
11
|
+
# implementation instances where each implementation instance is of a
|
12
|
+
# different type of format. An Item instance may not contain multiple
|
13
|
+
# implementations of the same type of format. An Item instance is then
|
14
|
+
# serialized into a hash that is used for publishing to clients.
|
10
15
|
class Item
|
16
|
+
|
17
|
+
# The initialize method can accept either a single Format implementation
|
18
|
+
# instance or an array of Format implementation instances. Optionally
|
19
|
+
# specify an ID and/or previous ID to be sent as part of the message
|
20
|
+
# published to the client.
|
11
21
|
def initialize(formats, id=nil, prev_id=nil)
|
12
22
|
@id = id
|
13
23
|
@prev_id = prev_id
|
@@ -17,7 +27,18 @@ class Item
|
|
17
27
|
@formats = formats
|
18
28
|
end
|
19
29
|
|
30
|
+
# The export method serializes all of the formats, ID, and previous ID
|
31
|
+
# into a hash that is used for publishing to clients. If more than one
|
32
|
+
# instance of the same type of Format implementation was specified then
|
33
|
+
# an error will be raised.
|
20
34
|
def export
|
35
|
+
format_types = []
|
36
|
+
@formats.each do |format|
|
37
|
+
if !format_types.index(format.class.name).nil?
|
38
|
+
raise 'more than one instance of ' + format.class.name + ' specified'
|
39
|
+
end
|
40
|
+
format_types.push(format.class.name)
|
41
|
+
end
|
21
42
|
out = Hash.new
|
22
43
|
if !@id.nil?
|
23
44
|
out['id'] = @id
|
data/lib/pcccbhandler.rb
CHANGED
@@ -5,7 +5,19 @@
|
|
5
5
|
# :copyright: (c) 2015 by Fanout, Inc.
|
6
6
|
# :license: MIT, see LICENSE for more details.
|
7
7
|
|
8
|
+
# The PubControlClientCallbackHandler class is used internally for allowing
|
9
|
+
# an async publish call made from the PubControl class to execute a callback
|
10
|
+
# method only a single time. A PubControl instance can potentially contain
|
11
|
+
# many PubControlClient instances in which case this class tracks the number
|
12
|
+
# of successful publishes relative to the total number of PubControlClient
|
13
|
+
# instances. A failure to publish in any of the PubControlClient instances
|
14
|
+
# will result in a failed result passed to the callback method and the error
|
15
|
+
# from the first encountered failure.
|
8
16
|
class PubControlClientCallbackHandler
|
17
|
+
|
18
|
+
# The initialize method accepts: a num_calls parameter which is an integer
|
19
|
+
# representing the number of PubControlClient instances, and a callback
|
20
|
+
# method to be executed after all publishing is complete.
|
9
21
|
def initialize(num_calls, callback)
|
10
22
|
@num_calls = num_calls
|
11
23
|
@callback = callback
|
@@ -13,6 +25,12 @@ class PubControlClientCallbackHandler
|
|
13
25
|
@first_error_message = nil
|
14
26
|
end
|
15
27
|
|
28
|
+
# The handler method which is executed by PubControlClient when publishing
|
29
|
+
# is complete. This method tracks the number of publishes performed and
|
30
|
+
# when all publishes are complete it will call the callback method
|
31
|
+
# originally specified by the consumer. If publishing failures are
|
32
|
+
# encountered only the first error is saved and reported to the callback
|
33
|
+
# method.
|
16
34
|
def handler(success, message)
|
17
35
|
if !success and @success
|
18
36
|
@success = false
|
@@ -24,6 +42,7 @@ class PubControlClientCallbackHandler
|
|
24
42
|
end
|
25
43
|
end
|
26
44
|
|
45
|
+
# This method is used as a workaround to retrieve the handler method symbol.
|
27
46
|
# TODO: how to get handler symbol without this method?
|
28
47
|
def handler_method_symbol
|
29
48
|
return method(:handler)
|
data/lib/pubcontrol.rb
CHANGED
@@ -10,7 +10,15 @@ require_relative 'item.rb'
|
|
10
10
|
require_relative 'pubcontrolclient.rb'
|
11
11
|
require_relative 'pcccbhandler.rb'
|
12
12
|
|
13
|
+
# The PubControl class allows a consumer to manage a set of publishing
|
14
|
+
# endpoints and to publish to all of those endpoints via a single publish
|
15
|
+
# or publish_async method call. A PubControl instance can be configured
|
16
|
+
# either using a hash or array of hashes containing configuration information
|
17
|
+
# or by manually adding PubControlClient instances.
|
13
18
|
class PubControl
|
19
|
+
|
20
|
+
# Initialize with or without a configuration. A configuration can be applied
|
21
|
+
# after initialization via the apply_config method.
|
14
22
|
def initialize(config=nil)
|
15
23
|
@clients = Array.new
|
16
24
|
if !config.nil?
|
@@ -18,14 +26,21 @@ class PubControl
|
|
18
26
|
end
|
19
27
|
end
|
20
28
|
|
29
|
+
# Remove all of the configured PubControlClient instances.
|
21
30
|
def remove_all_clients
|
22
31
|
@clients = Array.new
|
23
32
|
end
|
24
33
|
|
34
|
+
# Add the specified PubControlClient instance.
|
25
35
|
def add_client(client)
|
26
36
|
@clients.push(client)
|
27
37
|
end
|
28
38
|
|
39
|
+
# Apply the specified configuration to this PubControl instance. The
|
40
|
+
# configuration object can either be a hash or an array of hashes where
|
41
|
+
# each hash corresponds to a single PubControlClient instance. Each hash
|
42
|
+
# will be parsed and a PubControlClient will be created either using just
|
43
|
+
# a URI or a URI and JWT authentication information.
|
29
44
|
def apply_config(config)
|
30
45
|
if !config.is_a?(Array)
|
31
46
|
config = [config]
|
@@ -39,18 +54,29 @@ class PubControl
|
|
39
54
|
end
|
40
55
|
end
|
41
56
|
|
57
|
+
# The finish method is a blocking method that ensures that all asynchronous
|
58
|
+
# publishing is complete for all of the configured PubControlClient
|
59
|
+
# instances prior to returning and allowing the consumer to proceed.
|
42
60
|
def finish
|
43
61
|
@clients.each do |pub|
|
44
62
|
pub.finish
|
45
63
|
end
|
46
64
|
end
|
47
65
|
|
66
|
+
# The synchronous publish method for publishing the specified item to the
|
67
|
+
# specified channel for all of the configured PubControlClient instances.
|
48
68
|
def publish(channel, item)
|
49
69
|
@clients.each do |pub|
|
50
70
|
pub.publish(channel, item)
|
51
71
|
end
|
52
72
|
end
|
53
73
|
|
74
|
+
# The asynchronous publish method for publishing the specified item to the
|
75
|
+
# specified channel on the configured endpoint. The callback method is
|
76
|
+
# optional and will be passed the publishing results after publishing is
|
77
|
+
# complete. Note that a failure to publish in any of the configured
|
78
|
+
# PubControlClient instances will result in a failure result being passed
|
79
|
+
# to the callback method along with the first encountered error message.
|
54
80
|
def publish_async(channel, item, callback=nil)
|
55
81
|
cb = nil
|
56
82
|
if !callback.nil?
|
data/lib/pubcontrolclient.rb
CHANGED
@@ -13,9 +13,16 @@ require 'json'
|
|
13
13
|
require 'net/http'
|
14
14
|
require_relative 'item.rb'
|
15
15
|
|
16
|
+
# The PubControlClient class allows consumers to publish either synchronously
|
17
|
+
# or asynchronously to an endpoint of their choice. The consumer wraps a Format
|
18
|
+
# class instance in an Item class instance and passes that to the publish
|
19
|
+
# methods. The async publish method has an optional callback parameter that
|
20
|
+
# is called after the publishing is complete to notify the consumer of the
|
21
|
+
# result.
|
16
22
|
class PubControlClient
|
17
23
|
attr_accessor :req_queue
|
18
24
|
|
25
|
+
# Initialize this class with a URL representing the publishing endpoint.
|
19
26
|
def initialize(uri)
|
20
27
|
@uri = uri
|
21
28
|
@lock = Mutex.new
|
@@ -29,6 +36,8 @@ class PubControlClient
|
|
29
36
|
@auth_jwt_key = nil
|
30
37
|
end
|
31
38
|
|
39
|
+
# Call this method and pass a username and password to use basic
|
40
|
+
# authentication with the configured endpoint.
|
32
41
|
def set_auth_basic(username, password)
|
33
42
|
@lock.synchronize do
|
34
43
|
@auth_basic_user = username
|
@@ -36,6 +45,8 @@ class PubControlClient
|
|
36
45
|
end
|
37
46
|
end
|
38
47
|
|
48
|
+
# Call this method and pass a claim and key to use JWT authentication
|
49
|
+
# with the configured endpoint.
|
39
50
|
def set_auth_jwt(claim, key)
|
40
51
|
@lock.synchronize do
|
41
52
|
@auth_jwt_claim = claim
|
@@ -43,6 +54,8 @@ class PubControlClient
|
|
43
54
|
end
|
44
55
|
end
|
45
56
|
|
57
|
+
# The synchronous publish method for publishing the specified item to the
|
58
|
+
# specified channel on the configured endpoint.
|
46
59
|
def publish(channel, item)
|
47
60
|
export = item.export
|
48
61
|
export['channel'] = channel
|
@@ -52,9 +65,13 @@ class PubControlClient
|
|
52
65
|
uri = @uri
|
53
66
|
auth = gen_auth_header
|
54
67
|
end
|
55
|
-
|
68
|
+
pubcall(uri, auth, [export])
|
56
69
|
end
|
57
70
|
|
71
|
+
# The asynchronous publish method for publishing the specified item to the
|
72
|
+
# specified channel on the configured endpoint. The callback method is
|
73
|
+
# optional and will be passed the publishing results after publishing is
|
74
|
+
# complete.
|
58
75
|
def publish_async(channel, item, callback=nil)
|
59
76
|
export = item.export
|
60
77
|
export['channel'] = channel
|
@@ -68,6 +85,9 @@ class PubControlClient
|
|
68
85
|
queue_req(['pub', uri, auth, export, callback])
|
69
86
|
end
|
70
87
|
|
88
|
+
# The finish method is a blocking method that ensures that all asynchronous
|
89
|
+
# publishing is complete prior to returning and allowing the consumer to
|
90
|
+
# proceed.
|
71
91
|
def finish
|
72
92
|
@lock.synchronize do
|
73
93
|
if !@thread.nil?
|
@@ -78,7 +98,17 @@ class PubControlClient
|
|
78
98
|
end
|
79
99
|
end
|
80
100
|
|
81
|
-
|
101
|
+
# A helper method for returning the current UNIX UTC timestamp.
|
102
|
+
def self.timestamp_utcnow
|
103
|
+
return Time.now.utc.to_i
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
# An internal method for preparing the HTTP POST request for publishing
|
109
|
+
# data to the endpoint. This method accepts the URI endpoint, authorization
|
110
|
+
# header, and a list of items to publish.
|
111
|
+
def pubcall(uri, auth_header, items)
|
82
112
|
uri = URI(uri + '/publish/')
|
83
113
|
content = Hash.new
|
84
114
|
content['items'] = items
|
@@ -89,16 +119,30 @@ class PubControlClient
|
|
89
119
|
end
|
90
120
|
request['Content-Type'] = 'application/json'
|
91
121
|
use_ssl = uri.scheme == 'https'
|
92
|
-
response =
|
93
|
-
http.request(request)
|
94
|
-
end
|
122
|
+
response = make_http_request(uri, use_ssl, request)
|
95
123
|
if !response.kind_of? Net::HTTPSuccess
|
96
124
|
raise 'failed to publish: ' + response.class.to_s + ' ' +
|
97
125
|
response.message
|
98
126
|
end
|
99
127
|
end
|
100
128
|
|
101
|
-
|
129
|
+
# An internal method for making the specified HTTP request to the
|
130
|
+
# specified URI with an option that determines whether to use
|
131
|
+
# SSL.
|
132
|
+
def make_http_request(uri, use_ssl, request)
|
133
|
+
response = Net::HTTP.start(uri.host, uri.port, use_ssl: use_ssl) do |http|
|
134
|
+
http.request(request)
|
135
|
+
end
|
136
|
+
return response
|
137
|
+
end
|
138
|
+
|
139
|
+
# An internal method for publishing a batch of requests. The requests are
|
140
|
+
# parsed for the URI, authorization header, and each request is published
|
141
|
+
# to the endpoint. After all publishing is complete, each callback
|
142
|
+
# corresponding to each request is called (if a callback was originally
|
143
|
+
# provided for that request) and passed a result indicating whether that
|
144
|
+
# request was successfully published.
|
145
|
+
def pubbatch(reqs)
|
102
146
|
raise 'reqs length == 0' unless reqs.length > 0
|
103
147
|
uri = reqs[0][0]
|
104
148
|
auth_header = reqs[0][1]
|
@@ -109,7 +153,7 @@ class PubControlClient
|
|
109
153
|
callbacks.push(req[3])
|
110
154
|
end
|
111
155
|
begin
|
112
|
-
|
156
|
+
pubcall(uri, auth_header, items)
|
113
157
|
result = [true, '']
|
114
158
|
rescue => e
|
115
159
|
result = [false, e.message]
|
@@ -121,12 +165,11 @@ class PubControlClient
|
|
121
165
|
end
|
122
166
|
end
|
123
167
|
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
168
|
+
# An internal method that is meant to run as a separate thread and process
|
169
|
+
# asynchronous publishing requests. The method runs continously and
|
170
|
+
# publishes requests in batches containing a maximum of 10 requests. The
|
171
|
+
# method completes and the thread is terminated only when a 'stop' command
|
172
|
+
# is provided in the request queue.
|
130
173
|
def pubworker
|
131
174
|
quit = false
|
132
175
|
while !quit do
|
@@ -149,15 +192,19 @@ class PubControlClient
|
|
149
192
|
end
|
150
193
|
@thread_mutex.unlock
|
151
194
|
if reqs.length > 0
|
152
|
-
|
195
|
+
pubbatch(reqs)
|
153
196
|
end
|
154
197
|
end
|
155
198
|
end
|
156
199
|
|
200
|
+
# An internal method used to generate an authorization header. The
|
201
|
+
# authorization header is generated based on whether basic or JWT
|
202
|
+
# authorization information was provided via the publicly accessible
|
203
|
+
# 'set_*_auth' methods defined above.
|
157
204
|
def gen_auth_header
|
158
205
|
if !@auth_basic_user.nil?
|
159
206
|
return 'Basic ' + Base64.encode64(
|
160
|
-
|
207
|
+
"#{@auth_basic_user}:#{@auth_basic_pass}")
|
161
208
|
elsif !@auth_jwt_claim.nil?
|
162
209
|
if !@auth_jwt_claim.key?('exp')
|
163
210
|
claim = @auth_jwt_claim.clone
|
@@ -171,6 +218,10 @@ class PubControlClient
|
|
171
218
|
end
|
172
219
|
end
|
173
220
|
|
221
|
+
# An internal method that ensures that asynchronous publish calls are
|
222
|
+
# properly processed. This method initializes the required class fields,
|
223
|
+
# starts the pubworker worker thread, and is meant to execute only when
|
224
|
+
# the consumer makes an asynchronous publish call.
|
174
225
|
def ensure_thread
|
175
226
|
if @thread.nil?
|
176
227
|
@thread_cond = ConditionVariable.new
|
@@ -179,6 +230,10 @@ class PubControlClient
|
|
179
230
|
end
|
180
231
|
end
|
181
232
|
|
233
|
+
# An internal method for adding an asynchronous publish request to the
|
234
|
+
# publishing queue. This method will also activate the pubworker worker
|
235
|
+
# thread to make sure that it process any and all requests added to
|
236
|
+
# the queue.
|
182
237
|
def queue_req(req)
|
183
238
|
@thread_mutex.lock
|
184
239
|
@req_queue.push_back(req)
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pubcontrol
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Konstantin Bokarius
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-
|
11
|
+
date: 2015-03-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: algorithms
|