rpush 2.0.1 → 2.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +9 -0
- data/README.md +24 -18
- data/bin/rpush +1 -42
- data/lib/generators/rpush_config_generator.rb +7 -0
- data/lib/generators/{rpush_generator.rb → rpush_migration_generator.rb} +6 -11
- data/lib/generators/templates/rpush.rb +12 -12
- data/lib/generators/templates/rpush_2_0_0_updates.rb +2 -1
- data/lib/generators/templates/rpush_2_1_0_updates.rb +11 -0
- data/lib/rpush.rb +12 -4
- data/lib/rpush/apns_feedback.rb +1 -1
- data/lib/rpush/cli.rb +133 -0
- data/lib/rpush/client/active_model/apns/binary_notification_validator.rb +2 -2
- data/lib/rpush/client/active_model/apns/notification.rb +4 -1
- data/lib/rpush/client/active_model/notification.rb +0 -4
- data/lib/rpush/client/active_record/apns/notification.rb +0 -6
- data/lib/rpush/client/active_record/notification.rb +3 -2
- data/lib/rpush/client/redis/notification.rb +2 -0
- data/lib/rpush/configuration.rb +16 -24
- data/lib/rpush/daemon.rb +28 -1
- data/lib/rpush/daemon/app_runner.rb +6 -1
- data/lib/rpush/daemon/proc_title.rb +1 -1
- data/lib/rpush/daemon/signal_handler.rb +13 -3
- data/lib/rpush/daemon/store/active_record.rb +1 -1
- data/lib/rpush/deprecatable.rb +2 -1
- data/lib/rpush/embed.rb +1 -1
- data/lib/rpush/logger.rb +32 -19
- data/lib/rpush/push.rb +1 -1
- data/lib/rpush/version.rb +1 -1
- data/lib/tasks/quality.rake +1 -1
- data/lib/tasks/test.rake +12 -0
- data/spec/functional/apns_spec.rb +13 -11
- data/spec/functional_spec_helper.rb +1 -4
- data/spec/spec_helper.rb +4 -4
- data/spec/support/active_record_setup.rb +2 -1
- data/spec/unit/client/active_record/apns/notification_spec.rb +29 -3
- data/spec/unit/configuration_spec.rb +0 -7
- data/spec/unit/daemon/app_runner_spec.rb +1 -1
- data/spec/unit/daemon/signal_handler_spec.rb +1 -1
- data/spec/unit/daemon_spec.rb +1 -1
- data/spec/unit/deprecatable_spec.rb +1 -1
- data/spec/unit/logger_spec.rb +4 -4
- 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 =
|
6
|
+
MAX_BYTES = 2048
|
7
7
|
|
8
8
|
def validate(record)
|
9
|
-
return unless record.
|
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,
|
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")
|
@@ -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,
|
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)
|
data/lib/rpush/configuration.rb
CHANGED
@@ -23,9 +23,10 @@ module Rpush
|
|
23
23
|
@client_initialized = true
|
24
24
|
end
|
25
25
|
|
26
|
-
CONFIG_ATTRS = [:
|
27
|
-
:
|
28
|
-
|
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(:
|
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(
|
55
|
+
super(File.join(Rpush.root, path))
|
56
56
|
else
|
57
57
|
super
|
58
58
|
end
|
59
59
|
end
|
60
60
|
|
61
|
-
def
|
62
|
-
|
63
|
-
|
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
|
75
|
-
|
76
|
-
|
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.
|
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
|
-
|
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
|
-
|
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 | %
|
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
|
-
|
32
|
-
Feeder.wakeup
|
31
|
+
handle_hup
|
33
32
|
when 'USR2'
|
34
|
-
|
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)
|
data/lib/rpush/deprecatable.rb
CHANGED
@@ -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
|
-
|
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
data/lib/rpush/logger.rb
CHANGED
@@ -1,9 +1,8 @@
|
|
1
1
|
module Rpush
|
2
2
|
class Logger
|
3
3
|
def initialize
|
4
|
-
|
5
|
-
|
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,
|
34
|
-
@logger.auto_flushing =
|
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,
|
35
|
+
@logger = ActiveSupport::Logger.new(log, Rpush.config.log_level)
|
37
36
|
end
|
38
37
|
end
|
39
38
|
|
40
|
-
def
|
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
|
-
|
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
data/lib/rpush/version.rb
CHANGED
data/lib/tasks/quality.rake
CHANGED
data/lib/tasks/test.rake
CHANGED
@@ -21,8 +21,20 @@ namespace :test do
|
|
21
21
|
begin
|
22
22
|
Dir.chdir(path)
|
23
23
|
cmd('echo "gem \'rake\'" >> Gemfile')
|
24
|
+
cmd('echo "gem \'pg\'" >> Gemfile')
|
24
25
|
cmd("echo \"gem 'rpush', :path => '#{rpush_root}'\" >> Gemfile")
|
25
26
|
cmd('bundle install')
|
27
|
+
|
28
|
+
File.open('config/database.yml', 'w') do |fd|
|
29
|
+
fd.write(<<-YML)
|
30
|
+
development:
|
31
|
+
adapter: postgresql
|
32
|
+
database: rpush_rails_test
|
33
|
+
pool: 5
|
34
|
+
timeout: 5000
|
35
|
+
YML
|
36
|
+
end
|
37
|
+
|
26
38
|
cmd('bundle exec rails g rpush')
|
27
39
|
cmd('bundle exec rake db:migrate')
|
28
40
|
ensure
|