rapns_rails_2 3.4.3 → 3.5.0
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.
- data/CHANGELOG.md +2 -1
- data/README.md +1 -1
- data/config/database.yml +1 -1
- data/lib/generators/templates/rapns.rb +1 -1
- data/lib/rapns/apns/device_token_format_validator.rb +1 -1
- data/lib/rapns/configuration.rb +1 -1
- data/lib/rapns/daemon/feeder.rb +6 -6
- data/lib/rapns/daemon/gcm/delivery.rb +10 -3
- data/lib/rapns/notifier.rb +17 -0
- data/lib/rapns/version.rb +1 -1
- data/lib/rapns_rails_2.rb +1 -0
- data/spec/unit/apns/feedback_spec.rb +1 -1
- data/spec/unit/daemon/feeder_spec.rb +24 -16
- data/spec/unit/daemon/gcm/delivery_spec.rb +2 -2
- data/spec/unit/notifier_spec.rb +20 -0
- data/spec/unit_spec_helper.rb +1 -1
- metadata +67 -47
- checksums.yaml +0 -15
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -11,7 +11,7 @@
|
|
11
11
|
* Run as a daemon or inside an [existing processs](https://github.com/ileitch/rapns/wiki/Embedding-API).
|
12
12
|
* Use in a scheduler for low-workload deployments ([Push API](https://github.com/ileitch/rapns/wiki/Push-API)).
|
13
13
|
* Reflection API for fine-grained instrumentation and error handling ([Reflection API](https://github.com/ileitch/rapns/wiki/Reflection-API)).
|
14
|
-
* Works with MRI, JRuby, Rubinius 1.
|
14
|
+
* Works with MRI, JRuby, Rubinius 1.9, 2.0.
|
15
15
|
* Built with love.
|
16
16
|
|
17
17
|
### Who uses Rapns?
|
data/config/database.yml
CHANGED
@@ -71,7 +71,7 @@ Rapns.reflect do |on|
|
|
71
71
|
# on.gcm_canonical_id do |old_id, canonical_id|
|
72
72
|
# end
|
73
73
|
|
74
|
-
# Called when the GCM returns a failure that indicates an invalid
|
74
|
+
# Called when the GCM returns a failure that indicates an invalid registration id.
|
75
75
|
# You will need to delete the registration_id from your records.
|
76
76
|
# on.gcm_invalid_registration_id do |app, error, registration_id|
|
77
77
|
# end
|
data/lib/rapns/configuration.rb
CHANGED
@@ -9,7 +9,7 @@ module Rapns
|
|
9
9
|
|
10
10
|
CONFIG_ATTRS = [:foreground, :push_poll, :feedback_poll, :embedded,
|
11
11
|
:airbrake_notify, :check_for_errors, :pid_file, :batch_size,
|
12
|
-
:push, :store, :logger, :batch_storage_updates, :
|
12
|
+
:push, :store, :logger, :batch_storage_updates, :wakeup]
|
13
13
|
|
14
14
|
class ConfigurationWithoutDefaults < Struct.new(*CONFIG_ATTRS)
|
15
15
|
end
|
data/lib/rapns/daemon/feeder.rb
CHANGED
@@ -51,15 +51,15 @@ module Rapns
|
|
51
51
|
end
|
52
52
|
|
53
53
|
def self.interruptible_sleeper
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
54
|
+
return @interruptible_sleeper if @interruptible_sleeper
|
55
|
+
|
56
|
+
@interruptible_sleeper = InterruptibleSleep.new
|
57
|
+
if Rapns.config.wakeup
|
58
|
+
@interruptible_sleeper.enable_wake_on_udp Rapns.config.wakeup[:bind], Rapns.config.wakeup[:port]
|
59
59
|
end
|
60
|
+
|
60
61
|
@interruptible_sleeper
|
61
62
|
end
|
62
|
-
|
63
63
|
end
|
64
64
|
end
|
65
65
|
end
|
@@ -46,6 +46,9 @@ module Rapns
|
|
46
46
|
|
47
47
|
def ok(response)
|
48
48
|
body = multi_json_load(response.body)
|
49
|
+
|
50
|
+
handle_canonical_ids(response, body)
|
51
|
+
|
49
52
|
if body['failure'].to_i == 0
|
50
53
|
mark_delivered
|
51
54
|
Rapns.logger.info("[#{@app.name}] #{@notification.id} sent to #{@notification.registration_ids.join(', ')}")
|
@@ -54,7 +57,6 @@ module Rapns
|
|
54
57
|
handle_errors(response, body)
|
55
58
|
end
|
56
59
|
|
57
|
-
handle_canonical_ids(response, body)
|
58
60
|
end
|
59
61
|
|
60
62
|
def handle_errors(response, body)
|
@@ -63,9 +65,10 @@ module Rapns
|
|
63
65
|
body['results'].each_with_index do |result, i|
|
64
66
|
errors[i] = result['error'] if result['error'] && ! INVALID_REGISTRATION_ID_STATES.include?(result['error'])
|
65
67
|
end
|
66
|
-
return if errors.empty?
|
67
68
|
|
68
|
-
if
|
69
|
+
if errors.empty?
|
70
|
+
all_errors_were_invalid_registration_ids(response)
|
71
|
+
elsif body['success'].to_i == 0 && errors.values.all? { |error| UNAVAILABLE_STATES.include?(error) }
|
69
72
|
all_devices_unavailable(response)
|
70
73
|
elsif errors.values.any? { |error| UNAVAILABLE_STATES.include?(error) }
|
71
74
|
some_devices_unavailable(response, errors)
|
@@ -117,6 +120,10 @@ module Rapns
|
|
117
120
|
Rapns.logger.warn("All recipients unavailable. " + retry_message)
|
118
121
|
end
|
119
122
|
|
123
|
+
def all_errors_were_invalid_registration_ids(response)
|
124
|
+
mark_failed(nil, "All registration IDs were invalid.")
|
125
|
+
end
|
126
|
+
|
120
127
|
def some_devices_unavailable(response, errors)
|
121
128
|
unavailable_idxs = errors.find_all { |i, error| UNAVAILABLE_STATES.include?(error) }.map(&:first)
|
122
129
|
new_notification = create_new_notification(response, unavailable_idxs)
|
data/lib/rapns/notifier.rb
CHANGED
@@ -32,4 +32,21 @@ module Rapns
|
|
32
32
|
end
|
33
33
|
|
34
34
|
end
|
35
|
+
|
36
|
+
# Call this from a client application after saving a Notification to the database to wakeup the Rapns
|
37
|
+
# Daemon to deliver the notification immediately.
|
38
|
+
def self.wakeup
|
39
|
+
notifier.notify
|
40
|
+
end
|
41
|
+
|
42
|
+
# Default notifier instance. This uses the :connect, :port values in Rapns.config.wakeup to connect to the
|
43
|
+
# wakeup socket in the Rapns Daemon. It will fall back to :host, :port if :connect is not specified.
|
44
|
+
def self.notifier
|
45
|
+
unless @notifier
|
46
|
+
if Rapns.config.wakeup
|
47
|
+
@notifier = Notifier.new(Rapns.config.wakeup[:connect] || Rapns.config.wakeup[:host], Rapns.config.wakeup[:port])
|
48
|
+
end
|
49
|
+
end
|
50
|
+
@notifier
|
51
|
+
end
|
35
52
|
end
|
data/lib/rapns/version.rb
CHANGED
data/lib/rapns_rails_2.rb
CHANGED
@@ -4,6 +4,6 @@ describe Rapns::Apns::Feedback do
|
|
4
4
|
it "should validate the format of the device_token" do
|
5
5
|
notification = Rapns::Apns::Feedback.new(:device_token => "{$%^&*()}")
|
6
6
|
notification.valid?.should be_false
|
7
|
-
notification.errors[:device_token].include
|
7
|
+
notification.errors[:device_token].should include("is invalid")
|
8
8
|
end
|
9
9
|
end
|
@@ -5,77 +5,85 @@ describe Rapns::Daemon::Feeder do
|
|
5
5
|
:push_poll => 0,
|
6
6
|
:embedded => false,
|
7
7
|
:push => false,
|
8
|
-
:
|
9
|
-
:udp_wake_port => nil) }
|
8
|
+
:wakeup => nil) }
|
10
9
|
let!(:app) { Rapns::Apns::App.create!(:name => 'my_app', :environment => 'development', :certificate => TEST_CERT) }
|
11
10
|
let(:notification) { Rapns::Apns::Notification.create!(:device_token => "a" * 64, :app => app) }
|
12
11
|
let(:logger) { double }
|
12
|
+
let(:interruptible_sleep) { double(:sleep => nil, :interrupt_sleep => nil) }
|
13
13
|
|
14
14
|
before do
|
15
15
|
Rapns.stub(:config => config,:logger => logger)
|
16
16
|
Rapns::Daemon.stub(:store => double(:deliverable_notifications => [notification]))
|
17
17
|
Rapns::Daemon::Feeder.stub(:stop? => true)
|
18
18
|
Rapns::Daemon::AppRunner.stub(:enqueue => nil, :idle => [double(:app => app)])
|
19
|
+
Rapns::Daemon::InterruptibleSleep.stub(:new => interruptible_sleep)
|
20
|
+
Rapns::Daemon::Feeder.instance_variable_set('@interruptible_sleeper', nil)
|
19
21
|
end
|
20
22
|
|
21
|
-
def
|
23
|
+
def start_and_stop
|
22
24
|
Rapns::Daemon::Feeder.start
|
25
|
+
Rapns::Daemon::Feeder.stop
|
23
26
|
end
|
24
27
|
|
25
28
|
it "starts the loop in a new thread if embedded" do
|
26
29
|
config.stub(:embedded => true)
|
27
30
|
Thread.should_receive(:new).and_yield
|
28
31
|
Rapns::Daemon::Feeder.should_receive(:feed_forever)
|
29
|
-
|
32
|
+
start_and_stop
|
30
33
|
end
|
31
34
|
|
32
35
|
it 'loads deliverable notifications' do
|
33
36
|
Rapns::Daemon.store.should_receive(:deliverable_notifications).with([app])
|
34
|
-
|
37
|
+
start_and_stop
|
35
38
|
end
|
36
39
|
|
37
40
|
it 'does not attempt to load deliverable notifications if there are no idle runners' do
|
38
41
|
Rapns::Daemon::AppRunner.stub(:idle => [])
|
39
42
|
Rapns::Daemon.store.should_not_receive(:deliverable_notifications)
|
40
|
-
|
43
|
+
start_and_stop
|
41
44
|
end
|
42
45
|
|
43
46
|
it 'enqueues notifications without looping if in push mode' do
|
44
47
|
config.stub(:push => true)
|
45
48
|
Rapns::Daemon::Feeder.should_not_receive(:feed_forever)
|
46
49
|
Rapns::Daemon::Feeder.should_receive(:enqueue_notifications)
|
47
|
-
|
50
|
+
start_and_stop
|
48
51
|
end
|
49
52
|
|
50
53
|
it "enqueues the notifications" do
|
51
54
|
Rapns::Daemon::AppRunner.should_receive(:enqueue).with([notification])
|
52
|
-
|
55
|
+
start_and_stop
|
53
56
|
end
|
54
57
|
|
55
58
|
it "logs errors" do
|
56
59
|
e = StandardError.new("bork")
|
57
60
|
Rapns::Daemon.store.stub(:deliverable_notifications).and_raise(e)
|
58
61
|
Rapns.logger.should_receive(:error).with(e)
|
59
|
-
|
62
|
+
start_and_stop
|
60
63
|
end
|
61
64
|
|
62
65
|
it "interrupts sleep when stopped" do
|
63
66
|
Rapns::Daemon::Feeder.should_receive(:interrupt_sleep)
|
64
|
-
|
67
|
+
start_and_stop
|
65
68
|
end
|
66
69
|
|
67
70
|
it "enqueues notifications when started" do
|
68
71
|
Rapns::Daemon::Feeder.should_receive(:enqueue_notifications).at_least(:once)
|
69
72
|
Rapns::Daemon::Feeder.stub(:loop).and_yield
|
70
|
-
|
73
|
+
start_and_stop
|
71
74
|
end
|
72
75
|
|
73
76
|
it "sleeps for the given period" do
|
74
77
|
config.stub(:push_poll => 2)
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
78
|
+
interruptible_sleep.should_receive(:sleep).with(2)
|
79
|
+
start_and_stop
|
80
|
+
end
|
81
|
+
|
82
|
+
it "creates the wakeup socket" do
|
83
|
+
bind = '127.0.0.1'
|
84
|
+
port = 12345
|
85
|
+
config.stub(:wakeup => { :bind => bind, :port => port})
|
86
|
+
interruptible_sleep.should_receive(:enable_wake_on_udp).with(bind, port)
|
87
|
+
start_and_stop
|
80
88
|
end
|
81
89
|
end
|
@@ -110,7 +110,7 @@ describe Rapns::Daemon::Gcm::Delivery do
|
|
110
110
|
perform
|
111
111
|
end
|
112
112
|
|
113
|
-
it 'does
|
113
|
+
it 'does marks a notification as failed if any ids are invalid.' do
|
114
114
|
body = {
|
115
115
|
'failure' => 1,
|
116
116
|
'success' => 2,
|
@@ -122,7 +122,7 @@ describe Rapns::Daemon::Gcm::Delivery do
|
|
122
122
|
]}
|
123
123
|
|
124
124
|
response.stub(:body => MultiJson.dump(body))
|
125
|
-
batch.
|
125
|
+
batch.should_receive(:mark_failed)
|
126
126
|
batch.should_not_receive(:mark_retryable)
|
127
127
|
store.should_not_receive(:create_gcm_notification)
|
128
128
|
perform
|
data/spec/unit/notifier_spec.rb
CHANGED
@@ -29,4 +29,24 @@ describe Rapns::Notifier do
|
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
32
|
+
|
33
|
+
describe "default notifier" do
|
34
|
+
it "creates using :connect first" do
|
35
|
+
Rapns.config.stub :wakeup => { :connect => '127.0.0.1', :port => 1234 }
|
36
|
+
Rapns::Notifier.should_receive(:new).with('127.0.0.1', 1234)
|
37
|
+
Rapns.notifier
|
38
|
+
end
|
39
|
+
|
40
|
+
it "creates using :host next" do
|
41
|
+
Rapns.config.stub :wakeup => { :host => '127.0.0.1', :port => 1234 }
|
42
|
+
Rapns::Notifier.should_receive(:new).with('127.0.0.1', 1234)
|
43
|
+
Rapns.notifier
|
44
|
+
end
|
45
|
+
|
46
|
+
it "returns nil when wakeup is not specified" do
|
47
|
+
Rapns.config.stub :wakeup => nil
|
48
|
+
Rapns::Notifier.should_not_receive(:new)
|
49
|
+
Rapns.notifier.should be_nil
|
50
|
+
end
|
51
|
+
end
|
32
52
|
end
|
data/spec/unit_spec_helper.rb
CHANGED
@@ -4,7 +4,6 @@ require 'bundler'
|
|
4
4
|
Bundler.require(:default)
|
5
5
|
|
6
6
|
require 'active_record'
|
7
|
-
require 'database_cleaner'
|
8
7
|
|
9
8
|
unless ENV['TRAVIS'] && ENV['QUALITY'] == 'false'
|
10
9
|
begin
|
@@ -53,6 +52,7 @@ require 'generators/templates/add_gcm'
|
|
53
52
|
migration.up
|
54
53
|
end
|
55
54
|
|
55
|
+
require 'database_cleaner'
|
56
56
|
DatabaseCleaner.strategy = :truncation
|
57
57
|
|
58
58
|
require 'rapns_rails_2'
|
metadata
CHANGED
@@ -1,52 +1,62 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: rapns_rails_2
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 19
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 3
|
8
|
+
- 5
|
9
|
+
- 0
|
10
|
+
version: 3.5.0
|
5
11
|
platform: ruby
|
6
|
-
authors:
|
12
|
+
authors:
|
7
13
|
- Ian Leitch
|
8
14
|
- Marc Rohloff
|
9
15
|
autorequire:
|
10
16
|
bindir: bin
|
11
17
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
18
|
+
|
19
|
+
date: 2013-11-20 00:00:00 Z
|
20
|
+
dependencies:
|
21
|
+
- !ruby/object:Gem::Dependency
|
15
22
|
name: multi_json
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
requirements:
|
18
|
-
- - ~>
|
19
|
-
- !ruby/object:Gem::Version
|
20
|
-
version: '1.0'
|
21
|
-
type: :runtime
|
22
23
|
prerelease: false
|
23
|
-
|
24
|
-
|
24
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
25
27
|
- - ~>
|
26
|
-
- !ruby/object:Gem::Version
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
- - ! '>='
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
version: '0'
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
hash: 15
|
30
|
+
segments:
|
31
|
+
- 1
|
32
|
+
- 0
|
33
|
+
version: "1.0"
|
35
34
|
type: :runtime
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: net-http-persistent
|
36
38
|
prerelease: false
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ">="
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
45
|
+
segments:
|
46
|
+
- 0
|
47
|
+
version: "0"
|
48
|
+
type: :runtime
|
49
|
+
version_requirements: *id002
|
42
50
|
description: Professional grade APNs and GCM for Ruby with Rails 2 compatibility
|
43
|
-
email:
|
51
|
+
email:
|
44
52
|
- port001@gmail.com
|
45
|
-
executables:
|
53
|
+
executables:
|
46
54
|
- rapns
|
47
55
|
extensions: []
|
56
|
+
|
48
57
|
extra_rdoc_files: []
|
49
|
-
|
58
|
+
|
59
|
+
files:
|
50
60
|
- CHANGELOG.md
|
51
61
|
- LICENSE
|
52
62
|
- README.md
|
@@ -160,28 +170,38 @@ files:
|
|
160
170
|
- bin/rapns
|
161
171
|
homepage: https://github.com/marcrohloff/rapns_rails_2
|
162
172
|
licenses: []
|
163
|
-
|
173
|
+
|
164
174
|
post_install_message:
|
165
175
|
rdoc_options: []
|
166
|
-
|
176
|
+
|
177
|
+
require_paths:
|
167
178
|
- lib
|
168
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
179
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
180
|
+
none: false
|
181
|
+
requirements:
|
182
|
+
- - ">="
|
183
|
+
- !ruby/object:Gem::Version
|
184
|
+
hash: 3
|
185
|
+
segments:
|
186
|
+
- 0
|
187
|
+
version: "0"
|
188
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
189
|
+
none: false
|
190
|
+
requirements:
|
191
|
+
- - ">="
|
192
|
+
- !ruby/object:Gem::Version
|
193
|
+
hash: 3
|
194
|
+
segments:
|
195
|
+
- 0
|
196
|
+
version: "0"
|
178
197
|
requirements: []
|
198
|
+
|
179
199
|
rubyforge_project:
|
180
|
-
rubygems_version:
|
200
|
+
rubygems_version: 1.8.4
|
181
201
|
signing_key:
|
182
|
-
specification_version:
|
202
|
+
specification_version: 3
|
183
203
|
summary: Professional grade APNs and GCM for Ruby
|
184
|
-
test_files:
|
204
|
+
test_files:
|
185
205
|
- config/database.yml
|
186
206
|
- spec/support/cert_with_password.pem
|
187
207
|
- spec/support/cert_without_password.pem
|
checksums.yaml
DELETED
@@ -1,15 +0,0 @@
|
|
1
|
-
---
|
2
|
-
!binary "U0hBMQ==":
|
3
|
-
metadata.gz: !binary |-
|
4
|
-
ODdlNDhlOTIzODkyMDZiODdiNzIwYjAyNDJlNjkzZDYyMmYyZDhjNQ==
|
5
|
-
data.tar.gz: !binary |-
|
6
|
-
NDlhMWY3NGRmOTUwMmQ1NGRjMjg2MjE2ZDY5NmYwNWE5NjgyZWM1MA==
|
7
|
-
!binary "U0hBNTEy":
|
8
|
-
metadata.gz: !binary |-
|
9
|
-
MTkzNDAxZTZmNWM4YTg3OTEzYWJhM2Q1YmE3MzBhZjg0YjUxYWJmMjAwYWMz
|
10
|
-
NGZmMGM0Mzk4MDM1MGU3OTFjZWVlNThhZTg1MDhiMmRmNDFhZjk3ZjcxYTA4
|
11
|
-
OTllMzFjM2NkZWRmYmVhZTlmOTkwYzlhMGI3NmVkZjQ1NjRiMTE=
|
12
|
-
data.tar.gz: !binary |-
|
13
|
-
YzQ3ZmMzMzU1MzFmYzczZGI4M2FlYWRjMTY3Y2NmNmJlOTY5Y2UxMWNhODUz
|
14
|
-
NTlmY2QwNTEyMmM0MWVkNGExNGJiNjdlYmViNjY3MzkyOWFhM2M3ZWUwY2Jm
|
15
|
-
M2Q0ODNlNmJiMmU1MDI2Mjc3ZGIzYjE1ZWQ1YjgxZmZiNGY3MzU=
|