rpush 2.0.1-java → 2.1.0-java

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/README.md +24 -18
  4. data/bin/rpush +1 -42
  5. data/lib/generators/rpush_config_generator.rb +7 -0
  6. data/lib/generators/{rpush_generator.rb → rpush_migration_generator.rb} +6 -11
  7. data/lib/generators/templates/rpush.rb +12 -12
  8. data/lib/generators/templates/rpush_2_0_0_updates.rb +2 -1
  9. data/lib/generators/templates/rpush_2_1_0_updates.rb +11 -0
  10. data/lib/rpush.rb +12 -4
  11. data/lib/rpush/apns_feedback.rb +1 -1
  12. data/lib/rpush/cli.rb +133 -0
  13. data/lib/rpush/client/active_model/apns/binary_notification_validator.rb +2 -2
  14. data/lib/rpush/client/active_model/apns/notification.rb +4 -1
  15. data/lib/rpush/client/active_model/notification.rb +0 -4
  16. data/lib/rpush/client/active_record/apns/notification.rb +0 -6
  17. data/lib/rpush/client/active_record/notification.rb +3 -2
  18. data/lib/rpush/client/redis/notification.rb +2 -0
  19. data/lib/rpush/configuration.rb +16 -24
  20. data/lib/rpush/daemon.rb +28 -1
  21. data/lib/rpush/daemon/app_runner.rb +6 -1
  22. data/lib/rpush/daemon/proc_title.rb +1 -1
  23. data/lib/rpush/daemon/signal_handler.rb +13 -3
  24. data/lib/rpush/daemon/store/active_record.rb +1 -1
  25. data/lib/rpush/deprecatable.rb +2 -1
  26. data/lib/rpush/embed.rb +1 -1
  27. data/lib/rpush/logger.rb +32 -19
  28. data/lib/rpush/push.rb +1 -1
  29. data/lib/rpush/version.rb +1 -1
  30. data/lib/tasks/quality.rake +1 -1
  31. data/lib/tasks/test.rake +12 -0
  32. data/spec/functional/apns_spec.rb +13 -11
  33. data/spec/functional_spec_helper.rb +1 -4
  34. data/spec/spec_helper.rb +4 -4
  35. data/spec/support/active_record_setup.rb +2 -1
  36. data/spec/unit/client/active_record/apns/notification_spec.rb +29 -3
  37. data/spec/unit/configuration_spec.rb +0 -7
  38. data/spec/unit/daemon/app_runner_spec.rb +1 -1
  39. data/spec/unit/daemon/signal_handler_spec.rb +1 -1
  40. data/spec/unit/daemon_spec.rb +1 -1
  41. data/spec/unit/deprecatable_spec.rb +1 -1
  42. data/spec/unit/logger_spec.rb +4 -4
  43. metadata +48 -3
@@ -3,10 +3,10 @@ module Rpush
3
3
  module ActiveModel
4
4
  module Apns
5
5
  class BinaryNotificationValidator < ::ActiveModel::Validator
6
- MAX_BYTES = 256
6
+ MAX_BYTES = 2048
7
7
 
8
8
  def validate(record)
9
- return unless record.payload_size > MAX_BYTES
9
+ return unless record.payload.bytesize > MAX_BYTES
10
10
  record.errors[:base] << "APN notification cannot be larger than #{MAX_BYTES} bytes. Try condensing your alert and device attributes."
11
11
  end
12
12
  end
@@ -48,6 +48,8 @@ module Rpush
48
48
  json['aps']['alert'] = alert if alert
49
49
  json['aps']['badge'] = badge if badge
50
50
  json['aps']['sound'] = sound if sound
51
+ json['aps']['category'] = category if category
52
+ json['aps']['url-args'] = url_args if url_args
51
53
 
52
54
  if data && data[CONTENT_AVAILABLE_KEY]
53
55
  json['aps']['content-available'] = 1
@@ -63,10 +65,11 @@ module Rpush
63
65
  end
64
66
 
65
67
  def to_binary(options = {})
68
+ frame_payload = payload
66
69
  frame_id = options[:for_validation] ? 0 : id
67
70
  frame = ""
68
71
  frame << [1, 32, device_token].pack("cnH*")
69
- frame << [2, payload.bytesize, payload].pack("cna*")
72
+ frame << [2, frame_payload.bytesize, frame_payload].pack("cna*")
70
73
  frame << [3, 4, frame_id].pack("cnN")
71
74
  frame << [4, 4, expiry || APNS_DEFAULT_EXPIRY].pack("cnN")
72
75
  frame << [5, 1, priority_for_frame].pack("cnc")
@@ -13,10 +13,6 @@ module Rpush
13
13
  multi_json_dump(as_json)
14
14
  end
15
15
 
16
- def payload_size
17
- payload.bytesize
18
- end
19
-
20
16
  def payload_data_size
21
17
  multi_json_dump(as_json['data']).bytesize
22
18
  end
@@ -6,12 +6,6 @@ module Rpush
6
6
  include Deprecatable
7
7
  include Rpush::Client::ActiveModel::Apns::Notification
8
8
 
9
- alias_method :attributes_for_device=, :data=
10
- alias_method :attributes_for_device, :data
11
-
12
- deprecated(:attributes_for_device, '2.1.0', 'Use :data instead.')
13
- deprecated(:attributes_for_device=, '2.1.0', 'Use :data instead.')
14
-
15
9
  def alert=(alert)
16
10
  if alert.is_a?(Hash)
17
11
  write_attribute(:alert, multi_json_dump(alert))
@@ -7,15 +7,16 @@ module Rpush
7
7
 
8
8
  self.table_name = 'rpush_notifications'
9
9
 
10
- # TODO: Dump using multi json.
11
10
  serialize :registration_ids
11
+ serialize :url_args
12
12
 
13
13
  belongs_to :app, class_name: 'Rpush::Client::ActiveRecord::App'
14
14
 
15
15
  if Rpush.attr_accessible_available?
16
16
  attr_accessible :badge, :device_token, :sound, :alert, :data, :expiry, :delivered,
17
17
  :delivered_at, :failed, :failed_at, :error_code, :error_description, :deliver_after,
18
- :alert_is_json, :app, :app_id, :collapse_key, :delay_while_idle, :registration_ids, :uri
18
+ :alert_is_json, :app, :app_id, :collapse_key, :delay_while_idle, :registration_ids,
19
+ :uri, :url_args, :category
19
20
  end
20
21
 
21
22
  def data=(attrs)
@@ -40,6 +40,8 @@ module Rpush
40
40
  attribute :registration_ids, :array
41
41
  attribute :uri, :string
42
42
  attribute :priority, :integer
43
+ attribute :url_args, :array
44
+ attribute :category, :string
43
45
 
44
46
  def app
45
47
  return nil unless app_id
@@ -23,9 +23,10 @@ module Rpush
23
23
  @client_initialized = true
24
24
  end
25
25
 
26
- CONFIG_ATTRS = [:foreground, :push_poll, :feedback_poll, :embedded,
27
- :check_for_errors, :pid_file, :batch_size, :push, :client, :logger,
28
- :batch_storage_updates, :log_dir, :environment]
26
+ CONFIG_ATTRS = [:push_poll, :feedback_poll, :embedded, :pid_file, :batch_size,
27
+ :push, :client, :logger, :log_file, :foreground, :log_level,
28
+ # Deprecated
29
+ :log_dir]
29
30
 
30
31
  class ConfigurationWithoutDefaults < Struct.new(*CONFIG_ATTRS)
31
32
  end
@@ -33,8 +34,7 @@ module Rpush
33
34
  class Configuration < Struct.new(*CONFIG_ATTRS)
34
35
  include Deprecatable
35
36
 
36
- deprecated(:batch_storage_updates=, '2.1.0', 'Updates are now always batched by the storage backends.')
37
- deprecated(:check_for_errors=, '2.1.0', 'APNs error detection is now performed asynchronously and does not require pauses.')
37
+ deprecated(:log_dir=, '2.3.0', 'Please use log_file instead.')
38
38
 
39
39
  delegate :redis_options, :redis_options=, to: :Modis
40
40
 
@@ -52,45 +52,37 @@ module Rpush
52
52
 
53
53
  def pid_file=(path)
54
54
  if path && !Pathname.new(path).absolute?
55
- super(File.join(Rails.root, path))
55
+ super(File.join(Rpush.root, path))
56
56
  else
57
57
  super
58
58
  end
59
59
  end
60
60
 
61
- def logger=(logger)
62
- super(logger)
63
- end
64
-
65
- def foreground=(bool)
66
- if Rpush.jruby?
67
- # The JVM does not support fork().
68
- super(true)
61
+ def log_file=(path)
62
+ if path && !Pathname.new(path).absolute?
63
+ super(File.join(Rpush.root, path))
69
64
  else
70
65
  super
71
66
  end
72
67
  end
73
68
 
74
- def set_defaults
75
- if Rpush.jruby?
76
- # The JVM does not support fork().
77
- self.foreground = true
78
- else
79
- self.foreground = false
80
- end
69
+ def logger=(logger)
70
+ super(logger)
71
+ end
81
72
 
73
+ def set_defaults
82
74
  self.push_poll = 2
83
75
  self.feedback_poll = 60
84
76
  self.batch_size = 100
85
- self.pid_file = nil
86
77
  self.client = :active_record
87
78
  self.logger = nil
88
- self.log_dir = Rails.root
79
+ self.log_file = 'log/rpush.log'
80
+ self.pid_file = 'tmp/rpush.pid'
81
+ self.log_level = (defined?(Rails) && Rails.logger) ? Rails.logger.level : ::Logger::Severity::INFO
89
82
 
90
83
  # Internal options.
91
84
  self.embedded = false
92
85
  self.push = false
93
- self.environment = Rails.env
94
86
  end
95
87
  end
96
88
  end
data/lib/rpush/daemon.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # encoding: UTF-8
2
+
1
3
  require 'thread'
2
4
  require 'socket'
3
5
  require 'pathname'
@@ -46,6 +48,8 @@ require 'rpush/daemon/adm'
46
48
 
47
49
  module Rpush
48
50
  module Daemon
51
+ extend Term::ANSIColor
52
+
49
53
  class << self
50
54
  attr_accessor :store
51
55
  end
@@ -60,6 +64,9 @@ module Rpush
60
64
  # No further store connections will be made from this thread.
61
65
  store.release_connection
62
66
 
67
+ Rpush.logger.info('Rpush operational.')
68
+ show_welcome_if_needed
69
+
63
70
  # Blocking call, returns after Feeder.stop is called from another thread.
64
71
  Feeder.start
65
72
 
@@ -68,12 +75,19 @@ module Rpush
68
75
  end
69
76
 
70
77
  def self.shutdown
71
- puts "\nShutting down..."
78
+ if Rpush.config.foreground
79
+ # Eat the '^C'
80
+ STDOUT.write("\b\b")
81
+ STDOUT.flush
82
+ end
83
+
84
+ Rpush.logger.info('Shutting down... ', true)
72
85
 
73
86
  shutdown_lock.synchronize do
74
87
  Feeder.stop
75
88
  AppRunner.stop
76
89
  delete_pid_file
90
+ puts green('✔') if Rpush.config.foreground
77
91
  end
78
92
  end
79
93
 
@@ -105,6 +119,7 @@ module Rpush
105
119
  def self.write_pid_file
106
120
  unless Rpush.config.pid_file.blank?
107
121
  begin
122
+ FileUtils.mkdir_p(File.dirname(Rpush.config.pid_file))
108
123
  File.open(Rpush.config.pid_file, 'w') { |f| f.puts Process.pid }
109
124
  rescue SystemCallError => e
110
125
  Rpush.logger.error("Failed to write PID to '#{Rpush.config.pid_file}': #{e.inspect}")
@@ -116,5 +131,17 @@ module Rpush
116
131
  pid_file = Rpush.config.pid_file
117
132
  File.delete(pid_file) if !pid_file.blank? && File.exist?(pid_file)
118
133
  end
134
+
135
+ def self.show_welcome_if_needed
136
+ if Rpush::Daemon::AppRunner.app_ids.count == 0
137
+ puts <<-EOS
138
+
139
+ * #{green('Is this your first time using Rpush?')}
140
+ You need to create an App before you can start using Rpush.
141
+ Please refer to the documentation at https://github.com/rpush/rpush
142
+
143
+ EOS
144
+ end
145
+ end
119
146
  end
120
147
  end
@@ -1,6 +1,10 @@
1
+ # encoding: UTF-8
2
+
1
3
  module Rpush
2
4
  module Daemon
3
5
  class AppRunner
6
+ extend Term::ANSIColor
7
+
4
8
  extend Reflectable
5
9
  include Reflectable
6
10
  include Loggable
@@ -24,9 +28,10 @@ module Rpush
24
28
  end
25
29
 
26
30
  def self.start_app(app)
31
+ Rpush.logger.info("[#{app.name}] Starting #{pluralize(app.connections, 'dispatcher')}... ", true)
27
32
  @runners[app.id] = new(app)
28
33
  @runners[app.id].start
29
- log_info("[#{app.name}] Started, #{pluralize(app.connections, 'dispatcher')}.")
34
+ puts green('') if Rpush.config.foreground
30
35
  rescue StandardError => e
31
36
  @runners.delete(app.id)
32
37
  Rpush.logger.error("[#{app.name}] Exception raised during startup. Notifications will not be delivered for this app.")
@@ -9,7 +9,7 @@ module Rpush
9
9
  total_dispatchers = AppRunner.total_dispatchers
10
10
  dispatchers_str = total_dispatchers == 1 ? 'dispatcher' : 'dispatchers'
11
11
  total_queued = AppRunner.total_queued
12
- format("rpush | %s | %d queued | %d %s", Rpush.config.environment, total_queued, total_dispatchers, dispatchers_str)
12
+ format("rpush | %d queued | %d %s", total_queued, total_dispatchers, dispatchers_str)
13
13
  end
14
14
  end
15
15
  end
@@ -28,10 +28,9 @@ module Rpush
28
28
  begin
29
29
  case signal
30
30
  when 'HUP'
31
- Synchronizer.sync
32
- Feeder.wakeup
31
+ handle_hup
33
32
  when 'USR2'
34
- AppRunner.debug
33
+ handle_usr2
35
34
  when 'INT', 'TERM'
36
35
  Thread.new { Rpush::Daemon.shutdown }
37
36
  break
@@ -48,6 +47,17 @@ module Rpush
48
47
  end
49
48
  end
50
49
 
50
+ def self.handle_hup
51
+ Rpush.logger.info("Received HUP signal.")
52
+ Synchronizer.sync
53
+ Feeder.wakeup
54
+ end
55
+
56
+ def self.handle_usr2
57
+ Rpush.logger.info("Received USR2 signal.")
58
+ AppRunner.debug
59
+ end
60
+
51
61
  def self.trap_signals?
52
62
  !Rpush.config.embedded
53
63
  end
@@ -166,7 +166,7 @@ module Rpush
166
166
  end
167
167
 
168
168
  def ready_for_delivery
169
- Rpush::Client::ActiveRecord::Notification.where('processing = ? AND delivered = ? AND failed = ? AND (deliver_after IS NULL OR deliver_after < ?)', false, false, false, Time.now)
169
+ Rpush::Client::ActiveRecord::Notification.where('processing = ? AND delivered = ? AND failed = ? AND (deliver_after IS NULL OR deliver_after < ?)', false, false, false, Time.now).order('created_at ASC')
170
170
  end
171
171
 
172
172
  def mark_processing(notifications)
@@ -14,7 +14,8 @@ module Rpush
14
14
  warning << " #{msg}" if msg
15
15
  class_eval(<<-RUBY, __FILE__, __LINE__)
16
16
  def #{method_name}(*args, &blk)
17
- Rpush::Deprecation.warn(#{warning.inspect})
17
+ trace = "\n\nCALLED FROM:\n" + caller.join("\n")
18
+ Rpush::Deprecation.warn(#{warning.inspect} + trace)
18
19
  #{method_name_as_var}_without_warning(*args, &blk)
19
20
  end
20
21
  RUBY
data/lib/rpush/embed.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Rpush
2
2
  def self.embed(options = {})
3
- Rpush.require_for_daemon
3
+ require 'rpush/daemon'
4
4
 
5
5
  if @embed_thread
6
6
  STDERR.puts 'Rpush.embed can only be run once inside this process.'
data/lib/rpush/logger.rb CHANGED
@@ -1,9 +1,8 @@
1
1
  module Rpush
2
2
  class Logger
3
3
  def initialize
4
- log_dir = File.join(Rpush.config.log_dir, 'log')
5
- FileUtils.mkdir_p(log_dir)
6
- log = File.open(File.join(log_dir, 'rpush.log'), 'a')
4
+ FileUtils.mkdir_p(File.dirname(Rpush.config.log_file))
5
+ log = File.open(Rpush.config.log_file, 'a')
7
6
  log.sync = true
8
7
  setup_logger(log)
9
8
  rescue Errno::ENOENT, Errno::EPERM => e
@@ -12,16 +11,16 @@ module Rpush
12
11
  error('Logging disabled.')
13
12
  end
14
13
 
15
- def info(msg)
16
- log(:info, msg)
14
+ def info(msg, inline = false)
15
+ log(:info, msg, inline)
17
16
  end
18
17
 
19
- def error(msg)
20
- log(:error, msg, 'ERROR', STDERR)
18
+ def error(msg, inline = false)
19
+ log(:error, msg, inline, 'ERROR', STDERR)
21
20
  end
22
21
 
23
- def warn(msg)
24
- log(:warn, msg, 'WARNING', STDERR)
22
+ def warn(msg, inline = false)
23
+ log(:warn, msg, inline, 'WARNING', STDERR)
25
24
  end
26
25
 
27
26
  private
@@ -30,14 +29,22 @@ module Rpush
30
29
  if Rpush.config.logger
31
30
  @logger = Rpush.config.logger
32
31
  elsif ActiveSupport.const_defined?('BufferedLogger')
33
- @logger = ActiveSupport::BufferedLogger.new(log, Rails.logger.level)
34
- @logger.auto_flushing = Rails.logger.respond_to?(:auto_flushing) ? Rails.logger.auto_flushing : true
32
+ @logger = ActiveSupport::BufferedLogger.new(log, Rpush.config.log_level)
33
+ @logger.auto_flushing = auto_flushing
35
34
  else
36
- @logger = ActiveSupport::Logger.new(log, Rails.logger.level)
35
+ @logger = ActiveSupport::Logger.new(log, Rpush.config.log_level)
37
36
  end
38
37
  end
39
38
 
40
- def log(where, msg, prefix = nil, io = STDOUT)
39
+ def auto_flushing
40
+ if defined?(Rails) && Rails.logger.respond_to?(:auto_flushing)
41
+ Rails.logger.auto_flushing
42
+ else
43
+ true
44
+ end
45
+ end
46
+
47
+ def log(where, msg, inline = false, prefix = nil, io = STDOUT)
41
48
  if msg.is_a?(Exception)
42
49
  formatted_backtrace = msg.backtrace.join("\n")
43
50
  msg = "#{msg.class.name}, #{msg.message}\n#{formatted_backtrace}"
@@ -47,13 +54,19 @@ module Rpush
47
54
  formatted_msg << "[#{prefix}] " if prefix
48
55
  formatted_msg << msg
49
56
 
50
- if io == STDERR
51
- io.puts formatted_msg
52
- elsif Rpush.config.foreground
53
- io.puts formatted_msg
54
- end
55
-
57
+ log_foreground(io, formatted_msg, inline)
56
58
  @logger.send(where, formatted_msg) if @logger
57
59
  end
60
+
61
+ def log_foreground(io, formatted_msg, inline)
62
+ return unless io == STDERR || Rpush.config.foreground
63
+
64
+ if inline
65
+ io.write(formatted_msg)
66
+ io.flush
67
+ else
68
+ io.puts(formatted_msg)
69
+ end
70
+ end
58
71
  end
59
72
  end
data/lib/rpush/push.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Rpush
2
2
  def self.push(options = {})
3
- Rpush.require_for_daemon
3
+ require 'rpush/daemon'
4
4
 
5
5
  config = Rpush::ConfigurationWithoutDefaults.new
6
6
  options.each { |k, v| config.send("#{k}=", v) }
data/lib/rpush/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Rpush
2
- VERSION = '2.0.1'
2
+ VERSION = '2.1.0'
3
3
  end
@@ -7,7 +7,7 @@ begin
7
7
  cane.no_style = false
8
8
  cane.style_measure = 1000
9
9
  cane.no_doc = true
10
- cane.abc_max = 20
10
+ cane.abc_max = 22
11
11
  end
12
12
 
13
13
  namespace :spec do