lowdown 0.3.0 → 0.3.1

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: ab366da937c62aeebaaa7070b47f3f3a108e1460
4
- data.tar.gz: 568554722d19bf9899cfdfda1fa96de1698365ce
3
+ metadata.gz: 658a05f8102498326e20e21fff2c403ff597bbf7
4
+ data.tar.gz: 2274644492c37bdfe02681faccf3128b6297b0d3
5
5
  SHA512:
6
- metadata.gz: 90a597f2d9faae4afe0a4640b3618303b8740de0132fbfd5d9b291526ef3a240b0bbbe35208279197811a021305cb0edff7408c4d96e356a14a7a3a686b4db01
7
- data.tar.gz: 81fe7bf93ff1c4fa94090720c32293155a08b59fe2b6e6e0b5ca9dce7feac5d322d0f6c260a9ba68a8476443656710882dd33610b824a21601131cc9db96abba
6
+ metadata.gz: 610fe096be572adaaf73453bb1a12fcbd6b4b64da59a0110fbe876f4a4d18e10174cc9ecc2fd50912577a0b98cf6c94cf241b605acbbf11e100ed89183bcaf8a
7
+ data.tar.gz: 53b1305c9bccf953d3cfe174cd785f77a3000b3cc37e7a1c7a3c7bffd3b64d6c5e8318bf925c0610e4b62aab01ece0c43c1089c35b9955bc1ff9f614cc8444e0
@@ -110,6 +110,12 @@ Style/ParallelAssignment:
110
110
  Style/StringLiterals:
111
111
  EnforcedStyle: double_quotes
112
112
 
113
+ Style/StringLiteralsInInterpolation:
114
+ EnforcedStyle: double_quotes
115
+
116
+ Style/FormatString:
117
+ EnforcedStyle: percent
118
+
113
119
  Style/StructInheritance:
114
120
  Enabled: false
115
121
 
data/README.md CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
  [![Build Status](https://travis-ci.org/alloy/lowdown.svg?branch=master)](https://travis-ci.org/alloy/lowdown)
6
6
 
7
- NOTE: _This is not battle-tested yet. This will follow over the next few weeks._
7
+ ⚠︎ NOTE: _This is not battle-tested yet, which will follow over the next few weeks. A v1 will be released at that time._
8
8
 
9
9
  Lowdown is a Ruby client for the HTTP/2 version of the Apple Push Notification Service.
10
10
 
@@ -74,6 +74,8 @@ end
74
74
 
75
75
  ### Persistent connection
76
76
 
77
+ ⚠︎ NOTE: _See the ‘Gotchas’ section, specifically about process forking._
78
+
77
79
  The trick to creating a persistent connection is to specify the `keep_alive: true` option when creating the client:
78
80
 
79
81
  ```ruby
@@ -134,7 +136,7 @@ Keep in mind that, like with the block version, this message is sent on the grou
134
136
 
135
137
  While we’re on the topic of threading anyways, here’s an important thing to keep in mind; each set of `group` callbacks
136
138
  is performed on its own thread. It is thus _your_ responsibility to take this into account. E.g. if you are planning to
137
- update a DB model with the status of a notification delivery, be sure to respect the treading rules of your DB client,
139
+ update a DB model with the status of a notification delivery, be sure to respect the threading rules of your DB client,
138
140
  which usually means to not re-use models that were loaded on a different thread.
139
141
 
140
142
  A simple approach to this is by passing the data you need to be able to update the DB as a `context`, which can be any
@@ -161,6 +163,37 @@ increase this with the `pool_size` option:
161
163
  Lowdown::Client.production(true, File.read("path/to/certificate.pem"), pool_size: 3)
162
164
  ```
163
165
 
166
+ ## Gotchas
167
+
168
+ * If you’re forking your process, be sure to **not** load lowdown before forking (because this [does not work well with
169
+ Celluloid](https://github.com/celluloid/celluloid/wiki/Gotchas#do-not-create-actors-prior-to-forking-the-process), or
170
+ with threading and Ruby in general).
171
+
172
+ Forking is done by, e.g. [Spring][spring] and [DelayedJob][delayed_job], when daemonizing workers. In practice, this
173
+ means that e.g. you should not initialize a client from a Rails initializer, but rather do it lazily when it’s really
174
+ required. E.g.:
175
+
176
+ ```ruby
177
+ class PushNotificationService
178
+ def initialize(certificate_path)
179
+ @certificate_path = certificate_path
180
+ @client_mutex = Mutex.new
181
+ end
182
+
183
+ def client
184
+ client = nil
185
+ @client_mutex.synchronize do
186
+ @client ||= Lowdown::Client.production(true, File.read(certificate_path), keep_alive: true)
187
+ client = @client
188
+ end
189
+ client
190
+ end
191
+ end
192
+
193
+ # In your initializer:
194
+ PUSH_NOTIFICATION_SERVICE = PushNotificationService.new("path/to/certificate.pem")
195
+ ```
196
+
164
197
  ## Related tool ☞
165
198
 
166
199
  Also checkout [this library](https://github.com/alloy/time_zone_scheduler) for scheduling across time zones.
@@ -177,3 +210,6 @@ The gem is available as open source under the terms of the [MIT License](http://
177
210
  [notification]: http://www.rubydoc.info/gems/lowdown/Lowdown/Notification
178
211
  [group]: http://www.rubydoc.info/gems/lowdown/Lowdown/RequestGroup
179
212
  [delegate]: http://www.rubydoc.info/gems/lowdown/Lowdown/Connection/DelegateProtocol
213
+
214
+ [spring]: https://github.com/rails/spring
215
+ [delayed_job]: https://github.com/collectiveidea/delayed_job
@@ -44,9 +44,6 @@ loop do
44
44
  end
45
45
  end.each(&:join)
46
46
 
47
- logger.info "Sleep for 5 seconds"
48
- sleep(5)
49
-
50
47
  rescue Interrupt
51
48
  logger.info "[!] Interrupt, exiting"
52
49
  break
@@ -54,8 +51,11 @@ loop do
54
51
  rescue Exception => e
55
52
  logger.error "[!] Exception occurred, re-trying in 1 second: #{e.inspect}\n\t#{e.backtrace.join("\n\t")}"
56
53
  sleep 1
57
- redo
58
54
  end
55
+
56
+ GC.start
57
+ logger.info "Sleep for 5 seconds"
58
+ sleep(5)
59
59
  end
60
60
 
61
61
  client.disconnect
@@ -11,27 +11,37 @@ production = environment == "production"
11
11
 
12
12
  # $CELLULOID_DEBUG = true
13
13
  # Celluloid.logger.level = Logger::DEBUG
14
- # Celluloid.logger.level = Logger::INFO
15
- Celluloid.logger.level = Logger::ERROR
14
+ Celluloid.logger.level = Logger::INFO
15
+ # Celluloid.logger.level = Logger::ERROR
16
16
 
17
- # Connection time can take a while, just count the time it takes to connect.
18
- start = nil
17
+ client = Lowdown::Client.production(production, certificate: File.read(cert_file), pool_size: 2)
19
18
 
20
19
  # The block form of Client#connect flushes and closes the connection at the end of the block.
21
- Lowdown::Client.production(production, certificate: File.read(cert_file), pool_size: 2).connect do |group|
22
- start = Time.now
23
- 600.times do
24
- notification = Lowdown::Notification.new(:token => device_token)
25
- notification.payload = { :alert => "Hello HTTP/2! ID=#{notification.id}" }
26
- group.send_notification(notification) do |response|
27
- if response.success?
28
- puts "Sent notification with ID: #{notification.id}"
29
- else
30
- puts "[!] (##{response.id}): #{response}"
20
+ loop do
21
+ begin
22
+ client.connect do |group|
23
+ 600.times do
24
+ notification = Lowdown::Notification.new(:token => device_token)
25
+ notification.payload = { :alert => "Hello HTTP/2! ID=#{notification.id}" }
26
+ group.send_notification(notification) do |response|
27
+ if response.success?
28
+ puts "Sent notification with ID: #{notification.id}"
29
+ else
30
+ puts "[!] (##{response.id}): #{response}"
31
+ end
32
+ end
31
33
  end
32
34
  end
35
+ rescue Interrupt
36
+ puts "[!] Interrupt, exiting"
37
+ break
38
+
39
+ rescue Exception => e
40
+ puts "[!] Error occurred: #{e.message}"
33
41
  end
34
- end
35
42
 
36
- puts "Finished in #{Time.now - start} seconds"
43
+ GC.start
44
+ puts "Sleep for 5 seconds"
45
+ sleep(5)
46
+ end
37
47
 
@@ -181,9 +181,13 @@ module Lowdown
181
181
  # @return [void]
182
182
  #
183
183
  def disconnect
184
- @connection.disconnect
185
- rescue Celluloid::DeadActorError
186
- # Rescue this exception instead of calling #alive? as that only works on an actor, not a pool.
184
+ if @connection.respond_to?(:actors)
185
+ @connection.actors.each do |connection|
186
+ connection.async.disconnect if connection.alive?
187
+ end
188
+ else
189
+ @connection.async.disconnect if @connection.alive?
190
+ end
187
191
  end
188
192
 
189
193
  # Use this to group a batch of requests and halt the caller thread until all of the requests in the group have been
@@ -63,9 +63,15 @@ module Lowdown
63
63
  HEARTBEAT_TIMEOUT = CONNECT_TIMEOUT
64
64
 
65
65
  include Celluloid::IO
66
- include Celluloid::Internals::Logger
67
66
  finalizer :disconnect
68
67
 
68
+ include Celluloid::Internals::Logger
69
+ %w(debug info warn error fatal unknown).each do |level|
70
+ define_method(level) do |message|
71
+ super("[APNS Connection##{"0x%x" % (object_id << 1)}] #{message}")
72
+ end
73
+ end
74
+
69
75
  # @param [URI, String] uri
70
76
  # the details to connect to the APN service.
71
77
  #
@@ -109,7 +115,10 @@ module Lowdown
109
115
  # @return [void]
110
116
  #
111
117
  def disconnect
112
- @connection.close if @connection
118
+ if @connection
119
+ info "Closing..."
120
+ @connection.close
121
+ end
113
122
  @heartbeat.cancel if @heartbeat
114
123
  reset_state!
115
124
  end
@@ -146,7 +155,7 @@ module Lowdown
146
155
  return if @connection
147
156
  @connecting = true
148
157
 
149
- info "Opening APNS connection."
158
+ info "Connecting..."
150
159
 
151
160
  # Celluloid::IO::DNSResolver bug. In case there is no connection at all:
152
161
  # 1. This results in `nil`:
@@ -232,17 +241,18 @@ module Lowdown
232
241
  @max_stream_count = @http.remote_settings[:settings_max_concurrent_streams]
233
242
  @connected = true
234
243
 
235
- debug "APNS connection established. Maximum number of concurrent streams: #{@max_stream_count}. " \
236
- "Flushing #{@request_queue.size} enqueued requests."
244
+ info "Connection established."
245
+ debug "Maximum number of streams: #{@max_stream_count}. Flushing #{@request_queue.size} enqueued requests."
237
246
 
238
247
  @request_queue.size.times do
239
248
  async.try_to_perform_request!
240
249
  end
241
250
 
242
251
  @heartbeat = every(HEARTBEAT_INTERVAL) do
243
- debug "Sending heartbeat ping"
252
+ debug "Sending heartbeat ping..."
244
253
  begin
245
254
  future.ping.call(HEARTBEAT_TIMEOUT)
255
+ debug "Got heartbeat reply."
246
256
  rescue Celluloid::TimedOut
247
257
  raise TimedOut, "Heartbeat ping timed-out."
248
258
  end
@@ -316,17 +326,22 @@ module Lowdown
316
326
 
317
327
  def try_to_perform_request!
318
328
  unless @connected
319
- debug "Defer performing request, because the connection has not been established yet"
329
+ unless @warned_about_not_connected
330
+ warn "Defer performing request, because the connection has not been established yet."
331
+ @warned_about_not_connected = true
332
+ end
320
333
  return
334
+ else
335
+ @warned_about_not_connected = false
321
336
  end
322
337
 
323
338
  unless @http.active_stream_count < @max_stream_count
324
- debug "Defer performing request, because the maximum concurren stream count has been reached"
339
+ debug "Defer performing request, because the maximum concurren stream count has been reached."
325
340
  return
326
341
  end
327
342
 
328
343
  unless request = @request_queue.shift
329
- debug "Defer performing request, because the request queue is empty"
344
+ debug "Defer performing request, because the request queue is empty."
330
345
  return
331
346
  end
332
347
 
@@ -144,6 +144,10 @@ module Lowdown
144
144
  self
145
145
  end
146
146
 
147
+ def alive?
148
+ true
149
+ end
150
+
147
151
  # @!group Real API: Instance Method Summary
148
152
 
149
153
  # Yields stubbed {#responses} or if none are available defaults to success responses. It does this on a different
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Lowdown
4
- VERSION = "0.3.0".freeze
4
+ VERSION = "0.3.1".freeze
5
5
  end
6
6
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: lowdown
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eloy Durán
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-01-29 00:00:00.000000000 Z
11
+ date: 2016-02-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: http-2
@@ -132,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
132
132
  version: '0'
133
133
  requirements: []
134
134
  rubyforge_project:
135
- rubygems_version: 2.5.1
135
+ rubygems_version: 2.4.5.1
136
136
  signing_key:
137
137
  specification_version: 4
138
138
  summary: A Ruby client for the HTTP/2 version of the Apple Push Notification Service.