pubcontrol 1.0.2 → 1.0.3
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/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
|