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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 067d5122d7e3b2c0330f38dcd1984955d941c62a
4
- data.tar.gz: 044dfe46f63a3c7cc1ef0b49554c83d66c8485d7
3
+ metadata.gz: 2b9cbc03efff0d5b151b06fe26ae636a3bd2b051
4
+ data.tar.gz: 080f28bded46f251eaf25d259f3628c6142759bc
5
5
  SHA512:
6
- metadata.gz: a0f628d0a1c540f36c9c05d2cbb4327aff13d3e878e9bbf571bf55352a0198b99b6468a69a9781eaa18003554424b6a04aa8e82f4a3526b7fc96ad98bd8936a0
7
- data.tar.gz: 625e8ac7931c3c69995bd3d565da318327b4b1e2b3f3b73355caf769c78049ffa30d7f95bce7a8de57d66f0f588bda591ed4b202676f800c3815bea9c12950ee
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?
@@ -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
- PubControlClient.pubcall(uri, auth, [export])
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
- def self.pubcall(uri, auth_header, items)
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 = Net::HTTP.start(uri.host, uri.port, use_ssl: use_ssl) do |http|
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
- def self.pubbatch(reqs)
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
- PubControlClient.pubcall(uri, auth_header, items)
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
- def self.timestamp_utcnow
125
- return Time.now.utc.to_i
126
- end
127
-
128
- private
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
- PubControlClient.pubbatch(reqs)
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
- '#{@auth_basic_user}:#{@auth_basic_pass}')
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.2
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-02-01 00:00:00.000000000 Z
11
+ date: 2015-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: algorithms