rapns_rails_2 3.4.3 → 3.5.0
Sign up to get free protection for your applications and to get access to all the features.
- 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=
|