rpush 1.0.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +99 -0
- data/LICENSE +7 -0
- data/README.md +189 -0
- data/bin/rpush +36 -0
- data/config/database.yml +44 -0
- data/lib/generators/rpush_generator.rb +44 -0
- data/lib/generators/templates/add_adm.rb +23 -0
- data/lib/generators/templates/add_alert_is_json_to_rapns_notifications.rb +9 -0
- data/lib/generators/templates/add_app_to_rapns.rb +11 -0
- data/lib/generators/templates/add_fail_after_to_rpush_notifications.rb +9 -0
- data/lib/generators/templates/add_gcm.rb +102 -0
- data/lib/generators/templates/add_rpush.rb +349 -0
- data/lib/generators/templates/add_wpns.rb +16 -0
- data/lib/generators/templates/create_rapns_apps.rb +16 -0
- data/lib/generators/templates/create_rapns_feedback.rb +18 -0
- data/lib/generators/templates/create_rapns_notifications.rb +29 -0
- data/lib/generators/templates/rename_rapns_to_rpush.rb +63 -0
- data/lib/generators/templates/rpush.rb +104 -0
- data/lib/rpush.rb +62 -0
- data/lib/rpush/TODO +3 -0
- data/lib/rpush/adm/app.rb +15 -0
- data/lib/rpush/adm/data_validator.rb +11 -0
- data/lib/rpush/adm/notification.rb +29 -0
- data/lib/rpush/apns/app.rb +29 -0
- data/lib/rpush/apns/binary_notification_validator.rb +12 -0
- data/lib/rpush/apns/device_token_format_validator.rb +12 -0
- data/lib/rpush/apns/feedback.rb +16 -0
- data/lib/rpush/apns/notification.rb +84 -0
- data/lib/rpush/apns_feedback.rb +13 -0
- data/lib/rpush/app.rb +18 -0
- data/lib/rpush/configuration.rb +75 -0
- data/lib/rpush/daemon.rb +140 -0
- data/lib/rpush/daemon/adm.rb +9 -0
- data/lib/rpush/daemon/adm/delivery.rb +222 -0
- data/lib/rpush/daemon/apns.rb +16 -0
- data/lib/rpush/daemon/apns/certificate_expired_error.rb +20 -0
- data/lib/rpush/daemon/apns/delivery.rb +64 -0
- data/lib/rpush/daemon/apns/disconnection_error.rb +20 -0
- data/lib/rpush/daemon/apns/feedback_receiver.rb +79 -0
- data/lib/rpush/daemon/app_runner.rb +187 -0
- data/lib/rpush/daemon/batch.rb +115 -0
- data/lib/rpush/daemon/constants.rb +59 -0
- data/lib/rpush/daemon/delivery.rb +28 -0
- data/lib/rpush/daemon/delivery_error.rb +19 -0
- data/lib/rpush/daemon/dispatcher/http.rb +21 -0
- data/lib/rpush/daemon/dispatcher/tcp.rb +30 -0
- data/lib/rpush/daemon/dispatcher_loop.rb +54 -0
- data/lib/rpush/daemon/dispatcher_loop_collection.rb +33 -0
- data/lib/rpush/daemon/feeder.rb +68 -0
- data/lib/rpush/daemon/gcm.rb +9 -0
- data/lib/rpush/daemon/gcm/delivery.rb +222 -0
- data/lib/rpush/daemon/interruptible_sleep.rb +61 -0
- data/lib/rpush/daemon/loggable.rb +31 -0
- data/lib/rpush/daemon/reflectable.rb +13 -0
- data/lib/rpush/daemon/retry_header_parser.rb +23 -0
- data/lib/rpush/daemon/retryable_error.rb +20 -0
- data/lib/rpush/daemon/service_config_methods.rb +33 -0
- data/lib/rpush/daemon/store/active_record.rb +154 -0
- data/lib/rpush/daemon/store/active_record/reconnectable.rb +68 -0
- data/lib/rpush/daemon/tcp_connection.rb +143 -0
- data/lib/rpush/daemon/too_many_requests_error.rb +20 -0
- data/lib/rpush/daemon/wpns.rb +9 -0
- data/lib/rpush/daemon/wpns/delivery.rb +132 -0
- data/lib/rpush/deprecatable.rb +23 -0
- data/lib/rpush/deprecation.rb +23 -0
- data/lib/rpush/embed.rb +28 -0
- data/lib/rpush/gcm/app.rb +11 -0
- data/lib/rpush/gcm/expiry_collapse_key_mutual_inclusion_validator.rb +11 -0
- data/lib/rpush/gcm/notification.rb +30 -0
- data/lib/rpush/logger.rb +63 -0
- data/lib/rpush/multi_json_helper.rb +16 -0
- data/lib/rpush/notification.rb +69 -0
- data/lib/rpush/notifier.rb +52 -0
- data/lib/rpush/payload_data_size_validator.rb +10 -0
- data/lib/rpush/push.rb +16 -0
- data/lib/rpush/railtie.rb +11 -0
- data/lib/rpush/reflection.rb +58 -0
- data/lib/rpush/registration_ids_count_validator.rb +10 -0
- data/lib/rpush/version.rb +3 -0
- data/lib/rpush/wpns/app.rb +9 -0
- data/lib/rpush/wpns/notification.rb +26 -0
- data/lib/tasks/cane.rake +18 -0
- data/lib/tasks/rpush.rake +16 -0
- data/lib/tasks/test.rake +38 -0
- data/spec/functional/adm_spec.rb +43 -0
- data/spec/functional/apns_spec.rb +58 -0
- data/spec/functional/embed_spec.rb +49 -0
- data/spec/functional/gcm_spec.rb +42 -0
- data/spec/functional/wpns_spec.rb +41 -0
- data/spec/support/cert_with_password.pem +90 -0
- data/spec/support/cert_without_password.pem +59 -0
- data/spec/support/install.sh +32 -0
- data/spec/support/simplecov_helper.rb +20 -0
- data/spec/support/simplecov_quality_formatter.rb +8 -0
- data/spec/tmp/.gitkeep +0 -0
- data/spec/unit/adm/app_spec.rb +58 -0
- data/spec/unit/adm/notification_spec.rb +45 -0
- data/spec/unit/apns/app_spec.rb +29 -0
- data/spec/unit/apns/feedback_spec.rb +9 -0
- data/spec/unit/apns/notification_spec.rb +208 -0
- data/spec/unit/apns_feedback_spec.rb +21 -0
- data/spec/unit/app_spec.rb +30 -0
- data/spec/unit/configuration_spec.rb +45 -0
- data/spec/unit/daemon/adm/delivery_spec.rb +243 -0
- data/spec/unit/daemon/apns/certificate_expired_error_spec.rb +11 -0
- data/spec/unit/daemon/apns/delivery_spec.rb +101 -0
- data/spec/unit/daemon/apns/disconnection_error_spec.rb +18 -0
- data/spec/unit/daemon/apns/feedback_receiver_spec.rb +117 -0
- data/spec/unit/daemon/app_runner_spec.rb +292 -0
- data/spec/unit/daemon/batch_spec.rb +232 -0
- data/spec/unit/daemon/delivery_error_spec.rb +13 -0
- data/spec/unit/daemon/delivery_spec.rb +38 -0
- data/spec/unit/daemon/dispatcher/http_spec.rb +33 -0
- data/spec/unit/daemon/dispatcher/tcp_spec.rb +38 -0
- data/spec/unit/daemon/dispatcher_loop_collection_spec.rb +37 -0
- data/spec/unit/daemon/dispatcher_loop_spec.rb +71 -0
- data/spec/unit/daemon/feeder_spec.rb +98 -0
- data/spec/unit/daemon/gcm/delivery_spec.rb +310 -0
- data/spec/unit/daemon/interruptible_sleep_spec.rb +68 -0
- data/spec/unit/daemon/reflectable_spec.rb +27 -0
- data/spec/unit/daemon/retryable_error_spec.rb +14 -0
- data/spec/unit/daemon/service_config_methods_spec.rb +33 -0
- data/spec/unit/daemon/store/active_record/reconnectable_spec.rb +114 -0
- data/spec/unit/daemon/store/active_record_spec.rb +357 -0
- data/spec/unit/daemon/tcp_connection_spec.rb +287 -0
- data/spec/unit/daemon/too_many_requests_error_spec.rb +14 -0
- data/spec/unit/daemon/wpns/delivery_spec.rb +159 -0
- data/spec/unit/daemon_spec.rb +159 -0
- data/spec/unit/deprecatable_spec.rb +32 -0
- data/spec/unit/deprecation_spec.rb +15 -0
- data/spec/unit/embed_spec.rb +50 -0
- data/spec/unit/gcm/app_spec.rb +4 -0
- data/spec/unit/gcm/notification_spec.rb +36 -0
- data/spec/unit/logger_spec.rb +127 -0
- data/spec/unit/notification_shared.rb +105 -0
- data/spec/unit/notification_spec.rb +15 -0
- data/spec/unit/notifier_spec.rb +49 -0
- data/spec/unit/push_spec.rb +43 -0
- data/spec/unit/reflection_spec.rb +30 -0
- data/spec/unit/rpush_spec.rb +9 -0
- data/spec/unit/wpns/app_spec.rb +4 -0
- data/spec/unit/wpns/notification_spec.rb +30 -0
- data/spec/unit_spec_helper.rb +101 -0
- metadata +276 -0
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
class TooManyRequestsError < StandardError
|
|
3
|
+
attr_reader :code, :description, :response
|
|
4
|
+
|
|
5
|
+
def initialize(code, notification_id, description, response)
|
|
6
|
+
@code = code
|
|
7
|
+
@notification_id = notification_id
|
|
8
|
+
@description = description
|
|
9
|
+
@response = response
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def to_s
|
|
13
|
+
message
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def message
|
|
17
|
+
"Too many requests for #{@notification_id}, received error #{@code} (#{@description}) - retry after #{@response.header['retry-after']}"
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Daemon
|
|
3
|
+
module Wpns
|
|
4
|
+
|
|
5
|
+
# http://msdn.microsoft.com/en-us/library/windowsphone/develop/ff941100%28v=vs.105%29.aspx
|
|
6
|
+
class Delivery < Rpush::Daemon::Delivery
|
|
7
|
+
|
|
8
|
+
FAILURE_MESSAGES = {
|
|
9
|
+
400 => 'Bad XML or malformed notification URI.',
|
|
10
|
+
401 => 'Unauthorized to send a notification to this app.'
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
def initialize(app, http, notification, batch)
|
|
14
|
+
@app = app
|
|
15
|
+
@http = http
|
|
16
|
+
@notification = notification
|
|
17
|
+
@batch = batch
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def perform
|
|
21
|
+
begin
|
|
22
|
+
handle_response(do_post)
|
|
23
|
+
rescue Rpush::DeliveryError => error
|
|
24
|
+
mark_failed(error.code, error.description)
|
|
25
|
+
raise
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
private
|
|
30
|
+
|
|
31
|
+
def handle_response(response)
|
|
32
|
+
code = response.code.to_i
|
|
33
|
+
case code
|
|
34
|
+
when 200
|
|
35
|
+
ok(response)
|
|
36
|
+
when 406
|
|
37
|
+
not_acceptable(response)
|
|
38
|
+
when 412
|
|
39
|
+
precondition_failed(response)
|
|
40
|
+
when 503
|
|
41
|
+
service_unavailable(response)
|
|
42
|
+
else
|
|
43
|
+
handle_failure(code)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def handle_failure(code, msg=nil)
|
|
48
|
+
unless msg
|
|
49
|
+
msg = if FAILURE_MESSAGES.key?(code)
|
|
50
|
+
FAILURE_MESSAGES[code]
|
|
51
|
+
else
|
|
52
|
+
Rpush::Daemon::HTTP_STATUS_CODES[code]
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
raise Rpush::DeliveryError.new(code, @notification.id, msg)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def ok(response)
|
|
59
|
+
status = status_from_response(response)
|
|
60
|
+
case status[:notification]
|
|
61
|
+
when ["Received"]
|
|
62
|
+
mark_delivered
|
|
63
|
+
log_info("#{@notification.id} sent successfully")
|
|
64
|
+
when ["QueueFull"]
|
|
65
|
+
mark_retryable(@notification, Time.now + (60*10))
|
|
66
|
+
log_warn("#{@notification.id} cannot be sent. The Queue is full.")
|
|
67
|
+
when ["Suppressed"]
|
|
68
|
+
handle_failure(200, "Notification was received but suppressed by the service.")
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def not_acceptable(response)
|
|
73
|
+
retry_notification("Per-day throttling limit reached.")
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def precondition_failed(response)
|
|
77
|
+
retry_notification("Device unreachable.")
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def service_unavailable(response)
|
|
81
|
+
mark_retryable_exponential(@notification)
|
|
82
|
+
log_warn("Service Unavailable. " + retry_message)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def retry_message
|
|
86
|
+
"Notification #{@notification.id} will be retried after #{@notification.deliver_after.strftime("%Y-%m-%d %H:%M:%S")} (retry #{@notification.retries})."
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def retry_notification(reason)
|
|
90
|
+
deliver_after = Time.now + (60*60)
|
|
91
|
+
mark_retryable(@notification, deliver_after)
|
|
92
|
+
log_warn("#{reason} " + retry_message)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def do_post
|
|
96
|
+
body = notification_to_xml
|
|
97
|
+
header = {
|
|
98
|
+
"Content-Length" => body.length.to_s,
|
|
99
|
+
"Content-Type" => "text/xml",
|
|
100
|
+
"X-WindowsPhone-Target" => "toast",
|
|
101
|
+
"X-NotificationClass" => '2'
|
|
102
|
+
}
|
|
103
|
+
post = Net::HTTP::Post.new(URI.parse(@notification.uri).path, initheader=header)
|
|
104
|
+
post.body = body
|
|
105
|
+
@http.request(URI.parse(@notification.uri), post)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def status_from_response(response)
|
|
109
|
+
headers = response.to_hash
|
|
110
|
+
{
|
|
111
|
+
notification: headers["x-notificationstatus"],
|
|
112
|
+
notification_channel: headers["x-subscriptionstatus"],
|
|
113
|
+
device_connection: headers["x-deviceconnectionstatus"]
|
|
114
|
+
}
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def notification_to_xml
|
|
118
|
+
msg = @notification.alert.gsub(/&/, "&").gsub(/</, "<") \
|
|
119
|
+
.gsub(/>/, ">").gsub(/'/, "'").gsub(/"/, """)
|
|
120
|
+
<<-EOF
|
|
121
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
122
|
+
<wp:Notification xmlns:wp="WPNotification">
|
|
123
|
+
<wp:Toast>
|
|
124
|
+
<wp:Text1>#{msg}</wp:Text1>
|
|
125
|
+
</wp:Toast>
|
|
126
|
+
</wp:Notification>
|
|
127
|
+
EOF
|
|
128
|
+
end
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Deprecatable
|
|
3
|
+
def self.included(base)
|
|
4
|
+
base.extend ClassMethods
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
module ClassMethods
|
|
8
|
+
def deprecated(method_name, version, msg=nil)
|
|
9
|
+
instance_eval do
|
|
10
|
+
alias_method "#{method_name}_without_warning", method_name
|
|
11
|
+
end
|
|
12
|
+
warning = "#{method_name} is deprecated and will be removed from Rpush #{version}."
|
|
13
|
+
warning << " #{msg}" if msg
|
|
14
|
+
class_eval(<<-RUBY, __FILE__, __LINE__)
|
|
15
|
+
def #{method_name}(*args, &blk)
|
|
16
|
+
Rpush::Deprecation.warn(#{warning.inspect})
|
|
17
|
+
#{method_name}_without_warning(*args, &blk)
|
|
18
|
+
end
|
|
19
|
+
RUBY
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
class Deprecation
|
|
3
|
+
def self.muted
|
|
4
|
+
begin
|
|
5
|
+
orig_val = Thread.current[:rpush_mute_deprecations]
|
|
6
|
+
Thread.current[:rpush_mute_deprecations] = true
|
|
7
|
+
yield
|
|
8
|
+
ensure
|
|
9
|
+
Thread.current[:rpush_mute_deprecations] = orig_val
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.muted?
|
|
14
|
+
Thread.current[:rpush_mute_deprecations] == true
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.warn(msg)
|
|
18
|
+
unless Rpush::Deprecation.muted?
|
|
19
|
+
STDERR.puts "DEPRECATION WARNING: #{msg}"
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/rpush/embed.rb
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
def self.embed(options = {})
|
|
3
|
+
Rpush.require_for_daemon
|
|
4
|
+
|
|
5
|
+
config = Rpush::ConfigurationWithoutDefaults.new
|
|
6
|
+
options.each { |k, v| config.send("#{k}=", v) }
|
|
7
|
+
config.embedded = true
|
|
8
|
+
Rpush.config.update(config)
|
|
9
|
+
Rpush::Daemon.start
|
|
10
|
+
|
|
11
|
+
Kernel.at_exit { shutdown }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.shutdown
|
|
15
|
+
return unless Rpush.config.embedded
|
|
16
|
+
Rpush::Daemon.shutdown
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.sync
|
|
20
|
+
return unless Rpush.config.embedded
|
|
21
|
+
Rpush::Daemon::AppRunner.sync
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def self.debug
|
|
25
|
+
return unless Rpush.config.embedded
|
|
26
|
+
Rpush::Daemon::AppRunner.debug
|
|
27
|
+
end
|
|
28
|
+
end
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Gcm
|
|
3
|
+
class ExpiryCollapseKeyMutualInclusionValidator < ActiveModel::Validator
|
|
4
|
+
def validate(record)
|
|
5
|
+
if record.collapse_key && !record.expiry
|
|
6
|
+
record.errors[:expiry] << "must be set when using a collapse_key"
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module Gcm
|
|
3
|
+
class Notification < Rpush::Notification
|
|
4
|
+
validates :registration_ids, :presence => true
|
|
5
|
+
|
|
6
|
+
validates_with Rpush::PayloadDataSizeValidator, limit: 4096
|
|
7
|
+
validates_with Rpush::RegistrationIdsCountValidator, limit: 1000
|
|
8
|
+
|
|
9
|
+
validates_with Rpush::Gcm::ExpiryCollapseKeyMutualInclusionValidator
|
|
10
|
+
|
|
11
|
+
def as_json
|
|
12
|
+
json = {
|
|
13
|
+
'registration_ids' => registration_ids,
|
|
14
|
+
'delay_while_idle' => delay_while_idle,
|
|
15
|
+
'data' => data
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
if collapse_key
|
|
19
|
+
json['collapse_key'] = collapse_key
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
if expiry
|
|
23
|
+
json['time_to_live'] = expiry
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
json
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
data/lib/rpush/logger.rb
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
class Logger
|
|
3
|
+
def initialize(options)
|
|
4
|
+
@options = options
|
|
5
|
+
|
|
6
|
+
begin
|
|
7
|
+
log_dir = File.join(Rails.root, 'log')
|
|
8
|
+
FileUtils.mkdir_p(log_dir)
|
|
9
|
+
log = File.open(File.join(log_dir, 'rpush.log'), 'a')
|
|
10
|
+
log.sync = true
|
|
11
|
+
setup_logger(log)
|
|
12
|
+
rescue Errno::ENOENT, Errno::EPERM => e
|
|
13
|
+
@logger = nil
|
|
14
|
+
error(e)
|
|
15
|
+
error('Logging disabled.')
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def info(msg)
|
|
20
|
+
log(:info, msg)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def error(msg)
|
|
24
|
+
log(:error, msg, 'ERROR', STDERR)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def warn(msg)
|
|
28
|
+
log(:warn, msg, 'WARNING', STDERR)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
private
|
|
32
|
+
|
|
33
|
+
def setup_logger(log)
|
|
34
|
+
if Rpush.config.logger
|
|
35
|
+
@logger = Rpush.config.logger
|
|
36
|
+
elsif ActiveSupport.const_defined?('BufferedLogger')
|
|
37
|
+
@logger = ActiveSupport::BufferedLogger.new(log, Rails.logger.level)
|
|
38
|
+
@logger.auto_flushing = Rails.logger.respond_to?(:auto_flushing) ? Rails.logger.auto_flushing : true
|
|
39
|
+
else
|
|
40
|
+
@logger = ActiveSupport::Logger.new(log, Rails.logger.level)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def log(where, msg, prefix = nil, io = STDOUT)
|
|
45
|
+
if msg.is_a?(Exception)
|
|
46
|
+
formatted_backtrace = msg.backtrace.join("\n")
|
|
47
|
+
msg = "#{msg.class.name}, #{msg.message}\n#{formatted_backtrace}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
formatted_msg = "[#{Time.now.to_s(:db)}] "
|
|
51
|
+
formatted_msg << "[#{prefix}] " if prefix
|
|
52
|
+
formatted_msg << msg
|
|
53
|
+
|
|
54
|
+
if io == STDERR
|
|
55
|
+
io.puts formatted_msg
|
|
56
|
+
elsif @options[:foreground]
|
|
57
|
+
io.puts formatted_msg
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
@logger.send(where, formatted_msg) if @logger
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
module MultiJsonHelper
|
|
3
|
+
def multi_json_load(string, options = {})
|
|
4
|
+
# Calling load on multi_json less than v1.3.0 attempts to load a file from disk.
|
|
5
|
+
if Gem.loaded_specs['multi_json'].version >= Gem::Version.create('1.3.0')
|
|
6
|
+
MultiJson.load(string, options)
|
|
7
|
+
else
|
|
8
|
+
MultiJson.decode(string, options)
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def multi_json_dump(string, options = {})
|
|
13
|
+
MultiJson.respond_to?(:dump) ? MultiJson.dump(string, options) : MultiJson.encode(string, options)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
module Rpush
|
|
2
|
+
class Notification < ActiveRecord::Base
|
|
3
|
+
include Rpush::MultiJsonHelper
|
|
4
|
+
|
|
5
|
+
self.table_name = 'rpush_notifications'
|
|
6
|
+
|
|
7
|
+
# TODO: Dump using multi json.
|
|
8
|
+
serialize :registration_ids
|
|
9
|
+
|
|
10
|
+
belongs_to :app, :class_name => 'Rpush::App'
|
|
11
|
+
|
|
12
|
+
if Rpush.attr_accessible_available?
|
|
13
|
+
attr_accessible :badge, :device_token, :sound, :alert, :data, :expiry,:delivered,
|
|
14
|
+
:delivered_at, :failed, :failed_at, :error_code, :error_description, :deliver_after,
|
|
15
|
+
:alert_is_json, :app, :app_id, :collapse_key, :delay_while_idle, :registration_ids, :uri
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
validates :expiry, :numericality => true, :allow_nil => true
|
|
19
|
+
validates :app, :presence => true
|
|
20
|
+
|
|
21
|
+
scope :ready_for_delivery, lambda {
|
|
22
|
+
where('delivered = ? AND failed = ? AND (deliver_after IS NULL OR deliver_after < ?)',
|
|
23
|
+
false, false, Time.now)
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
scope :for_apps, lambda { |apps|
|
|
27
|
+
where('app_id IN (?)', apps.map(&:id))
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
scope :completed, lambda { where("delivered = ? OR failed = ?", true, true) }
|
|
31
|
+
|
|
32
|
+
def data=(attrs)
|
|
33
|
+
return unless attrs
|
|
34
|
+
raise ArgumentError, "must be a Hash" if !attrs.is_a?(Hash)
|
|
35
|
+
write_attribute(:data, multi_json_dump(attrs.merge(data || {})))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def registration_ids=(ids)
|
|
39
|
+
ids = [ids] if ids && !ids.is_a?(Array)
|
|
40
|
+
super
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def data
|
|
44
|
+
multi_json_load(read_attribute(:data)) if read_attribute(:data)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def payload
|
|
48
|
+
multi_json_dump(as_json)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def payload_size
|
|
52
|
+
payload.bytesize
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def payload_data_size
|
|
56
|
+
multi_json_dump(as_json['data']).bytesize
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class << self
|
|
60
|
+
def created_before(dt)
|
|
61
|
+
where("created_at < ?", dt)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def completed_and_older_than(dt)
|
|
65
|
+
completed.created_before(dt)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
end
|