rapns 3.2.0 → 3.3.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.
Files changed (36) hide show
  1. data/CHANGELOG.md +5 -0
  2. data/README.md +9 -18
  3. data/lib/generators/templates/rapns.rb +5 -0
  4. data/lib/rapns.rb +4 -0
  5. data/lib/rapns/apns_feedback.rb +1 -0
  6. data/lib/rapns/configuration.rb +4 -3
  7. data/lib/rapns/daemon.rb +20 -4
  8. data/lib/rapns/daemon/apns/feedback_receiver.rb +9 -11
  9. data/lib/rapns/daemon/delivery.rb +3 -20
  10. data/lib/rapns/daemon/delivery_queue.rb +2 -2
  11. data/lib/rapns/daemon/feeder.rb +5 -10
  12. data/lib/rapns/daemon/gcm/delivery.rb +19 -9
  13. data/lib/rapns/daemon/store/active_record.rb +74 -0
  14. data/lib/rapns/daemon/store/active_record/reconnectable.rb +61 -0
  15. data/lib/rapns/gcm/notification.rb +5 -4
  16. data/lib/rapns/gcm/registration_ids_count_validator.rb +1 -1
  17. data/lib/rapns/logger.rb +6 -2
  18. data/lib/rapns/push.rb +1 -0
  19. data/lib/rapns/reflection.rb +1 -1
  20. data/lib/rapns/version.rb +1 -1
  21. data/spec/unit/apns/notification_spec.rb +2 -0
  22. data/spec/unit/apns_feedback_spec.rb +5 -0
  23. data/spec/unit/configuration_spec.rb +1 -1
  24. data/spec/unit/daemon/apns/delivery_spec.rb +7 -64
  25. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +2 -2
  26. data/spec/unit/daemon/feeder_spec.rb +6 -58
  27. data/spec/unit/daemon/gcm/delivery_spec.rb +49 -57
  28. data/spec/unit/daemon/{database_reconnectable_spec.rb → store/active_record/reconnectable_spec.rb} +4 -3
  29. data/spec/unit/daemon/store/active_record_spec.rb +181 -0
  30. data/spec/unit/daemon_spec.rb +27 -7
  31. data/spec/unit/gcm/notification_spec.rb +2 -9
  32. data/spec/unit/push_spec.rb +5 -0
  33. data/spec/unit/reflection_spec.rb +0 -4
  34. data/spec/unit_spec_helper.rb +4 -1
  35. metadata +10 -7
  36. data/lib/rapns/daemon/database_reconnectable.rb +0 -57
@@ -1,8 +1,9 @@
1
- require "unit_spec_helper"
1
+ require 'unit_spec_helper'
2
+ require 'rapns/daemon/store/active_record/reconnectable'
2
3
 
3
- describe Rapns::Daemon::DatabaseReconnectable do
4
+ describe Rapns::Daemon::Store::ActiveRecord::Reconnectable do
4
5
  class TestDouble
5
- include Rapns::Daemon::DatabaseReconnectable
6
+ include Rapns::Daemon::Store::ActiveRecord::Reconnectable
6
7
 
7
8
  attr_reader :name
8
9
 
@@ -0,0 +1,181 @@
1
+ require 'unit_spec_helper'
2
+ require 'rapns/daemon/store/active_record'
3
+
4
+ describe Rapns::Daemon::Store::ActiveRecord do
5
+ let(:app) { Rapns::Apns::App.create!(:name => 'my_app', :environment => 'development', :certificate => TEST_CERT) }
6
+ let(:notification) { Rapns::Apns::Notification.create!(:device_token => "a" * 64, :app => app) }
7
+ let(:store) { Rapns::Daemon::Store::ActiveRecord.new }
8
+ let(:now) { Time.now }
9
+
10
+ before { Time.stub(:now => now) }
11
+
12
+ describe 'deliverable_notifications' do
13
+ it 'checks for new notifications with the ability to reconnect the database' do
14
+ store.should_receive(:with_database_reconnect_and_retry)
15
+ store.deliverable_notifications(app)
16
+ end
17
+
18
+ it 'loads notifications in batches' do
19
+ Rapns.config.batch_size = 5000
20
+ Rapns.config.push = false
21
+ relation = stub.as_null_object
22
+ relation.should_receive(:limit).with(5000)
23
+ Rapns::Notification.stub(:ready_for_delivery => relation)
24
+ store.deliverable_notifications([app])
25
+ end
26
+
27
+ it 'does not load notification in batches if in push mode' do
28
+ Rapns.config.push = true
29
+ relation = stub.as_null_object
30
+ relation.should_not_receive(:limit)
31
+ Rapns::Notification.stub(:ready_for_delivery => relation)
32
+ store.deliverable_notifications([app])
33
+ end
34
+
35
+ it 'loads an undelivered notification without deliver_after set' do
36
+ notification.update_attributes!(:delivered => false, :deliver_after => nil)
37
+ store.deliverable_notifications([app]).should == [notification]
38
+ end
39
+
40
+ it 'loads an notification with a deliver_after time in the past' do
41
+ notification.update_attributes!(:delivered => false, :deliver_after => 1.hour.ago)
42
+ store.deliverable_notifications([app]).should == [notification]
43
+ end
44
+
45
+ it 'does not load an notification with a deliver_after time in the future' do
46
+ notification.update_attributes!(:delivered => false, :deliver_after => 1.hour.from_now)
47
+ store.deliverable_notifications([app]).should be_empty
48
+ end
49
+
50
+ it 'does not load a previously delivered notification' do
51
+ notification.update_attributes!(:delivered => true, :delivered_at => Time.now)
52
+ store.deliverable_notifications([app]).should be_empty
53
+ end
54
+
55
+ it "does not enqueue a notification that has previously failed delivery" do
56
+ notification.update_attributes!(:delivered => false, :failed => true)
57
+ store.deliverable_notifications([app]).should be_empty
58
+ end
59
+
60
+ it 'does not load notifications for apps that are still processing the previous batch' do
61
+ notification
62
+ store.deliverable_notifications([]).should be_empty
63
+ end
64
+ end
65
+
66
+ describe 'retry_after' do
67
+ it 'increments the retry count' do
68
+ expect do
69
+ store.retry_after(notification, now)
70
+ end.to change(notification, :retries).by(1)
71
+ end
72
+
73
+ it 'sets the deliver after timestamp' do
74
+ deliver_after = now + 10.seconds
75
+ expect do
76
+ store.retry_after(notification, deliver_after)
77
+ end.to change(notification, :deliver_after).to(deliver_after)
78
+ end
79
+
80
+ it 'saves the notification without validation' do
81
+ notification.should_receive(:save!).with(:validate => false)
82
+ store.retry_after(notification, now)
83
+ end
84
+ end
85
+
86
+ describe 'mark_delivered' do
87
+ it 'marks the notification as delivered' do
88
+ expect do
89
+ store.mark_delivered(notification)
90
+ end.to change(notification, :delivered).to(true)
91
+ end
92
+
93
+ it 'sets the time the notification was delivered' do
94
+ expect do
95
+ store.mark_delivered(notification)
96
+ end.to change(notification, :delivered_at).to(now)
97
+ end
98
+
99
+ it 'saves the notification without validation' do
100
+ notification.should_receive(:save!).with(:validate => false)
101
+ store.mark_delivered(notification)
102
+ end
103
+ end
104
+
105
+ describe 'mark_failed' do
106
+ it 'marks the notification as not delivered' do
107
+ store.mark_failed(notification, nil, '')
108
+ notification.delivered.should be_false
109
+ end
110
+
111
+ it 'marks the notification as failed' do
112
+ expect do
113
+ store.mark_failed(notification, nil, '')
114
+ end.to change(notification, :failed).to(true)
115
+ end
116
+
117
+ it 'sets the time the notification delivery failed' do
118
+ expect do
119
+ store.mark_failed(notification, nil, '')
120
+ end.to change(notification, :failed_at).to(now)
121
+ end
122
+
123
+ it 'sets the error code' do
124
+ expect do
125
+ store.mark_failed(notification, 42, '')
126
+ end.to change(notification, :error_code).to(42)
127
+ end
128
+
129
+ it 'sets the error description' do
130
+ expect do
131
+ store.mark_failed(notification, 42, 'Weeee')
132
+ end.to change(notification, :error_description).to('Weeee')
133
+ end
134
+
135
+ it 'saves the notification without validation' do
136
+ notification.should_receive(:save!).with(:validate => false)
137
+ store.mark_failed(notification, nil, '')
138
+ end
139
+ end
140
+
141
+ describe 'create_apns_feedback' do
142
+ it 'creates the Feedback record' do
143
+ Rapns::Apns::Feedback.should_receive(:create!).with(
144
+ :failed_at => now, :device_token => 'ab' * 32, :app => app)
145
+ store.create_apns_feedback(now, 'ab' * 32, app)
146
+ end
147
+ end
148
+
149
+ describe 'create_gcm_notification' do
150
+ let(:data) { { :data => true } }
151
+ let(:attributes) { { :device_token => 'ab' * 32 } }
152
+ let(:registration_ids) { ['123', '456'] }
153
+ let(:deliver_after) { now + 10.seconds }
154
+ let(:args) { [attributes, data, registration_ids, deliver_after, app] }
155
+
156
+ it 'sets the given attributes' do
157
+ new_notification = store.create_gcm_notification(*args)
158
+ new_notification.device_token.should == 'ab' * 32
159
+ end
160
+
161
+ it 'sets the given data' do
162
+ new_notification = store.create_gcm_notification(*args)
163
+ new_notification.data['data'].should be_true
164
+ end
165
+
166
+ it 'sets the given registration IDs' do
167
+ new_notification = store.create_gcm_notification(*args)
168
+ new_notification.registration_ids.should == registration_ids
169
+ end
170
+
171
+ it 'sets the deliver_after timestamp' do
172
+ new_notification = store.create_gcm_notification(*args)
173
+ new_notification.deliver_after.should == deliver_after
174
+ end
175
+
176
+ it 'saves the new notification' do
177
+ new_notification = store.create_gcm_notification(*args)
178
+ new_notification.new_record?.should be_false
179
+ end
180
+ end
181
+ end
@@ -1,4 +1,5 @@
1
1
  require 'unit_spec_helper'
2
+ require 'rapns/daemon/store/active_record'
2
3
 
3
4
  describe Rapns::Daemon, "when starting" do
4
5
  module Rails; end
@@ -6,27 +7,34 @@ describe Rapns::Daemon, "when starting" do
6
7
  let(:certificate) { stub }
7
8
  let(:password) { stub }
8
9
  let(:config) { stub(:pid_file => nil, :airbrake_notify => false,
9
- :foreground => true, :embedded => false, :push => false) }
10
+ :foreground => true, :embedded => false, :push => false, :store => :active_record) }
10
11
  let(:logger) { stub(:logger, :info => nil, :error => nil, :warn => nil) }
11
12
 
12
13
  before do
13
- Rapns.stub(:config => config)
14
- Rapns::Logger.stub(:new => logger)
14
+ Rapns.stub(:config => config, :logger => logger)
15
15
  Rapns::Daemon::Feeder.stub(:start)
16
16
  Rapns::Daemon::AppRunner.stub(:sync => nil, :stop => nil)
17
- Rapns::Daemon.stub(:daemonize => nil, :reconnect_database => nil, :exit => nil, :puts => nil)
17
+ Rapns::Daemon.stub(:daemonize => nil, :exit => nil, :puts => nil)
18
18
  File.stub(:open)
19
19
  Rails.stub(:root).and_return("/rails_root")
20
20
  end
21
21
 
22
- unless defined?(JRUBY_VERSION)
22
+ unless Rapns.jruby?
23
23
  it "forks into a daemon if the foreground option is false" do
24
24
  config.stub(:foreground => false)
25
- ActiveRecord::Base.stub(:establish_connection)
25
+ Rapns::Daemon.initialize_store
26
+ Rapns::Daemon.store.stub(:after_daemonize => nil)
26
27
  Rapns::Daemon.should_receive(:daemonize)
27
28
  Rapns::Daemon.start
28
29
  end
29
30
 
31
+ it 'notifies the store after forking' do
32
+ config.stub(:foreground => false)
33
+ Rapns::Daemon.initialize_store
34
+ Rapns::Daemon.store.should_receive(:after_daemonize)
35
+ Rapns::Daemon.start
36
+ end
37
+
30
38
  it "does not fork into a daemon if the foreground option is true" do
31
39
  config.stub(:foreground => true)
32
40
  Rapns::Daemon.should_not_receive(:daemonize)
@@ -57,6 +65,18 @@ describe Rapns::Daemon, "when starting" do
57
65
  Rapns::Daemon.start
58
66
  end
59
67
 
68
+ it 'instantiates the store' do
69
+ config.stub(:store => :active_record)
70
+ Rapns::Daemon.start
71
+ Rapns::Daemon.store.should be_kind_of(Rapns::Daemon::Store::ActiveRecord)
72
+ end
73
+
74
+ it 'logs an error if the store cannot be loaded' do
75
+ config.stub(:store => :foo_bar)
76
+ Rapns.logger.should_receive(:error).with(kind_of(LoadError))
77
+ Rapns::Daemon.start
78
+ end
79
+
60
80
  it "writes the process ID to the PID file" do
61
81
  Rapns::Daemon.should_receive(:write_pid_file)
62
82
  Rapns::Daemon.start
@@ -91,7 +111,7 @@ describe Rapns::Daemon, "when being shutdown" do
91
111
  end
92
112
 
93
113
  # These tests do not work on JRuby.
94
- unless defined? JRUBY_VERSION
114
+ unless Rapns.jruby?
95
115
  it "shuts down when signaled signaled SIGINT" do
96
116
  Rapns::Daemon.setup_signal_traps
97
117
  Rapns::Daemon.should_receive(:shutdown)
@@ -27,7 +27,7 @@ describe Rapns::Gcm::Notification do
27
27
  it 'num of registration Ids limit of 1000' do
28
28
  notification.registration_ids = ['a']*(1000+1)
29
29
  notification.valid?.should be_false
30
- notification.errors[:base].should == ["GCM notification num of registration_ids cannot be larger than 1000."]
30
+ notification.errors[:base].should == ["GCM notification number of registration_ids cannot be larger than 1000."]
31
31
  end
32
32
 
33
33
  it 'allows assignment of a single registration ID' do
@@ -45,15 +45,8 @@ describe Rapns::Gcm::Notification do
45
45
  notification.errors[:expiry].should == ['must be set when using a collapse_key']
46
46
  end
47
47
 
48
- it 'does not include time_to_live in the payload if collapse_key is not set' do
48
+ it 'includes time_to_live in the payload' do
49
49
  notification.expiry = 100
50
- notification.collapse_key = nil
51
- notification.as_json.key?('time_to_live').should be_false
52
- end
53
-
54
- it 'includes time_to_live in the payload if collapse_key is set' do
55
- notification.expiry = 100
56
- notification.collapse_key = 'sync'
57
50
  notification.as_json['time_to_live'].should == 100
58
51
  end
59
52
  end
@@ -12,6 +12,11 @@ describe Rapns, 'push' do
12
12
  Rapns.config.push.should be_true
13
13
  end
14
14
 
15
+ it 'initializes the store' do
16
+ Rapns::Daemon.should_receive(:initialize_store)
17
+ Rapns.push
18
+ end
19
+
15
20
  it 'syncs the app runner' do
16
21
  Rapns::Daemon::AppRunner.should_receive(:sync)
17
22
  Rapns.push
@@ -12,10 +12,6 @@ describe Rapns do
12
12
  end
13
13
  end
14
14
 
15
- # :apns_feedback, :notification_enqueued, :notification_delivered,
16
- # :notification_failed, :notification_will_retry, :apns_connection_lost,
17
- # :error
18
-
19
15
  describe Rapns::Reflections do
20
16
  it 'dispatches the given reflection' do
21
17
  did_yield = false
@@ -72,7 +72,10 @@ RSpec.configure do |config|
72
72
  DatabaseCleaner.clean
73
73
  end
74
74
 
75
- config.after(:each) { Rapns.logger = nil }
75
+ config.after(:each) do
76
+ Rapns.logger = nil
77
+ Rapns::Daemon.store = nil
78
+ end
76
79
  end
77
80
 
78
81
  # a test certificate that contains both an X509 certificate and
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rapns
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.2.0
4
+ version: 3.3.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-02 00:00:00.000000000 Z
12
+ date: 2013-04-21 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: multi_json
@@ -80,7 +80,6 @@ files:
80
80
  - lib/rapns/daemon/apns/disconnection_error.rb
81
81
  - lib/rapns/daemon/apns/feedback_receiver.rb
82
82
  - lib/rapns/daemon/app_runner.rb
83
- - lib/rapns/daemon/database_reconnectable.rb
84
83
  - lib/rapns/daemon/delivery.rb
85
84
  - lib/rapns/daemon/delivery_error.rb
86
85
  - lib/rapns/daemon/delivery_handler.rb
@@ -93,6 +92,8 @@ files:
93
92
  - lib/rapns/daemon/gcm/delivery_handler.rb
94
93
  - lib/rapns/daemon/interruptible_sleep.rb
95
94
  - lib/rapns/daemon/reflectable.rb
95
+ - lib/rapns/daemon/store/active_record.rb
96
+ - lib/rapns/daemon/store/active_record/reconnectable.rb
96
97
  - lib/rapns/deprecatable.rb
97
98
  - lib/rapns/deprecation.rb
98
99
  - lib/rapns/embed.rb
@@ -134,7 +135,6 @@ files:
134
135
  - spec/unit/daemon/apns/feedback_receiver_spec.rb
135
136
  - spec/unit/daemon/app_runner_shared.rb
136
137
  - spec/unit/daemon/app_runner_spec.rb
137
- - spec/unit/daemon/database_reconnectable_spec.rb
138
138
  - spec/unit/daemon/delivery_error_spec.rb
139
139
  - spec/unit/daemon/delivery_handler_shared.rb
140
140
  - spec/unit/daemon/delivery_queue_spec.rb
@@ -144,6 +144,8 @@ files:
144
144
  - spec/unit/daemon/gcm/delivery_spec.rb
145
145
  - spec/unit/daemon/interruptible_sleep_spec.rb
146
146
  - spec/unit/daemon/reflectable_spec.rb
147
+ - spec/unit/daemon/store/active_record/reconnectable_spec.rb
148
+ - spec/unit/daemon/store/active_record_spec.rb
147
149
  - spec/unit/daemon_spec.rb
148
150
  - spec/unit/deprecatable_spec.rb
149
151
  - spec/unit/deprecation_spec.rb
@@ -173,7 +175,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
173
175
  version: '0'
174
176
  segments:
175
177
  - 0
176
- hash: -3688702007568166292
178
+ hash: -3085387548794744992
177
179
  required_rubygems_version: !ruby/object:Gem::Requirement
178
180
  none: false
179
181
  requirements:
@@ -182,7 +184,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
182
184
  version: '0'
183
185
  segments:
184
186
  - 0
185
- hash: -3688702007568166292
187
+ hash: -3085387548794744992
186
188
  requirements: []
187
189
  rubyforge_project:
188
190
  rubygems_version: 1.8.25
@@ -211,7 +213,6 @@ test_files:
211
213
  - spec/unit/daemon/apns/feedback_receiver_spec.rb
212
214
  - spec/unit/daemon/app_runner_shared.rb
213
215
  - spec/unit/daemon/app_runner_spec.rb
214
- - spec/unit/daemon/database_reconnectable_spec.rb
215
216
  - spec/unit/daemon/delivery_error_spec.rb
216
217
  - spec/unit/daemon/delivery_handler_shared.rb
217
218
  - spec/unit/daemon/delivery_queue_spec.rb
@@ -221,6 +222,8 @@ test_files:
221
222
  - spec/unit/daemon/gcm/delivery_spec.rb
222
223
  - spec/unit/daemon/interruptible_sleep_spec.rb
223
224
  - spec/unit/daemon/reflectable_spec.rb
225
+ - spec/unit/daemon/store/active_record/reconnectable_spec.rb
226
+ - spec/unit/daemon/store/active_record_spec.rb
224
227
  - spec/unit/daemon_spec.rb
225
228
  - spec/unit/deprecatable_spec.rb
226
229
  - spec/unit/deprecation_spec.rb
@@ -1,57 +0,0 @@
1
- class PGError < StandardError; end if !defined?(PGError)
2
- class Mysql; class Error < StandardError; end; end if !defined?(Mysql)
3
- module Mysql2; class Error < StandardError; end; end if !defined?(Mysql2)
4
- module ActiveRecord; end
5
- class ActiveRecord::JDBCError < StandardError; end if !defined?(ActiveRecord::JDBCError)
6
-
7
- module Rapns
8
- module Daemon
9
- module DatabaseReconnectable
10
- ADAPTER_ERRORS = [ActiveRecord::StatementInvalid, PGError, Mysql::Error,
11
- Mysql2::Error, ActiveRecord::JDBCError]
12
-
13
- def with_database_reconnect_and_retry
14
- begin
15
- ActiveRecord::Base.connection_pool.with_connection do
16
- yield
17
- end
18
- rescue *ADAPTER_ERRORS => e
19
- Rapns.logger.error(e)
20
- database_connection_lost
21
- retry
22
- end
23
- end
24
-
25
- def database_connection_lost
26
- Rapns.logger.warn("Lost connection to database, reconnecting...")
27
- attempts = 0
28
- loop do
29
- begin
30
- Rapns.logger.warn("Attempt #{attempts += 1}")
31
- reconnect_database
32
- check_database_is_connected
33
- break
34
- rescue *ADAPTER_ERRORS => e
35
- Rapns.logger.error(e, :airbrake_notify => false)
36
- sleep_to_avoid_thrashing
37
- end
38
- end
39
- Rapns.logger.warn("Database reconnected")
40
- end
41
-
42
- def reconnect_database
43
- ActiveRecord::Base.clear_all_connections!
44
- ActiveRecord::Base.establish_connection
45
- end
46
-
47
- def check_database_is_connected
48
- # Simply asking the adapter for the connection state is not sufficient.
49
- Rapns::Notification.count
50
- end
51
-
52
- def sleep_to_avoid_thrashing
53
- sleep 2
54
- end
55
- end
56
- end
57
- end