rapns 3.0.1 → 3.1.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 (46) hide show
  1. data/CHANGELOG.md +10 -2
  2. data/README.md +26 -3
  3. data/bin/rapns +2 -7
  4. data/lib/generators/templates/add_gcm.rb +3 -1
  5. data/lib/generators/templates/rapns.rb +42 -11
  6. data/lib/rapns/apns/notification.rb +8 -2
  7. data/lib/rapns/app.rb +3 -3
  8. data/lib/rapns/configuration.rb +46 -13
  9. data/lib/rapns/daemon/apns/connection.rb +12 -9
  10. data/lib/rapns/daemon/apns/delivery_handler.rb +1 -1
  11. data/lib/rapns/daemon/apns/feedback_receiver.rb +6 -2
  12. data/lib/rapns/daemon/app_runner.rb +23 -7
  13. data/lib/rapns/daemon/delivery.rb +5 -1
  14. data/lib/rapns/daemon/delivery_handler.rb +4 -0
  15. data/lib/rapns/daemon/feeder.rb +26 -5
  16. data/lib/rapns/daemon/reflectable.rb +13 -0
  17. data/lib/rapns/daemon.rb +33 -22
  18. data/lib/rapns/embed.rb +28 -0
  19. data/lib/rapns/gcm/notification.rb +7 -2
  20. data/lib/rapns/gcm/payload_data_size_validator.rb +13 -0
  21. data/lib/rapns/gcm/registration_ids_count_validator.rb +13 -0
  22. data/lib/rapns/push.rb +12 -0
  23. data/lib/rapns/reflection.rb +44 -0
  24. data/lib/rapns/version.rb +1 -1
  25. data/lib/rapns.rb +11 -1
  26. data/spec/support/cert_with_password.pem +90 -0
  27. data/spec/support/cert_without_password.pem +59 -0
  28. data/spec/unit/apns/app_spec.rb +15 -1
  29. data/spec/unit/apns/notification_spec.rb +16 -1
  30. data/spec/unit/configuration_spec.rb +10 -1
  31. data/spec/unit/daemon/apns/connection_spec.rb +11 -2
  32. data/spec/unit/daemon/apns/delivery_handler_spec.rb +1 -1
  33. data/spec/unit/daemon/apns/delivery_spec.rb +10 -0
  34. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +16 -7
  35. data/spec/unit/daemon/delivery_handler_shared.rb +8 -0
  36. data/spec/unit/daemon/feeder_spec.rb +37 -6
  37. data/spec/unit/daemon/gcm/delivery_spec.rb +33 -1
  38. data/spec/unit/daemon/reflectable_spec.rb +27 -0
  39. data/spec/unit/daemon_spec.rb +55 -9
  40. data/spec/unit/embed_spec.rb +44 -0
  41. data/spec/unit/gcm/notification_spec.rb +9 -3
  42. data/spec/unit/push_spec.rb +28 -0
  43. data/spec/unit/reflection_spec.rb +34 -0
  44. data/spec/unit_spec_helper.rb +4 -62
  45. metadata +22 -5
  46. data/lib/rapns/gcm/payload_size_validator.rb +0 -13
data/CHANGELOG.md CHANGED
@@ -1,7 +1,15 @@
1
- ## 3.0.1 (Sun 16, 2012)
1
+ ## 3.1.0 (Jan 26, 2013)
2
+ * Rapns.reflect API for fine-grained introspection.
3
+ * Rapns.embed API for embedding Rapns into an existing process.
4
+ * Rapns.push API for using Rapns in scheduled jobs.
5
+ * Fix issue with integration with ActiveScaffold (#98) (@jeffarena).
6
+ * Fix content-available setter for APNs (#95) (@dup2).
7
+ * GCM validation fixes (#96) (@DianthuDia).
8
+
9
+ ## 3.0.1 (Dec 16, 2012)
2
10
  * Fix compatibility with Rails 3.0.x. Fixes #89.
3
11
 
4
- ## 3.0.0 (Sat 15, 2012)
12
+ ## 3.0.0 (Dec 15, 2012)
5
13
  * Add support for Google Cloud Messaging.
6
14
  * Fix Heroku logging issue.
7
15
 
data/README.md CHANGED
@@ -1,14 +1,18 @@
1
1
  [![Build Status](https://secure.travis-ci.org/ileitch/rapns.png?branch=master)](http://travis-ci.org/ileitch/rapns)
2
2
 
3
- ### Rapns - Professional grade APNs and GCM daemon
3
+ ### Rapns - Professional grade APNs and GCM for Ruby.
4
4
 
5
5
  * Supports both APNs (iOS) and GCM (Google Cloud Messaging, Android).
6
6
  * Seamless Rails integration.
7
7
  * Scalable - choose the number of threads each app spawns.
8
8
  * Designed for uptime - signal -HUP to add, update apps.
9
9
  * Stable - reconnects database and network connections when lost.
10
+ * Run as a daemon or inside an existing process.
11
+ * Use in a scheduler for low-workload deployments ([Push API](rapns/wiki/Push-API)).
12
+ * Reflection API for fine-grained instrumentation ([Reflection API](rapns/wiki/Relfection-API)).
10
13
  * Works with MRI, JRuby, Rubinius 1.8 and 1.9.
11
14
  * [Airbrake](http://airbrakeapp.com/) integration.
15
+ * Built with a love for Open Source :)
12
16
 
13
17
  #### 2.x users please read [upgrading from 2.x to 3.0](rapns/wiki/Upgrading-from-version-2.x-to-3.0)
14
18
 
@@ -36,11 +40,16 @@ Generate the migrations, rapns.yml and migrate:
36
40
  3. Select both the certificate and private key.
37
41
  4. Right click and select `Export 2 items...`.
38
42
  5. Save the file as `cert.p12`, make sure the File Format is `Personal Information Exchange (p12)`.
39
- 6. If you decide to set a password for your exported certificate, please read the 'Adding Apps' section below.
40
- 7. Convert the certificate to a .pem, where `<environment>` should be `development` or `production`, depending on the certificate you exported.
43
+ 6. Convert the certificate to a .pem, where `<environment>` should be `development` or `production`, depending on the certificate you exported.
44
+
45
+ Without a password:
41
46
 
42
47
  `openssl pkcs12 -nodes -clcerts -in cert.p12 -out <environment>.pem`
43
48
 
49
+ With a password:
50
+
51
+ `openssl pkcs12 -clcerts -in cert.p12 -out <environment>.pem`
52
+
44
53
  ## Create an App
45
54
 
46
55
  #### APNs
@@ -86,9 +95,17 @@ n.save!
86
95
 
87
96
  ## Starting Rapns
88
97
 
98
+ As a daemon:
99
+
89
100
  cd /path/to/rails/app
90
101
  rapns <Rails environment> [options]
91
102
 
103
+ Inside an existing process:
104
+
105
+ Rapns.embed
106
+
107
+ *Please note that only ever a single instance of Rapns should be running.*
108
+
92
109
  See [Configuration](rapns/wiki/Configuration) for a list of options, or run `rapns --help`.
93
110
 
94
111
  ## Updating Rapns
@@ -102,6 +119,9 @@ After updating you should run `rails g rapns` to check for any new migrations.
102
119
  * [Upgrading from 2.x to 3.0](rapns/wiki/Upgrading-from-version-2.x-to-3.0)
103
120
  * [Deploying to Heroku](rapns/wiki/Heroku)
104
121
  * [Hot App Updates](rapns/wiki/Hot-App-Updates)
122
+ * [Reflection API](rapns/wiki/Reflection-API)
123
+ * [Push API](rapns/wiki/Push-API)
124
+ * [Embedding API](rapns/wiki/Embedding-API)
105
125
 
106
126
  ### APNs
107
127
  * [Advanced APNs Features](rapns/wiki/Advanced-APNs-Features)
@@ -138,3 +158,6 @@ Thank you to the following wonderful people for contributing:
138
158
  * [@adorr](https://github.com/adorr)
139
159
  * [@mattconnolly](https://github.com/mattconnolly)
140
160
  * [@emeitch](https://github.com/emeitch)
161
+ * [@jeffarena](https://github.com/jeffarena)
162
+ * [@DianthuDia](https://github.com/DianthuDia)
163
+ * [@dup2](https://github.com/dup2)
data/bin/rapns CHANGED
@@ -11,9 +11,7 @@ if environment.nil? || environment =~ /^-/
11
11
  exit 1
12
12
  end
13
13
 
14
- # A mock configuration to be used before the Rails environment is loaded.
15
- class CommandlineConfig < Struct.new(*Rapns::CONFIG_ATTRS); end
16
- config = CommandlineConfig.new
14
+ config = Rapns::ConfigurationWithoutDefaults.new
17
15
 
18
16
  ARGV.options do |opts|
19
17
  opts.banner = banner
@@ -34,8 +32,5 @@ load 'config/environment.rb'
34
32
  load 'config/initializers/rapns.rb' if File.exist?('config/initializers/rapns.rb')
35
33
 
36
34
  Rapns.config.update(config)
37
-
38
- require 'rapns/daemon'
39
- require 'rapns/patches'
40
-
35
+ Rapns.require_for_daemon
41
36
  Rapns::Daemon.start
@@ -34,7 +34,9 @@ class AddGcm < ActiveRecord::Migration
34
34
 
35
35
  add_column :rapns_notifications, :collapse_key, :string, :null => true
36
36
  add_column :rapns_notifications, :delay_while_idle, :boolean, :null => false, :default => false
37
- add_column :rapns_notifications, :registration_ids, :text, :null => true
37
+
38
+ reg_ids_type = ActiveRecord::Base.connection.adapter_name.include?('Mysql') ? :mediumtext : :text
39
+ add_column :rapns_notifications, :registration_ids, reg_ids_type, :null => true
38
40
  add_column :rapns_notifications, :app_id, :integer, :null => true
39
41
  add_column :rapns_notifications, :retries, :integer, :null => true, :default => 0
40
42
 
@@ -23,17 +23,48 @@
23
23
  # Path to write PID file. Relative to Rails root unless absolute.
24
24
  # config.pid_file = '/path/to/rapns.pid'
25
25
 
26
- # Define a block that will be called with a Rapns::Apns::Feedback instance
27
- # when feedback is received from the APNs that a notification has
28
- # failed to be delivered. Further notifications should not be sent to the device.
26
+ end
27
+
28
+ Rapns.reflect do |on|
29
+
30
+ # Called with a Rapns::Apns::Feedback instance when feedback is received
31
+ # from the APNs that a notification has failed to be delivered.
32
+ # Further notifications should not be sent to the device.
33
+ # on.apns_feedback do |feedback|
34
+ # end
35
+
36
+ # Called when a notification is queued internally for delivery.
37
+ # The internal queue for each app runner can be inspected:
29
38
  #
30
- # Example:
31
- # config.on_apns_feedback do |feedback|
32
- # device = Device.find_by_device_token(feedback.device_token)
33
- # if device
34
- # device.active = false
35
- # device.save!
36
- # end
39
+ # Rapns::Daemon::AppRunner.runners.each do |app_id, runner|
40
+ # runner.app
41
+ # runner.queue_size
42
+ # end
43
+ #
44
+ # on.notification_enqueued do |notification|
37
45
  # end
38
46
 
39
- end
47
+ # Called when a notification is successfully delivered.
48
+ # on.notification_delivered do |notification|
49
+ # end
50
+
51
+ # Called when notification delivery failed.
52
+ # Call 'error_code' and 'error_description' on the notification for the cause.
53
+ # on.notification_failed do |notification|
54
+ # end
55
+
56
+ # Called when a notification will be retried at a later date.
57
+ # Call 'deliver_after' on the notification for the next delivery date
58
+ # and 'retries' for the number of times this notification has been retried.
59
+ # on.notification_will_retry do |notification|
60
+ # end
61
+
62
+ # Called when an APNs connection is lost and will be reconnected.
63
+ # on.apns_connection_lost do |app, error|
64
+ # end
65
+
66
+ # Called when an exception is raised.
67
+ # on.error do |error|
68
+ # end
69
+
70
+ end
@@ -43,13 +43,13 @@ module Rapns
43
43
 
44
44
  MDM_KEY = '__rapns_mdm__'
45
45
  def mdm=(magic)
46
- self.attributes_for_device = { MDM_KEY => magic }
46
+ self.attributes_for_device = (attributes_for_device || {}).merge({ MDM_KEY => magic })
47
47
  end
48
48
 
49
49
  CONTENT_AVAILABLE_KEY = '__rapns_content_available__'
50
50
  def content_available=(bool)
51
51
  return unless bool
52
- self.attributes_for_device = { CONTENT_AVAILABLE_KEY => true }
52
+ self.attributes_for_device = (attributes_for_device || {}).merge({ CONTENT_AVAILABLE_KEY => true })
53
53
  end
54
54
 
55
55
  def as_json
@@ -81,6 +81,12 @@ module Rapns
81
81
  [1, id_for_pack, expiry, 0, 32, device_token, payload_size, payload].pack("cNNccH*na*")
82
82
  end
83
83
 
84
+ def data=(attrs)
85
+ return unless attrs
86
+ raise ArgumentError, "must be a Hash" if !attrs.is_a?(Hash)
87
+ super attrs.merge(data || {})
88
+ end
89
+
84
90
  end
85
91
  end
86
92
  end
data/lib/rapns/app.rb CHANGED
@@ -4,7 +4,7 @@ module Rapns
4
4
 
5
5
  attr_accessible :name, :environment, :certificate, :password, :connections, :auth_key
6
6
 
7
- has_many :notifications
7
+ has_many :notifications, :class_name => 'Rapns::Notification'
8
8
 
9
9
  validates :name, :presence => true, :uniqueness => { :scope => [:type, :environment] }
10
10
  validates_numericality_of :connections, :greater_than => 0, :only_integer => true
@@ -16,8 +16,8 @@ module Rapns
16
16
  def certificate_has_matching_private_key
17
17
  result = false
18
18
  if certificate.present?
19
- x509 = OpenSSL::X509::Certificate.new certificate rescue nil
20
- pkey = OpenSSL::PKey::RSA.new certificate rescue nil
19
+ x509 = OpenSSL::X509::Certificate.new(certificate) rescue nil
20
+ pkey = OpenSSL::PKey::RSA.new(certificate, password) rescue nil
21
21
  result = !x509.nil? && !pkey.nil?
22
22
  unless result
23
23
  errors.add :certificate, 'Certificate value must contain a certificate and a private key.'
@@ -7,21 +7,21 @@ module Rapns
7
7
  yield config if block_given?
8
8
  end
9
9
 
10
- CONFIG_ATTRS = [:foreground, :push_poll, :feedback_poll,
11
- :airbrake_notify, :check_for_errors, :pid_file, :batch_size]
10
+ CONFIG_ATTRS = [:foreground, :push_poll, :feedback_poll, :embedded,
11
+ :airbrake_notify, :check_for_errors, :pid_file, :batch_size,
12
+ :push]
13
+
14
+ class ConfigurationWithoutDefaults < Struct.new(*CONFIG_ATTRS)
15
+ end
12
16
 
13
17
  class Configuration < Struct.new(*CONFIG_ATTRS)
18
+ include Deprecatable
19
+
14
20
  attr_accessor :apns_feedback_callback
15
21
 
16
22
  def initialize
17
23
  super
18
-
19
- self.foreground = false
20
- self.push_poll = 2
21
- self.feedback_poll = 60
22
- self.airbrake_notify = true
23
- self.check_for_errors = true
24
- self.batch_size = 5000
24
+ set_defaults
25
25
  end
26
26
 
27
27
  def update(other)
@@ -31,10 +31,6 @@ module Rapns
31
31
  end
32
32
  end
33
33
 
34
- def on_apns_feedback(&block)
35
- self.apns_feedback_callback = block
36
- end
37
-
38
34
  def pid_file=(path)
39
35
  if path && !Pathname.new(path).absolute?
40
36
  super(File.join(Rails.root, path))
@@ -42,5 +38,42 @@ module Rapns
42
38
  super
43
39
  end
44
40
  end
41
+
42
+ def foreground=(bool)
43
+ if defined? JRUBY_VERSION
44
+ # The JVM does not support fork().
45
+ super(true)
46
+ else
47
+ super
48
+ end
49
+ end
50
+
51
+ def on_apns_feedback(&block)
52
+ self.apns_feedback_callback = block
53
+ end
54
+ deprecated(:on_apns_feedback, 3.2, "Please use the Rapns.reflect API instead.")
55
+
56
+ private
57
+
58
+ def set_defaults
59
+ if defined? JRUBY_VERSION
60
+ # The JVM does not support fork().
61
+ self.foreground = true
62
+ else
63
+ self.foreground = false
64
+ end
65
+
66
+ self.push_poll = 2
67
+ self.feedback_poll = 60
68
+ self.airbrake_notify = true
69
+ self.check_for_errors = true
70
+ self.batch_size = 5000
71
+ self.pid_file = nil
72
+ self.apns_feedback_callback = nil
73
+
74
+ # Internal options.
75
+ self.embedded = false
76
+ self.push = false
77
+ end
45
78
  end
46
79
  end
@@ -4,18 +4,20 @@ module Rapns
4
4
  class ConnectionError < StandardError; end
5
5
 
6
6
  class Connection
7
+ include Reflectable
8
+
7
9
  attr_accessor :last_write
8
10
 
9
11
  def self.idle_period
10
12
  30.minutes
11
13
  end
12
14
 
13
- def initialize(name, host, port, certificate, password)
14
- @name = name
15
+ def initialize(app, host, port)
16
+ @app = app
15
17
  @host = host
16
18
  @port = port
17
- @certificate = certificate
18
- @password = password
19
+ @certificate = app.certificate
20
+ @password = app.password
19
21
  written
20
22
  end
21
23
 
@@ -51,7 +53,8 @@ module Rapns
51
53
  retry_count += 1;
52
54
 
53
55
  if retry_count == 1
54
- Rapns::Daemon.logger.error("[#{@name}] Lost connection to #{@host}:#{@port} (#{e.class.name}), reconnecting...")
56
+ Rapns::Daemon.logger.error("[#{@app.name}] Lost connection to #{@host}:#{@port} (#{e.class.name}), reconnecting...")
57
+ reflect(:apns_connection_lost, @app, e)
55
58
  end
56
59
 
57
60
  if retry_count <= 3
@@ -59,7 +62,7 @@ module Rapns
59
62
  sleep 1
60
63
  retry
61
64
  else
62
- raise ConnectionError, "#{@name} tried #{retry_count-1} times to reconnect but failed (#{e.class.name})."
65
+ raise ConnectionError, "#{@app.name} tried #{retry_count-1} times to reconnect but failed (#{e.class.name})."
63
66
  end
64
67
  end
65
68
  end
@@ -72,7 +75,7 @@ module Rapns
72
75
  protected
73
76
 
74
77
  def reconnect_idle
75
- Rapns::Daemon.logger.info("[#{@name}] Idle period exceeded, reconnecting...")
78
+ Rapns::Daemon.logger.info("[#{@app.name}] Idle period exceeded, reconnecting...")
76
79
  reconnect
77
80
  end
78
81
 
@@ -104,10 +107,10 @@ module Rapns
104
107
  ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, @ssl_context)
105
108
  ssl_socket.sync = true
106
109
  ssl_socket.connect
107
- Rapns::Daemon.logger.info("[#{@name}] Connected to #{@host}:#{@port}")
110
+ Rapns::Daemon.logger.info("[#{@app.name}] Connected to #{@host}:#{@port}")
108
111
  [tcp_socket, ssl_socket]
109
112
  end
110
113
  end
111
114
  end
112
115
  end
113
- end
116
+ end
@@ -4,7 +4,7 @@ module Rapns
4
4
  class DeliveryHandler < Rapns::Daemon::DeliveryHandler
5
5
  def initialize(app, host, port)
6
6
  @app = app
7
- @connection = Connection.new(@app.name, host, port, @app.certificate, @app.password)
7
+ @connection = Connection.new(@app, host, port)
8
8
  @connection.connect
9
9
  end
10
10
 
@@ -2,6 +2,7 @@ module Rapns
2
2
  module Daemon
3
3
  module Apns
4
4
  class FeedbackReceiver
5
+ include Reflectable
5
6
  include InterruptibleSleep
6
7
  include DatabaseReconnectable
7
8
 
@@ -35,7 +36,7 @@ module Rapns
35
36
  def check_for_feedback
36
37
  connection = nil
37
38
  begin
38
- connection = Connection.new("FeedbackReceiver:#{@app.name}", @host, @port, @certificate, @password)
39
+ connection = Connection.new(@app, @host, @port)
39
40
  connection.connect
40
41
 
41
42
  while tuple = connection.read(FEEDBACK_TUPLE_BYTES)
@@ -59,8 +60,11 @@ module Rapns
59
60
  def create_feedback(failed_at, device_token)
60
61
  formatted_failed_at = failed_at.strftime("%Y-%m-%d %H:%M:%S UTC")
61
62
  with_database_reconnect_and_retry do
62
- Rapns::Daemon.logger.info("[FeedbackReceiver:#{@app.name}] Delivery failed at #{formatted_failed_at} for #{device_token}")
63
+ Rapns::Daemon.logger.info("[#{@app.name}] [FeedbackReceiver] Delivery failed at #{formatted_failed_at} for #{device_token}.")
63
64
  feedback = Rapns::Apns::Feedback.create!(:failed_at => failed_at, :device_token => device_token, :app => @app)
65
+ reflect(:apns_feedback, feedback)
66
+
67
+ # Deprecated.
64
68
  begin
65
69
  Rapns.config.apns_feedback_callback.call(feedback) if Rapns.config.apns_feedback_callback
66
70
  rescue StandardError => e
@@ -2,7 +2,7 @@ module Rapns
2
2
  module Daemon
3
3
  class AppRunner
4
4
  class << self
5
- attr_reader :runners # TODO: Needed?
5
+ attr_reader :runners
6
6
  end
7
7
 
8
8
  @runners = {}
@@ -86,20 +86,28 @@ module Rapns
86
86
  diff = handlers.size - app.connections
87
87
  return if diff == 0
88
88
  if diff > 0
89
- diff.times { handlers.pop.stop }
90
- Rapns::Daemon.logger.info("[#{app.name}] Terminated #{handlers_str(diff)}. #{handlers_str} remaining.")
89
+ diff.times { decrement_handlers }
90
+ Rapns::Daemon.logger.info("[#{app.name}] Stopped #{handlers_str(diff)}. #{handlers_str} remaining.")
91
91
  else
92
- diff.abs.times { handlers << start_handler }
93
- Rapns::Daemon.logger.info("[#{app.name}] Added #{handlers_str(diff)}. #{handlers_str} remaining.")
92
+ diff.abs.times { increment_handlers }
93
+ Rapns::Daemon.logger.info("[#{app.name}] Started #{handlers_str(diff)}. #{handlers_str} remaining.")
94
94
  end
95
95
  end
96
96
 
97
+ def decrement_handlers
98
+ handlers.pop.stop
99
+ end
100
+
101
+ def increment_handlers
102
+ handlers << start_handler
103
+ end
104
+
97
105
  def debug
98
106
  Rapns::Daemon.logger.info <<-EOS
99
107
 
100
108
  #{@app.name}:
101
- handlers: #{handlers.size}
102
- queued: #{queue.size}
109
+ handlers: #{num_handlers}
110
+ queued: #{queue_size}
103
111
  idle: #{idle?}
104
112
  EOS
105
113
  end
@@ -108,6 +116,14 @@ module Rapns
108
116
  queue.notifications_processed?
109
117
  end
110
118
 
119
+ def queue_size
120
+ queue.size
121
+ end
122
+
123
+ def num_handlers
124
+ handlers.size
125
+ end
126
+
111
127
  protected
112
128
 
113
129
  def start_handler
@@ -2,6 +2,7 @@ module Rapns
2
2
  module Daemon
3
3
  class Delivery
4
4
  include DatabaseReconnectable
5
+ include Reflectable
5
6
 
6
7
  def self.perform(*args)
7
8
  new(*args).perform
@@ -13,6 +14,7 @@ module Rapns
13
14
  notification.deliver_after = deliver_after
14
15
  notification.save!(:validate => false)
15
16
  end
17
+ reflect(:notification_will_retry, notification)
16
18
  end
17
19
 
18
20
  def retry_exponentially(notification)
@@ -25,6 +27,7 @@ module Rapns
25
27
  @notification.delivered_at = Time.now
26
28
  @notification.save!(:validate => false)
27
29
  end
30
+ reflect(:notification_delivered, @notification)
28
31
  end
29
32
 
30
33
  def mark_failed(code, description)
@@ -37,7 +40,8 @@ module Rapns
37
40
  @notification.error_description = description
38
41
  @notification.save!(:validate => false)
39
42
  end
43
+ reflect(:notification_failed, @notification)
40
44
  end
41
45
  end
42
46
  end
43
- end
47
+ end
@@ -1,6 +1,8 @@
1
1
  module Rapns
2
2
  module Daemon
3
3
  class DeliveryHandler
4
+ include Reflectable
5
+
4
6
  attr_accessor :queue
5
7
 
6
8
  def start
@@ -35,8 +37,10 @@ module Rapns
35
37
 
36
38
  begin
37
39
  deliver(notification)
40
+ reflect(:notification_delivered, notification)
38
41
  rescue StandardError => e
39
42
  Rapns::Daemon.logger.error(e)
43
+ reflect(:error, e)
40
44
  ensure
41
45
  queue.notification_processed
42
46
  end
@@ -3,12 +3,17 @@ module Rapns
3
3
  class Feeder
4
4
  extend InterruptibleSleep
5
5
  extend DatabaseReconnectable
6
+ extend Reflectable
6
7
 
7
- def self.start(poll)
8
- loop do
8
+ def self.start
9
+ @stop = false
10
+
11
+ if Rapns.config.embedded
12
+ Thread.new { feed_forever }
13
+ elsif Rapns.config.push
9
14
  enqueue_notifications
10
- interruptible_sleep poll
11
- break if @stop
15
+ else
16
+ feed_forever
12
17
  end
13
18
  end
14
19
 
@@ -19,17 +24,33 @@ module Rapns
19
24
 
20
25
  protected
21
26
 
27
+ def self.feed_forever
28
+ loop do
29
+ enqueue_notifications
30
+ interruptible_sleep(Rapns.config.push_poll)
31
+ break if stop?
32
+ end
33
+ end
34
+
35
+ def self.stop?
36
+ @stop
37
+ end
38
+
22
39
  def self.enqueue_notifications
23
40
  begin
24
41
  with_database_reconnect_and_retry do
25
42
  batch_size = Rapns.config.batch_size
26
43
  idle = Rapns::Daemon::AppRunner.idle.map(&:app)
27
- Rapns::Notification.ready_for_delivery.for_apps(idle).limit(batch_size).each do |notification|
44
+ relation = Rapns::Notification.ready_for_delivery.for_apps(idle)
45
+ relation = relation.limit(batch_size) unless Rapns.config.push
46
+ relation.each do |notification|
28
47
  Rapns::Daemon::AppRunner.enqueue(notification)
48
+ reflect(:notification_enqueued, notification)
29
49
  end
30
50
  end
31
51
  rescue StandardError => e
32
52
  Rapns::Daemon.logger.error(e)
53
+ reflect(:error, e)
33
54
  end
34
55
  end
35
56
  end
@@ -0,0 +1,13 @@
1
+ module Rapns
2
+ module Daemon
3
+ module Reflectable
4
+ def reflect(name, *args)
5
+ begin
6
+ Rapns.reflections.__dispatch(name, *args)
7
+ rescue StandardError => e
8
+ Rapns::Daemon.logger.error(e)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end