rpush 2.3.2 → 2.4.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 (69) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +1 -1
  4. data/lib/generators/rpush_migration_generator.rb +21 -6
  5. data/lib/generators/templates/rpush.rb +5 -5
  6. data/lib/generators/templates/rpush_2_0_0_updates.rb +24 -0
  7. data/lib/rpush/client/active_model/apns/notification.rb +1 -1
  8. data/lib/rpush/client/mongoid.rb +31 -0
  9. data/lib/rpush/client/mongoid/adm/app.rb +14 -0
  10. data/lib/rpush/client/mongoid/adm/notification.rb +11 -0
  11. data/lib/rpush/client/mongoid/apns/app.rb +11 -0
  12. data/lib/rpush/client/mongoid/apns/feedback.rb +21 -0
  13. data/lib/rpush/client/mongoid/apns/notification.rb +15 -0
  14. data/lib/rpush/client/mongoid/app.rb +23 -0
  15. data/lib/rpush/client/mongoid/gcm/app.rb +11 -0
  16. data/lib/rpush/client/mongoid/gcm/notification.rb +11 -0
  17. data/lib/rpush/client/mongoid/notification.rb +43 -0
  18. data/lib/rpush/client/mongoid/wpns/app.rb +11 -0
  19. data/lib/rpush/client/mongoid/wpns/notification.rb +11 -0
  20. data/lib/rpush/client/redis.rb +2 -2
  21. data/lib/rpush/configuration.rb +48 -29
  22. data/lib/rpush/daemon/adm/delivery.rb +1 -1
  23. data/lib/rpush/daemon/apns.rb +1 -1
  24. data/lib/rpush/daemon/apns/feedback_receiver.rb +2 -3
  25. data/lib/rpush/daemon/dispatcher/apns_tcp.rb +2 -1
  26. data/lib/rpush/daemon/feeder.rb +4 -7
  27. data/lib/rpush/daemon/gcm/delivery.rb +1 -1
  28. data/lib/rpush/daemon/interruptible_sleep.rb +5 -50
  29. data/lib/rpush/daemon/proc_title.rb +2 -1
  30. data/lib/rpush/daemon/store/active_record.rb +4 -0
  31. data/lib/rpush/daemon/store/interface.rb +1 -1
  32. data/lib/rpush/daemon/store/mongoid.rb +157 -0
  33. data/lib/rpush/daemon/store/redis.rb +6 -2
  34. data/lib/rpush/deprecatable.rb +1 -2
  35. data/lib/rpush/deprecation.rb +6 -0
  36. data/lib/rpush/embed.rb +5 -0
  37. data/lib/rpush/logger.rb +5 -8
  38. data/lib/rpush/push.rb +5 -0
  39. data/lib/rpush/version.rb +1 -1
  40. data/lib/tasks/quality.rake +1 -1
  41. data/lib/tasks/test.rake +9 -4
  42. data/spec/functional/apns_spec.rb +2 -1
  43. data/spec/functional_spec_helper.rb +2 -2
  44. data/spec/spec_helper.rb +18 -7
  45. data/spec/support/config/mongoid.yml +69 -0
  46. data/spec/support/mongoid_setup.rb +10 -0
  47. data/spec/unit/client/active_record/adm/app_spec.rb +1 -1
  48. data/spec/unit/client/active_record/adm/notification_spec.rb +1 -1
  49. data/spec/unit/client/active_record/apns/app_spec.rb +1 -1
  50. data/spec/unit/client/active_record/apns/feedback_spec.rb +1 -1
  51. data/spec/unit/client/active_record/apns/notification_spec.rb +11 -11
  52. data/spec/unit/client/active_record/app_spec.rb +1 -1
  53. data/spec/unit/client/active_record/gcm/notification_spec.rb +1 -1
  54. data/spec/unit/client/active_record/notification_spec.rb +1 -1
  55. data/spec/unit/client/active_record/wpns/notification_spec.rb +1 -1
  56. data/spec/unit/configuration_spec.rb +7 -0
  57. data/spec/unit/daemon/apns/feedback_receiver_spec.rb +5 -5
  58. data/spec/unit/daemon/feeder_spec.rb +2 -2
  59. data/spec/unit/daemon/proc_title_spec.rb +11 -0
  60. data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +1 -1
  61. data/spec/unit/daemon/store/active_record_spec.rb +21 -12
  62. data/spec/unit/daemon/store/mongoid_spec.rb +339 -0
  63. data/spec/unit/daemon/store/redis_spec.rb +365 -0
  64. data/spec/unit/embed_spec.rb +4 -2
  65. data/spec/unit/logger_spec.rb +14 -5
  66. data/spec/unit/notification_shared.rb +1 -1
  67. data/spec/unit/push_spec.rb +4 -2
  68. data/spec/unit_spec_helper.rb +3 -3
  69. metadata +25 -2
@@ -151,7 +151,7 @@ module Rpush
151
151
  end
152
152
 
153
153
  def create_new_notification(response, registration_ids)
154
- attrs = @notification.attributes.slice('app_id', 'collapse_key', 'delay_while_idle')
154
+ attrs = { 'app_id' => @notification.app_id, 'collapse_key' => @notification.collapse_key, 'delay_while_idle' => @notification.delay_while_idle }
155
155
  Rpush::Daemon.store.create_adm_notification(attrs, @notification.data, registration_ids, deliver_after_header(response), @notification.app)
156
156
  end
157
157
 
@@ -11,7 +11,7 @@ module Rpush
11
11
 
12
12
  batch_deliveries true
13
13
  dispatcher :apns_tcp, host: proc { |app| HOSTS[app.environment.to_sym] }
14
- loops Rpush::Daemon::Apns::FeedbackReceiver, if: -> { !Rpush.config.push }
14
+ loops Rpush::Daemon::Apns::FeedbackReceiver, if: -> { Rpush.config.apns.feedback_receiver.enabled && !Rpush.config.push }
15
15
  end
16
16
  end
17
17
  end
@@ -19,19 +19,18 @@ module Rpush
19
19
  @host, @port = HOSTS[@app.environment.to_sym]
20
20
  @certificate = app.certificate
21
21
  @password = app.password
22
- @interruptible_sleep = InterruptibleSleep.new(Rpush.config.feedback_poll)
22
+ @interruptible_sleep = InterruptibleSleep.new
23
23
  end
24
24
 
25
25
  def start
26
26
  return if Rpush.config.push
27
27
  Rpush.logger.info("[#{@app.name}] Starting feedback receiver... ", true)
28
- @interruptible_sleep.start
29
28
 
30
29
  @thread = Thread.new do
31
30
  loop do
32
31
  break if @stop
33
32
  check_for_feedback
34
- @interruptible_sleep.sleep
33
+ @interruptible_sleep.sleep(Rpush.config.apns.feedback_receiver.frequency)
35
34
  end
36
35
 
37
36
  Rpush::Daemon.store.release_connection
@@ -63,7 +63,7 @@ module Rpush
63
63
  # On Linux, select returns nil from a dropped connection.
64
64
  # On OS X, Errno::EBADF is raised following a Errno::EADDRNOTAVAIL from the write call.
65
65
  return unless @connection.select(SELECT_TIMEOUT)
66
- rescue SystemCallError
66
+ rescue SystemCallError, IOError
67
67
  # Connection closed.
68
68
  return
69
69
  end
@@ -93,6 +93,7 @@ module Rpush
93
93
  end
94
94
 
95
95
  def handle_error(code, notification_id)
96
+ notification_id = Rpush::Daemon.store.translate_integer_notification_id(notification_id)
96
97
  failed_pos = delivered_buffer.index(notification_id)
97
98
  description = APNS_ERRORS[code.to_i] || "Unknown error code #{code.inspect}. Possible Rpush bug?"
98
99
  log_error(description + " (#{code})")
@@ -21,7 +21,7 @@ module Rpush
21
21
  end
22
22
 
23
23
  def self.wakeup
24
- interruptible_sleeper.wakeup
24
+ interruptible_sleeper.stop
25
25
  end
26
26
 
27
27
  class << self
@@ -35,8 +35,8 @@ module Rpush
35
35
  def self.feed_forever
36
36
  loop do
37
37
  enqueue_notifications
38
- interruptible_sleeper.sleep
39
- break if should_stop
38
+ interruptible_sleeper.sleep(Rpush.config.push_poll)
39
+ return if should_stop
40
40
  end
41
41
  end
42
42
 
@@ -51,10 +51,7 @@ module Rpush
51
51
  end
52
52
 
53
53
  def self.interruptible_sleeper
54
- return @interruptible_sleeper if @interruptible_sleeper
55
- @interruptible_sleeper = InterruptibleSleep.new(Rpush.config.push_poll)
56
- @interruptible_sleeper.start
57
- @interruptible_sleeper
54
+ @interruptible_sleeper ||= InterruptibleSleep.new
58
55
  end
59
56
  end
60
57
  end
@@ -100,7 +100,7 @@ module Rpush
100
100
  end
101
101
 
102
102
  def create_new_notification(response, unavailable_idxs)
103
- attrs = @notification.attributes.slice('app_id', 'collapse_key', 'delay_while_idle')
103
+ attrs = { 'app_id' => @notification.app_id, 'collapse_key' => @notification.collapse_key, 'delay_while_idle' => @notification.delay_while_idle }
104
104
  registration_ids = @notification.registration_ids.values_at(*unavailable_idxs)
105
105
  Rpush::Daemon.store.create_gcm_notification(attrs, @notification.data,
106
106
  registration_ids, deliver_after_header(response), @notification.app)
@@ -3,60 +3,15 @@ require 'monitor'
3
3
  module Rpush
4
4
  module Daemon
5
5
  class InterruptibleSleep
6
- def initialize(duration)
7
- @duration = duration
8
- @stop = false
9
-
10
- @wakeup_obj = Object.new
11
- @wakeup_obj.extend(MonitorMixin)
12
- @wakeup_condition = @wakeup_obj.new_cond
13
-
14
- @sleep_obj = Object.new
15
- @sleep_obj.extend(MonitorMixin)
16
- @sleep_condition = @sleep_obj.new_cond
17
- end
18
-
19
- def sleep
20
- return if @stop
21
- goto_sleep
22
- wait_for_wakeup
23
- end
24
-
25
- def start
26
- @stop = false
27
-
28
- @thread = Thread.new do
29
- loop do
30
- wait_for_sleeper
31
- break if @stop
32
- Kernel.sleep(@duration)
33
- wakeup
34
- end
35
- end
6
+ def sleep(duration)
7
+ @thread = Thread.new { Kernel.sleep duration }
8
+ Thread.pass
9
+ @thread.join
36
10
  end
37
11
 
38
12
  def stop
39
- @stop = true
40
- wakeup
41
13
  @thread.kill if @thread
42
- end
43
-
44
- def wakeup
45
- @wakeup_obj.synchronize { @wakeup_condition.signal }
46
- end
47
-
48
- private
49
-
50
- def goto_sleep
51
- @sleep_obj.synchronize { @sleep_condition.signal }
52
- end
53
-
54
- def wait_for_wakeup
55
- @wakeup_obj.synchronize { @wakeup_condition.wait(@duration * 2) }
56
- end
57
-
58
- def wait_for_sleeper
59
- @sleep_obj.synchronize { @sleep_condition.wait }
14
+ rescue StandardError # rubocop:disable Lint/HandleExceptions
60
15
  end
61
16
  end
62
17
  end
@@ -2,7 +2,8 @@ module Rpush
2
2
  module Daemon
3
3
  class ProcTitle
4
4
  def self.update
5
- $0 = proc_title
5
+ return if Rpush.config.embedded || Rpush.config.push
6
+ Process.respond_to?(:setproctitle) ? Process.setproctitle(proc_title) : $0 = proc_title
6
7
  end
7
8
 
8
9
  def self.proc_title
@@ -169,6 +169,10 @@ module Rpush
169
169
  ready_for_delivery.count
170
170
  end
171
171
 
172
+ def translate_integer_notification_id(id)
173
+ id
174
+ end
175
+
172
176
  private
173
177
 
174
178
  def create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) # rubocop:disable ParameterLists
@@ -8,7 +8,7 @@ module Rpush
8
8
  :create_gcm_notification, :create_adm_notification, :update_app,
9
9
  :update_notification, :release_connection,
10
10
  :all_apps, :app, :mark_ids_failed, :mark_ids_retryable,
11
- :reopen_log, :pending_delivery_count]
11
+ :reopen_log, :pending_delivery_count, :translate_integer_notification_id]
12
12
 
13
13
  def self.check(klass)
14
14
  missing = PUBLIC_METHODS - klass.instance_methods.map(&:to_sym)
@@ -0,0 +1,157 @@
1
+ module Rpush
2
+ module Daemon
3
+ module Store
4
+ class Mongoid
5
+ DEFAULT_MARK_OPTIONS = { persist: true }
6
+
7
+ def app(app_id)
8
+ Rpush::Client::Mongoid::App.find(app_id)
9
+ end
10
+
11
+ def all_apps
12
+ Rpush::Client::Mongoid::App.all
13
+ end
14
+
15
+ def deliverable_notifications(limit)
16
+ relation = ready_for_delivery.limit(limit)
17
+ claim_notifications(relation)
18
+ end
19
+
20
+ def mark_delivered(notification, time, opts = {})
21
+ opts = DEFAULT_MARK_OPTIONS.dup.merge(opts)
22
+ notification.delivered = true
23
+ notification.delivered_at = time
24
+ notification.save!(validate: false) if opts[:persist]
25
+ end
26
+
27
+ def mark_batch_delivered(notifications)
28
+ return if notifications.empty?
29
+
30
+ now = Time.now
31
+ ids = []
32
+ notifications.each do |n|
33
+ mark_delivered(n, now, persist: false)
34
+ ids << n.id
35
+ end
36
+ Rpush::Client::Mongoid::Notification.in(id: ids).update_all(processing: false, delivered: true, delivered_at: now)
37
+ end
38
+
39
+ def mark_failed(notification, code, description, time, opts = {})
40
+ opts = DEFAULT_MARK_OPTIONS.dup.merge(opts)
41
+ notification.delivered = false
42
+ notification.delivered_at = nil
43
+ notification.failed = true
44
+ notification.failed_at = time
45
+ notification.error_code = code
46
+ notification.error_description = description
47
+ notification.save!(validate: false) if opts[:persist]
48
+ end
49
+
50
+ def mark_batch_failed(notifications, code, description)
51
+ now = Time.now
52
+ ids = []
53
+ notifications.each do |n|
54
+ mark_failed(n, code, description, now, persist: false)
55
+ ids << n.id
56
+ end
57
+ mark_ids_failed(ids, code, description, now)
58
+ end
59
+
60
+ def mark_ids_failed(ids, code, description, time)
61
+ return if ids.empty?
62
+
63
+ Rpush::Client::Mongoid::Notification.in(id: ids).update_all(processing: false, delivered: false, delivered_at: nil, failed: true, failed_at: time, error_code: code, error_description: description)
64
+ end
65
+
66
+ def mark_retryable(notification, deliver_after, opts = {})
67
+ opts = DEFAULT_MARK_OPTIONS.dup.merge(opts)
68
+ notification.delivered = false
69
+ notification.delivered_at = nil
70
+ notification.failed = false
71
+ notification.failed_at = nil
72
+ notification.retries += 1
73
+ notification.deliver_after = deliver_after
74
+
75
+ return unless opts[:persist]
76
+
77
+ notification.save!(validate: false)
78
+ end
79
+
80
+ def mark_batch_retryable(notifications, deliver_after)
81
+ ids = []
82
+ notifications.each do |n|
83
+ mark_retryable(n, deliver_after, persist: false)
84
+ ids << n.id
85
+ end
86
+ mark_ids_retryable(ids, deliver_after)
87
+ end
88
+
89
+ def mark_ids_retryable(ids, deliver_after)
90
+ return if ids.empty?
91
+
92
+ Rpush::Client::Mongoid::Notification.in(id: ids).update_all(processing: false, delivered: false, delivered_at: nil, failed: false, failed_at: nil, deliver_after: deliver_after, '$inc' => { retries: 1 })
93
+ end
94
+
95
+ def create_apns_feedback(failed_at, device_token, app)
96
+ Rpush::Client::Mongoid::Apns::Feedback.create!(failed_at: failed_at, device_token: device_token, app: app)
97
+ end
98
+
99
+ def create_gcm_notification(attrs, data, registration_ids, deliver_after, app) # rubocop:disable ParameterLists
100
+ notification = Rpush::Client::Mongoid::Gcm::Notification.new
101
+ create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app)
102
+ end
103
+
104
+ def create_adm_notification(attrs, data, registration_ids, deliver_after, app) # rubocop:disable ParameterLists
105
+ notification = Rpush::Client::Mongoid::Adm::Notification.new
106
+ create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app)
107
+ end
108
+
109
+ def update_app(app)
110
+ app.save!
111
+ end
112
+
113
+ def update_notification(notification)
114
+ notification.save!
115
+ end
116
+
117
+ def release_connection
118
+ end
119
+
120
+ def reopen_log
121
+ end
122
+
123
+ def pending_delivery_count
124
+ ready_for_delivery.count
125
+ end
126
+
127
+ def translate_integer_notification_id(id)
128
+ Rpush::Client::Mongoid::Notification.find_by(integer_id: id).id
129
+ end
130
+
131
+ private
132
+
133
+ def ready_for_delivery
134
+ Rpush::Client::Mongoid::Notification.where(processing: false, delivered: false, failed: false).or({ deliver_after: nil }, :deliver_after.lt => Time.now)
135
+ end
136
+
137
+ def claim_notifications(relation)
138
+ ids = relation.map(:id)
139
+ relation.where('$isolated' => 1).in(id: ids).update_all(processing: true, processing_pid: Process.pid)
140
+ Rpush::Client::Mongoid::Notification.where(processing: true, processing_pid: Process.pid).in(id: ids).asc('created_at')
141
+ end
142
+
143
+ def create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) # rubocop:disable ParameterLists
144
+ notification.assign_attributes(attrs)
145
+ notification.data = data
146
+ notification.registration_ids = registration_ids
147
+ notification.deliver_after = deliver_after
148
+ notification.app = app
149
+ notification.save!
150
+ notification
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
156
+
157
+ Rpush::Daemon::Store::Interface.check(Rpush::Daemon::Store::Mongoid)
@@ -49,7 +49,7 @@ module Rpush
49
49
  end
50
50
 
51
51
  def mark_ids_failed(ids, code, description, time)
52
- ids.each { |id| mark_failed(Rpush::Client::Redis::Apns::Notification.find(id), code, description, time) }
52
+ ids.each { |id| mark_failed(Rpush::Client::Redis::Notification.find(id), code, description, time) }
53
53
  end
54
54
 
55
55
  def mark_retryable(notification, deliver_after, opts = {})
@@ -75,7 +75,7 @@ module Rpush
75
75
  end
76
76
 
77
77
  def mark_ids_retryable(ids, deliver_after)
78
- ids.each { |id| mark_retryable(Rpush::Client::Redis::Apns::Notification.find(id), deliver_after) }
78
+ ids.each { |id| mark_retryable(Rpush::Client::Redis::Notification.find(id), deliver_after) }
79
79
  end
80
80
 
81
81
  def create_apns_feedback(failed_at, device_token, app)
@@ -115,6 +115,10 @@ module Rpush
115
115
  end
116
116
  end
117
117
 
118
+ def translate_integer_notification_id(id)
119
+ id
120
+ end
121
+
118
122
  private
119
123
 
120
124
  def create_gcm_like_notification(notification, attrs, data, registration_ids, deliver_after, app) # rubocop:disable ParameterLists
@@ -14,8 +14,7 @@ module Rpush
14
14
  warning << " #{msg}" if msg
15
15
  class_eval(<<-RUBY, __FILE__, __LINE__)
16
16
  def #{method_name}(*args, &blk)
17
- trace = "\n\nCALLED FROM:\n" + caller.join("\n")
18
- Rpush::Deprecation.warn(#{warning.inspect} + trace)
17
+ Rpush::Deprecation.warn_with_backtrace(#{warning.inspect})
19
18
  #{method_name_as_var}_without_warning(*args, &blk)
20
19
  end
21
20
  RUBY
@@ -16,5 +16,11 @@ module Rpush
16
16
  return if Rpush::Deprecation.muted?
17
17
  STDERR.puts "DEPRECATION WARNING: #{msg}"
18
18
  end
19
+
20
+ def self.warn_with_backtrace(msg)
21
+ return if Rpush::Deprecation.muted?
22
+ trace = "\n\nCALLED FROM:\n" + caller.join("\n")
23
+ warn(msg + trace)
24
+ end
19
25
  end
20
26
  end
@@ -2,6 +2,11 @@ module Rpush
2
2
  def self.embed(options = {})
3
3
  require 'rpush/daemon'
4
4
 
5
+ unless options.empty?
6
+ warning = "Passing configuration options directly to Rpush.embed is deprecated and will be removed from Rpush 2.5.0. Please setup configuration using Rpush.configure { |config| ... } before calling embed."
7
+ Rpush::Deprecation.warn_with_backtrace(warning)
8
+ end
9
+
5
10
  if @embed_thread
6
11
  STDERR.puts 'Rpush.embed can only be run once inside this process.'
7
12
  end
@@ -3,12 +3,9 @@ module Rpush
3
3
  attr_reader :internal_logger
4
4
 
5
5
  def initialize
6
- if Rpush.config.logger
7
- @internal_logger = Rpush.config.logger
8
- else
9
- @internal_logger = setup_logger(open_logfile)
10
- end
11
- rescue Errno::ENOENT, Errno::EPERM => e
6
+ @internal_logger = Rpush.config.logger || setup_logger(open_logfile)
7
+ @internal_logger.level = Rpush.config.log_level
8
+ rescue SystemCallError => e
12
9
  @internal_logger = nil
13
10
  error(e)
14
11
  error('Logging disabled.')
@@ -46,11 +43,11 @@ module Rpush
46
43
 
47
44
  def setup_logger(log)
48
45
  if ActiveSupport.const_defined?('BufferedLogger')
49
- logger = ActiveSupport::BufferedLogger.new(log, Rpush.config.log_level)
46
+ logger = ActiveSupport::BufferedLogger.new(log)
50
47
  logger.auto_flushing = auto_flushing
51
48
  logger
52
49
  else
53
- ActiveSupport::Logger.new(log, Rpush.config.log_level)
50
+ ActiveSupport::Logger.new(log)
54
51
  end
55
52
  end
56
53