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 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