rapns_rails_2 3.4.3

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 (113) hide show
  1. checksums.yaml +15 -0
  2. data/CHANGELOG.md +83 -0
  3. data/LICENSE +7 -0
  4. data/README.md +168 -0
  5. data/bin/rapns +37 -0
  6. data/config/database.yml +44 -0
  7. data/lib/generators/rapns_generator.rb +25 -0
  8. data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +9 -0
  9. data/lib/generators/templates/add_app_to_rapns.rb +11 -0
  10. data/lib/generators/templates/add_gcm.rb +95 -0
  11. data/lib/generators/templates/create_rapns_apps.rb +16 -0
  12. data/lib/generators/templates/create_rapns_feedback.rb +15 -0
  13. data/lib/generators/templates/create_rapns_notifications.rb +26 -0
  14. data/lib/generators/templates/rapns.rb +87 -0
  15. data/lib/rapns/TODO +3 -0
  16. data/lib/rapns/apns/app.rb +25 -0
  17. data/lib/rapns/apns/binary_notification_validator.rb +12 -0
  18. data/lib/rapns/apns/device_token_format_validator.rb +12 -0
  19. data/lib/rapns/apns/feedback.rb +16 -0
  20. data/lib/rapns/apns/notification.rb +91 -0
  21. data/lib/rapns/apns_feedback.rb +13 -0
  22. data/lib/rapns/app.rb +16 -0
  23. data/lib/rapns/configuration.rb +89 -0
  24. data/lib/rapns/daemon/apns/app_runner.rb +26 -0
  25. data/lib/rapns/daemon/apns/certificate_expired_error.rb +20 -0
  26. data/lib/rapns/daemon/apns/connection.rb +142 -0
  27. data/lib/rapns/daemon/apns/delivery.rb +64 -0
  28. data/lib/rapns/daemon/apns/delivery_handler.rb +35 -0
  29. data/lib/rapns/daemon/apns/disconnection_error.rb +20 -0
  30. data/lib/rapns/daemon/apns/feedback_receiver.rb +89 -0
  31. data/lib/rapns/daemon/app_runner.rb +179 -0
  32. data/lib/rapns/daemon/batch.rb +112 -0
  33. data/lib/rapns/daemon/delivery.rb +23 -0
  34. data/lib/rapns/daemon/delivery_error.rb +19 -0
  35. data/lib/rapns/daemon/delivery_handler.rb +52 -0
  36. data/lib/rapns/daemon/delivery_handler_collection.rb +33 -0
  37. data/lib/rapns/daemon/feeder.rb +65 -0
  38. data/lib/rapns/daemon/gcm/app_runner.rb +13 -0
  39. data/lib/rapns/daemon/gcm/delivery.rb +228 -0
  40. data/lib/rapns/daemon/gcm/delivery_handler.rb +20 -0
  41. data/lib/rapns/daemon/interruptible_sleep.rb +65 -0
  42. data/lib/rapns/daemon/reflectable.rb +13 -0
  43. data/lib/rapns/daemon/store/active_record/reconnectable.rb +66 -0
  44. data/lib/rapns/daemon/store/active_record.rb +128 -0
  45. data/lib/rapns/daemon.rb +129 -0
  46. data/lib/rapns/deprecatable.rb +23 -0
  47. data/lib/rapns/deprecation.rb +23 -0
  48. data/lib/rapns/embed.rb +28 -0
  49. data/lib/rapns/gcm/app.rb +7 -0
  50. data/lib/rapns/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +11 -0
  51. data/lib/rapns/gcm/notification.rb +37 -0
  52. data/lib/rapns/gcm/payload_data_size_validator.rb +13 -0
  53. data/lib/rapns/gcm/registration_ids_count_validator.rb +13 -0
  54. data/lib/rapns/logger.rb +76 -0
  55. data/lib/rapns/multi_json_helper.rb +16 -0
  56. data/lib/rapns/notification.rb +62 -0
  57. data/lib/rapns/notifier.rb +35 -0
  58. data/lib/rapns/push.rb +17 -0
  59. data/lib/rapns/rails-2-compatibility.rb +34 -0
  60. data/lib/rapns/reflection.rb +44 -0
  61. data/lib/rapns/upgraded.rb +31 -0
  62. data/lib/rapns/version.rb +3 -0
  63. data/lib/rapns_rails_2.rb +67 -0
  64. data/lib/tasks/cane.rake +18 -0
  65. data/lib/tasks/test.rake +38 -0
  66. data/spec/support/cert_with_password.pem +90 -0
  67. data/spec/support/cert_without_password.pem +59 -0
  68. data/spec/support/simplecov_helper.rb +13 -0
  69. data/spec/support/simplecov_quality_formatter.rb +8 -0
  70. data/spec/tmp/.gitkeep +0 -0
  71. data/spec/unit/apns/app_spec.rb +29 -0
  72. data/spec/unit/apns/feedback_spec.rb +9 -0
  73. data/spec/unit/apns/notification_spec.rb +215 -0
  74. data/spec/unit/apns_feedback_spec.rb +21 -0
  75. data/spec/unit/app_spec.rb +16 -0
  76. data/spec/unit/configuration_spec.rb +55 -0
  77. data/spec/unit/daemon/apns/app_runner_spec.rb +45 -0
  78. data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +11 -0
  79. data/spec/unit/daemon/apns/connection_spec.rb +287 -0
  80. data/spec/unit/daemon/apns/delivery_handler_spec.rb +59 -0
  81. data/spec/unit/daemon/apns/delivery_spec.rb +101 -0
  82. data/spec/unit/daemon/apns/disconnection_error_spec.rb +18 -0
  83. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +134 -0
  84. data/spec/unit/daemon/app_runner_shared.rb +83 -0
  85. data/spec/unit/daemon/app_runner_spec.rb +170 -0
  86. data/spec/unit/daemon/batch_spec.rb +219 -0
  87. data/spec/unit/daemon/delivery_error_spec.rb +13 -0
  88. data/spec/unit/daemon/delivery_handler_collection_spec.rb +37 -0
  89. data/spec/unit/daemon/delivery_handler_shared.rb +45 -0
  90. data/spec/unit/daemon/feeder_spec.rb +81 -0
  91. data/spec/unit/daemon/gcm/app_runner_spec.rb +19 -0
  92. data/spec/unit/daemon/gcm/delivery_handler_spec.rb +44 -0
  93. data/spec/unit/daemon/gcm/delivery_spec.rb +289 -0
  94. data/spec/unit/daemon/interruptible_sleep_spec.rb +68 -0
  95. data/spec/unit/daemon/reflectable_spec.rb +27 -0
  96. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +114 -0
  97. data/spec/unit/daemon/store/active_record_spec.rb +281 -0
  98. data/spec/unit/daemon_spec.rb +157 -0
  99. data/spec/unit/deprecatable_spec.rb +32 -0
  100. data/spec/unit/deprecation_spec.rb +15 -0
  101. data/spec/unit/embed_spec.rb +50 -0
  102. data/spec/unit/gcm/app_spec.rb +4 -0
  103. data/spec/unit/gcm/notification_spec.rb +52 -0
  104. data/spec/unit/logger_spec.rb +180 -0
  105. data/spec/unit/notification_shared.rb +45 -0
  106. data/spec/unit/notification_spec.rb +4 -0
  107. data/spec/unit/notifier_spec.rb +32 -0
  108. data/spec/unit/push_spec.rb +44 -0
  109. data/spec/unit/rapns_spec.rb +9 -0
  110. data/spec/unit/reflection_spec.rb +30 -0
  111. data/spec/unit/upgraded_spec.rb +40 -0
  112. data/spec/unit_spec_helper.rb +137 -0
  113. metadata +232 -0
@@ -0,0 +1,179 @@
1
+ module Rapns
2
+ module Daemon
3
+ class AppRunner
4
+ extend Reflectable
5
+ include Reflectable
6
+
7
+ class << self
8
+ attr_reader :runners
9
+ end
10
+
11
+ @runners = {}
12
+
13
+ def self.enqueue(notifications)
14
+ notifications.group_by(&:app_id).each do |app_id, group|
15
+ batch = Batch.new(group)
16
+ if app = runners[app_id]
17
+ app.enqueue(batch)
18
+ else
19
+ Rapns.logger.error("No such app '#{app_id}' for notifications #{batch.describe}.")
20
+ end
21
+ end
22
+ end
23
+
24
+ def self.sync
25
+ apps = Rapns::App.all
26
+ apps.each { |app| sync_app(app) }
27
+ removed = runners.keys - apps.map(&:id)
28
+ removed.each { |app_id| runners.delete(app_id).stop }
29
+ end
30
+
31
+ def self.sync_app(app)
32
+ if runners[app.id]
33
+ runners[app.id].sync(app)
34
+ else
35
+ runner = new_runner(app)
36
+ begin
37
+ runner.start
38
+ runners[app.id] = runner
39
+ rescue StandardError => e
40
+ Rapns.logger.error("[#{app.name}] Exception raised during startup. Notifications will not be delivered for this app.")
41
+ Rapns.logger.error(e)
42
+ reflect(:error, e)
43
+ end
44
+ end
45
+ end
46
+
47
+ def self.new_runner(app)
48
+ type = app.class.parent.name.demodulize
49
+ "Rapns::Daemon::#{type}::AppRunner".constantize.new(app)
50
+ end
51
+
52
+ def self.stop
53
+ runners.values.map(&:stop)
54
+ runners.clear
55
+ end
56
+
57
+ def self.debug
58
+ runners.values.map(&:debug)
59
+ end
60
+
61
+ def self.idle
62
+ runners.values.select(&:idle?)
63
+ end
64
+
65
+ def self.wait
66
+ sleep 0.1 while !runners.values.all?(&:idle?)
67
+ end
68
+
69
+ attr_reader :app
70
+ attr_accessor :batch
71
+
72
+ def initialize(app)
73
+ @app = app
74
+ end
75
+
76
+ def before_start; end
77
+ def after_start; end
78
+ def before_stop; end
79
+ def after_stop; end
80
+
81
+ def start
82
+ before_start
83
+ app.connections.times { handlers.push(start_handler) }
84
+ after_start
85
+ Rapns.logger.info("[#{app.name}] Started, #{handlers_str}.")
86
+ end
87
+
88
+ def stop
89
+ before_stop
90
+ handlers.stop
91
+ after_stop
92
+ end
93
+
94
+ def enqueue(batch)
95
+ self.batch = batch
96
+ batch.notifications.each do |notification|
97
+ queue.push([notification, batch])
98
+ reflect(:notification_enqueued, notification)
99
+ end
100
+ end
101
+
102
+ def sync(app)
103
+ @app = app
104
+ diff = handlers.size - app.connections
105
+ return if diff == 0
106
+ if diff > 0
107
+ decrement_handlers(diff)
108
+ Rapns.logger.info("[#{app.name}] Stopped #{handlers_str(diff)}. #{handlers_str} running.")
109
+ else
110
+ increment_handlers(diff.abs)
111
+ Rapns.logger.info("[#{app.name}] Started #{handlers_str(diff)}. #{handlers_str} running.")
112
+ end
113
+ end
114
+
115
+ def decrement_handlers(num)
116
+ num.times { handlers.pop }
117
+ end
118
+
119
+ def increment_handlers(num)
120
+ num.times { handlers.push(start_handler) }
121
+ end
122
+
123
+ def debug
124
+ Rapns.logger.info <<-EOS
125
+
126
+ #{@app.name}:
127
+ handlers: #{num_handlers}
128
+ queued: #{queue_size}
129
+ batch size: #{batch_size}
130
+ batch processed: #{batch_processed}
131
+ idle: #{idle?}
132
+ EOS
133
+ end
134
+
135
+ def idle?
136
+ batch ? batch.complete? : true
137
+ end
138
+
139
+ def queue_size
140
+ queue.size
141
+ end
142
+
143
+ def batch_size
144
+ batch ? batch.num_notifications : 0
145
+ end
146
+
147
+ def batch_processed
148
+ batch ? batch.num_processed : 0
149
+ end
150
+
151
+ def num_handlers
152
+ handlers.size
153
+ end
154
+
155
+ protected
156
+
157
+ def start_handler
158
+ handler = new_delivery_handler
159
+ handler.queue = queue
160
+ handler.start
161
+ handler
162
+ end
163
+
164
+ def queue
165
+ @queue ||= Queue.new
166
+ end
167
+
168
+ def handlers
169
+ @handlers ||= Rapns::Daemon::DeliveryHandlerCollection.new
170
+ end
171
+
172
+ def handlers_str(count = app.connections)
173
+ count = count.abs
174
+ str = count == 1 ? 'handler' : 'handlers'
175
+ "#{count} #{str}"
176
+ end
177
+ end
178
+ end
179
+ end
@@ -0,0 +1,112 @@
1
+ module Rapns
2
+ module Daemon
3
+ class Batch
4
+ include Reflectable
5
+
6
+ attr_reader :num_processed, :notifications,
7
+ :delivered, :failed, :retryable
8
+
9
+ def initialize(notifications)
10
+ @notifications = notifications
11
+ @num_processed = 0
12
+ @delivered = []
13
+ @failed = {}
14
+ @retryable = {}
15
+ @mutex = Mutex.new
16
+ end
17
+
18
+ def num_notifications
19
+ @notifications.size
20
+ end
21
+
22
+ def mark_retryable(notification, deliver_after)
23
+ if Rapns.config.batch_storage_updates
24
+ retryable[deliver_after] ||= []
25
+ retryable[deliver_after] << notification
26
+ else
27
+ Rapns::Daemon.store.mark_retryable(notification, deliver_after)
28
+ reflect(:notification_will_retry, notification)
29
+ end
30
+ end
31
+
32
+ def mark_delivered(notification)
33
+ if Rapns.config.batch_storage_updates
34
+ delivered << notification
35
+ else
36
+ Rapns::Daemon.store.mark_delivered(notification)
37
+ reflect(:notification_delivered, notification)
38
+ end
39
+ end
40
+
41
+ def mark_failed(notification, code, description)
42
+ if Rapns.config.batch_storage_updates
43
+ key = [code, description]
44
+ failed[key] ||= []
45
+ failed[key] << notification
46
+ else
47
+ Rapns::Daemon.store.mark_failed(notification, code, description)
48
+ reflect(:notification_failed, notification)
49
+ end
50
+ end
51
+
52
+ def notification_processed
53
+ @mutex.synchronize do
54
+ @num_processed += 1
55
+ complete if @num_processed >= @notifications.size
56
+ end
57
+ end
58
+
59
+ def complete?
60
+ @complete == true
61
+ end
62
+
63
+ def describe
64
+ notifications.map(&:id).join(', ')
65
+ end
66
+
67
+ private
68
+
69
+ def complete
70
+ [:complete_delivered, :complete_failed, :complete_retried].each do |method|
71
+ begin
72
+ send(method)
73
+ rescue StandardError => e
74
+ Rapns.logger.error(e)
75
+ reflect(:error, e)
76
+ end
77
+ end
78
+
79
+ notifications.clear
80
+ @complete = true
81
+ end
82
+
83
+ def complete_delivered
84
+ Rapns::Daemon.store.mark_batch_delivered(delivered)
85
+ delivered.each do |notification|
86
+ reflect(:notification_delivered, notification)
87
+ end
88
+ delivered.clear
89
+ end
90
+
91
+ def complete_failed
92
+ failed.each do |(code, description), notifications|
93
+ Rapns::Daemon.store.mark_batch_failed(notifications, code, description)
94
+ notifications.each do |notification|
95
+ reflect(:notification_failed, notification)
96
+ end
97
+ end
98
+ failed.clear
99
+ end
100
+
101
+ def complete_retried
102
+ retryable.each do |deliver_after, notifications|
103
+ Rapns::Daemon.store.mark_batch_retryable(notifications, deliver_after)
104
+ notifications.each do |notification|
105
+ reflect(:notification_will_retry, notification)
106
+ end
107
+ end
108
+ retryable.clear
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,23 @@
1
+ module Rapns
2
+ module Daemon
3
+ class Delivery
4
+ include Reflectable
5
+
6
+ def mark_retryable(notification, deliver_after)
7
+ @batch.mark_retryable(notification, deliver_after)
8
+ end
9
+
10
+ def mark_retryable_exponential(notification)
11
+ mark_retryable(notification, Time.now + 2 ** (notification.retries + 1))
12
+ end
13
+
14
+ def mark_delivered
15
+ @batch.mark_delivered(@notification)
16
+ end
17
+
18
+ def mark_failed(code, description)
19
+ @batch.mark_failed(@notification, code, description)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,19 @@
1
+ module Rapns
2
+ class DeliveryError < StandardError
3
+ attr_reader :code, :description
4
+
5
+ def initialize(code, notification_id, description)
6
+ @code = code
7
+ @notification_id = notification_id
8
+ @description = description
9
+ end
10
+
11
+ def to_s
12
+ message
13
+ end
14
+
15
+ def message
16
+ "Unable to deliver notification #{@notification_id}, received error #{@code} (#{@description})"
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,52 @@
1
+ module Rapns
2
+ module Daemon
3
+ class DeliveryHandler
4
+ include Reflectable
5
+
6
+ WAKEUP = :wakeup
7
+
8
+ attr_accessor :queue
9
+
10
+ def start
11
+ @thread = Thread.new do
12
+ loop do
13
+ handle_next_notification
14
+ break if @stop
15
+ end
16
+ end
17
+ end
18
+
19
+ def stop
20
+ @stop = true
21
+ end
22
+
23
+ def wakeup
24
+ queue.push(WAKEUP) if @thread
25
+ end
26
+
27
+ def wait
28
+ @thread.join if @thread
29
+ stopped
30
+ end
31
+
32
+ protected
33
+
34
+ def stopped
35
+ end
36
+
37
+ def handle_next_notification
38
+ notification, batch = queue.pop
39
+ return if notification == WAKEUP
40
+
41
+ begin
42
+ deliver(notification, batch)
43
+ rescue StandardError => e
44
+ Rapns.logger.error(e)
45
+ reflect(:error, e)
46
+ ensure
47
+ batch.notification_processed
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,33 @@
1
+ module Rapns
2
+ module Daemon
3
+ class DeliveryHandlerCollection
4
+ attr_reader :handlers
5
+
6
+ def initialize
7
+ @handlers = []
8
+ end
9
+
10
+ def push(handler)
11
+ @handlers << handler
12
+ end
13
+
14
+ def pop
15
+ handler = @handlers.pop
16
+ handler.stop
17
+ handler.wakeup
18
+ @handlers.map(&:wakeup)
19
+ handler.wait
20
+ end
21
+
22
+ def size
23
+ @handlers.size
24
+ end
25
+
26
+ def stop
27
+ @handlers.map(&:stop)
28
+ @handlers.map(&:wakeup)
29
+ @handlers.map(&:wait)
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,65 @@
1
+ module Rapns
2
+ module Daemon
3
+ class Feeder
4
+ extend Reflectable
5
+
6
+ def self.start
7
+ @stop = false
8
+
9
+ if Rapns.config.embedded
10
+ Thread.new { feed_forever }
11
+ elsif Rapns.config.push
12
+ enqueue_notifications
13
+ else
14
+ feed_forever
15
+ end
16
+ end
17
+
18
+ def self.stop
19
+ @stop = true
20
+ interrupt_sleep
21
+ end
22
+
23
+ def self.interrupt_sleep
24
+ interruptible_sleeper.interrupt_sleep
25
+ end
26
+
27
+ protected
28
+
29
+ def self.feed_forever
30
+ loop do
31
+ enqueue_notifications
32
+ interruptible_sleeper.sleep(Rapns.config.push_poll)
33
+ break if stop?
34
+ end
35
+ end
36
+
37
+ def self.stop?
38
+ @stop
39
+ end
40
+
41
+ def self.enqueue_notifications
42
+ begin
43
+ idle = Rapns::Daemon::AppRunner.idle.map(&:app)
44
+ return if idle.empty?
45
+ notifications = Rapns::Daemon.store.deliverable_notifications(idle)
46
+ Rapns::Daemon::AppRunner.enqueue(notifications)
47
+ rescue StandardError => e
48
+ Rapns.logger.error(e)
49
+ reflect(:error, e)
50
+ end
51
+ end
52
+
53
+ def self.interruptible_sleeper
54
+ unless @interruptible_sleeper
55
+ @interruptible_sleeper = InterruptibleSleep.new
56
+ if Rapns.config.udp_wake_host && Rapns.config.udp_wake_port
57
+ @interruptible_sleeper.enable_wake_on_udp Rapns.config.udp_wake_host, Rapns.config.udp_wake_port
58
+ end
59
+ end
60
+ @interruptible_sleeper
61
+ end
62
+
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,13 @@
1
+ module Rapns
2
+ module Daemon
3
+ module Gcm
4
+ class AppRunner < Rapns::Daemon::AppRunner
5
+ protected
6
+
7
+ def new_delivery_handler
8
+ DeliveryHandler.new(app)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end