apn_sender 1.0.5 → 1.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +5 -0
- data/README.md +158 -0
- data/Rakefile +27 -48
- data/lib/apn/connection/base.rb +9 -3
- data/lib/apn/feedback.rb +15 -24
- data/lib/apn/notification.rb +17 -26
- data/lib/apn/notification_job.rb +2 -1
- data/lib/apn/sender.rb +5 -5
- data/lib/apn/sender_daemon.rb +3 -0
- data/lib/apn/tasks.rb +3 -10
- data/lib/apn/version.rb +4 -0
- metadata +79 -84
- data/.document +0 -5
- data/README.rdoc +0 -137
- data/VERSION +0 -1
- data/apn_sender.gemspec +0 -69
- data/contrib/apn_sender.monitrc +0 -22
- data/generators/apn_sender_generator.rb +0 -9
- data/generators/templates/script +0 -10
- data/init.rb +0 -1
- data/rails/init.rb +0 -1
- data/test/helper.rb +0 -10
- data/test/test_apple_push_notification.rb +0 -7
data/CHANGELOG
ADDED
data/README.md
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
## UPDATE May 3, 2013: current status
|
2
|
+
|
3
|
+
This project was not supported for a while, but we are going to support it again.
|
4
|
+
|
5
|
+
-----
|
6
|
+
|
7
|
+
## Synopsis
|
8
|
+
|
9
|
+
Need to send background notifications to an iPhone application over a <em>persistent</em> connection in Ruby? Keep reading...
|
10
|
+
|
11
|
+
## The Story
|
12
|
+
|
13
|
+
So you're building the server component of an iPhone application in Ruby. And you want to send background notifications through the Apple Push Notification servers, which doesn't seem too bad at first. But then you read in the [Apple Documentation](https://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Introduction.html) that Apple's servers may treat non-persistent connections as a Denial of Service attack, and you realize that Rails has no easy way to maintain a persistent connection internally, and things start looking more complicated.
|
14
|
+
|
15
|
+
The apn_sender gem includes a background daemon which processes background messages from your application and sends them along to Apple <em>over a single, persistent socket</em>. It also includes the ability to query the Feedback service, helper methods for enqueueing your jobs, and a sample monit config to make sure the background worker is around when you need it.
|
16
|
+
|
17
|
+
## Yet another ApplePushNotification interface?
|
18
|
+
|
19
|
+
Yup. There's some great code out there already, but we didn't like the idea of getting banned from the APN gateway for establishing a new connection each time we needed to send a batch of messages, and none of the libraries I found handled maintaining a persistent connection.
|
20
|
+
|
21
|
+
## Current Status
|
22
|
+
This gem has been in production on 500px,sending million of notifications.
|
23
|
+
|
24
|
+
## Usage
|
25
|
+
|
26
|
+
### 1. Queueing Messages From Your Application
|
27
|
+
|
28
|
+
To queue a message for sending through Apple's Push Notification service from your Rails application:
|
29
|
+
|
30
|
+
```
|
31
|
+
APN.notify(token, opts_hash)
|
32
|
+
```
|
33
|
+
|
34
|
+
where ```token``` is the unique identifier of the iPhone to receive the notification and ```opts_hash``` can have any of the following keys:
|
35
|
+
|
36
|
+
Options:
|
37
|
+
|
38
|
+
* :alert ## The alert to send
|
39
|
+
* :badge ## The badge number to send
|
40
|
+
* :sound ## The sound file to play on receipt, or true to play the default sound installed with your app
|
41
|
+
|
42
|
+
|
43
|
+
If any other keys are present they'll be be passed along as custom data to your application.
|
44
|
+
|
45
|
+
### 2. Sending Queued Messages
|
46
|
+
|
47
|
+
Put your ```apn_development.pem``` and ```apn_production.pem``` certificates from Apple in your ```RAILS_ROOT/config/certs``` directory.
|
48
|
+
|
49
|
+
Once this is done, you can fire off a background worker with
|
50
|
+
|
51
|
+
```
|
52
|
+
$ rake apn:sender
|
53
|
+
```
|
54
|
+
|
55
|
+
For production, you're probably better off running a dedicated daemon and setting up monit to watch over it for you. Luckily, that's pretty easy:
|
56
|
+
|
57
|
+
```
|
58
|
+
# To generate daemon
|
59
|
+
./script/generate apn_sender
|
60
|
+
|
61
|
+
# To run daemon. Pass --help to print all options
|
62
|
+
./script/apn_sender --environment#production --verbose start
|
63
|
+
```
|
64
|
+
|
65
|
+
Note the --environment must be explicitly set (separately from your <code>Rails.env</code>) to production in order to send messages via the production APN servers. Any other environment sends messages through Apple's sandbox servers at <code>gateway.sandbox.push.apple.com</code>.
|
66
|
+
|
67
|
+
Also, there are two similar options: ```:cert_path``` and ```:full_cert_path```. The former specifies the directory in which to find the .pem file (either apn_production.pem or apn_development.pem, depending on the environment). The latter specifies a .pem file explicitly, allowing customized certificate names if needed.
|
68
|
+
|
69
|
+
Check ```logs/apn_sender.log``` for debugging output. In addition to logging any major errors there, apn_sender hooks into the Resque::Worker logging to display any verbose or very_verbose worker output in apn_sender.log file as well.
|
70
|
+
|
71
|
+
|
72
|
+
### 3. Checking Apple's Feedback Service
|
73
|
+
|
74
|
+
Since push notifications are a fire-and-forget sorta deal, where you get no indication if your message was received (or if the specified recipient even exists), Apple needed to come up with some other way to ensure their network isn't clogged with thousands of bogus messages (e.g. from developers sending messages to phones where their application <em>used</em> to be installed, but where the user has since removed it). Hence, the Feedback Service.
|
75
|
+
|
76
|
+
It's actually really simple - you connect to them periodically and they give you a big dump of tokens you shouldn't send to anymore. The gem wraps this up nicely -- just call:
|
77
|
+
|
78
|
+
```
|
79
|
+
# APN::Feedback accepts the same optional :environment
|
80
|
+
# and :cert_path / :full_cert_path options as APN::Sender
|
81
|
+
feedback # APN::Feedback.new()
|
82
|
+
|
83
|
+
tokens # feedback.tokens # #> Array of device tokens
|
84
|
+
tokens.each do |token|
|
85
|
+
# ... custom logic here to stop you app from
|
86
|
+
# sending further notifications to this token
|
87
|
+
end
|
88
|
+
```
|
89
|
+
|
90
|
+
If you're interested in knowing exactly <em>when</em> Apple determined each token was expired (which can be useful in determining if the application re-registered with your service since it first appeared in the expired queue):
|
91
|
+
|
92
|
+
```
|
93
|
+
items # feedback.data # #> Array of APN::FeedbackItem elements
|
94
|
+
items.each do |item|
|
95
|
+
item.token
|
96
|
+
item.timestamp
|
97
|
+
# ... custom logic here
|
98
|
+
end
|
99
|
+
```
|
100
|
+
|
101
|
+
The Feedback Service works as a big queue. When you connect it pops off all its data and sends it over the wire at once, which means connecting a second time will return an empty array, so for ease of use a call to either +tokens+ or +data+ will connect once and cache the data. If you call either one again it'll continue to use its cached version (rather than connecting to Apple a second time to retrieve an empty array, which is probably not what you want).
|
102
|
+
|
103
|
+
Forcing a reconnect is as easy as calling either method with the single parameter +true+, but be sure you've already used the existing data because you'll never get it back.
|
104
|
+
|
105
|
+
|
106
|
+
#### Warning: No really, check Apple's Feedback Service occasionally
|
107
|
+
|
108
|
+
If you're sending notifications, you should definitely call one of the ```receive``` methods periodically, as Apple's policies require it and they apparently monitor providers for compliance. I'd definitely recommend throwing together a quick rake task to take care of this for you (the [whenever library](http://github.com/javan/whenever) provides a nice wrapper around scheduling tasks to run at certain times (for systems with cron enabled)).
|
109
|
+
|
110
|
+
Just for the record, this is essentially what you want to have whenever run periodically for you:
|
111
|
+
```
|
112
|
+
def self.clear_uninstalled_applications
|
113
|
+
feedback_data # APN::Feedback.new(:environment #> :production).data
|
114
|
+
|
115
|
+
feedback_data.each do |item|
|
116
|
+
user # User.find_by_iphone_token( item.token )
|
117
|
+
|
118
|
+
if user.iphone_token_updated_at && user.iphone_token_updated_at > item.timestamp
|
119
|
+
return true # App has been reregistered since Apple determined it'd been uninstalled
|
120
|
+
else
|
121
|
+
user.update_attributes(:iphone_token #> nil, :iphone_token_updated_at #> Time.now)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
```
|
126
|
+
|
127
|
+
|
128
|
+
### Keeping Your Workers Working
|
129
|
+
|
130
|
+
There's also an included sample ```apn_sender.monitrc``` file in the ```contrib/``` folder to help monit handle server restarts and unexpected disasters.
|
131
|
+
|
132
|
+
|
133
|
+
## Installation
|
134
|
+
|
135
|
+
APN is built on top of [Resque](http://github.com/defunkt/resque) (an awesome [Redis](http://code.google.com/p/redis/)-based background runner similar to [delayed_job](http://github.com/collectiveidea/delayed_job)). Read through the [Resque README](http://github.com/defunkt/resque#readme) to get a feel for what's going on, follow the installation instructions there, and then run:
|
136
|
+
|
137
|
+
```
|
138
|
+
$ gem install apn_sender
|
139
|
+
```
|
140
|
+
In your Rails app, add (2.3.x):
|
141
|
+
|
142
|
+
```
|
143
|
+
config.gem 'apn_sender', :lib => 'apn'
|
144
|
+
```
|
145
|
+
or (3.x) to your Gemfile:
|
146
|
+
|
147
|
+
```
|
148
|
+
gem 'apn_sender', require: 'apn'
|
149
|
+
```
|
150
|
+
To add a few useful rake tasks for running workers, add the following line to your Rakefile:
|
151
|
+
|
152
|
+
```
|
153
|
+
require 'apn/tasks'
|
154
|
+
```
|
155
|
+
|
156
|
+
## Copyright
|
157
|
+
|
158
|
+
Copyright (c) 2010 Kali Donovan. See LICENSE for details.
|
data/Rakefile
CHANGED
@@ -1,56 +1,35 @@
|
|
1
|
-
require
|
2
|
-
|
3
|
-
|
4
|
-
load 'lib/apn/tasks.rb'
|
5
|
-
|
6
|
-
begin
|
7
|
-
require 'jeweler'
|
8
|
-
Jeweler::Tasks.new do |gem|
|
9
|
-
gem.name = "apn_sender"
|
10
|
-
gem.summary = %Q{Resque-based background worker to send Apple Push Notifications over a persistent TCP socket.}
|
11
|
-
gem.description = %Q{Resque-based background worker to send Apple Push Notifications over a persistent TCP socket. Includes Resque tweaks to allow persistent sockets between jobs, helper methods for enqueueing APN notifications, and a background daemon to send them.}
|
12
|
-
gem.email = "kali.donovan@gmail.com"
|
13
|
-
gem.homepage = "http://github.com/kdonovan/apple_push_notification"
|
14
|
-
gem.authors = ["Kali Donovan"]
|
15
|
-
gem.add_dependency 'resque'
|
16
|
-
gem.add_dependency 'resque-access_worker_from_job'
|
17
|
-
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
18
|
-
end
|
19
|
-
Jeweler::GemcutterTasks.new
|
20
|
-
rescue LoadError
|
21
|
-
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
22
|
-
end
|
1
|
+
require "bundler"
|
2
|
+
Bundler.setup
|
23
3
|
|
24
|
-
require
|
25
|
-
|
26
|
-
test.libs << 'lib' << 'test'
|
27
|
-
test.pattern = 'test/**/test_*.rb'
|
28
|
-
test.verbose = true
|
29
|
-
end
|
4
|
+
require "rake"
|
5
|
+
require "rspec/core/rake_task"
|
30
6
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
38
|
-
rescue LoadError
|
39
|
-
task :rcov do
|
40
|
-
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
41
|
-
end
|
7
|
+
$LOAD_PATH.unshift File.expand_path("../lib", __FILE__)
|
8
|
+
require "apn/version"
|
9
|
+
|
10
|
+
task :gem => :build
|
11
|
+
task :build do
|
12
|
+
system "gem build apn_sender.gemspec"
|
42
13
|
end
|
43
14
|
|
44
|
-
task :
|
15
|
+
task :install => :build do
|
16
|
+
system "gem install apn_sender-#{APN::VERSION}.gem"
|
17
|
+
end
|
45
18
|
|
46
|
-
task :
|
19
|
+
task :release => :build do
|
20
|
+
system "git tag -a v#{APN::VERSION} -m 'Tagging #{APN::VERSION}'"
|
21
|
+
system "git push --tags"
|
22
|
+
system "gem push apn_sender-#{APN::VERSION}.gem"
|
23
|
+
system "rm apn_sender-#{APN::VERSION}.gem"
|
24
|
+
end
|
47
25
|
|
48
|
-
|
49
|
-
|
50
|
-
|
26
|
+
RSpec::Core::RakeTask.new("spec") do |spec|
|
27
|
+
spec.pattern = "spec/**/*_spec.rb"
|
28
|
+
end
|
51
29
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
30
|
+
RSpec::Core::RakeTask.new('spec:progress') do |spec|
|
31
|
+
spec.rspec_opts = %w(--format progress)
|
32
|
+
spec.pattern = "spec/**/*_spec.rb"
|
56
33
|
end
|
34
|
+
|
35
|
+
task :default => :spec
|
data/lib/apn/connection/base.rb
CHANGED
@@ -29,11 +29,12 @@ module APN
|
|
29
29
|
|
30
30
|
# Default to Rails or Merg logger, if available
|
31
31
|
def setup_logger
|
32
|
-
@logger = if defined?(
|
33
|
-
|
32
|
+
@logger = if defined?(Merb::Logger)
|
33
|
+
Merb.logger
|
34
34
|
elsif defined?(::Rails.logger)
|
35
35
|
::Rails.logger
|
36
36
|
end
|
37
|
+
@logger ||= Logger.new(STDOUT)
|
37
38
|
end
|
38
39
|
|
39
40
|
# Log message to any logger provided by the user (e.g. the Rails logger).
|
@@ -89,7 +90,12 @@ module APN
|
|
89
90
|
|
90
91
|
ctx = OpenSSL::SSL::SSLContext.new
|
91
92
|
ctx.cert = OpenSSL::X509::Certificate.new(@apn_cert)
|
92
|
-
|
93
|
+
|
94
|
+
if @opts[:cert_pass]
|
95
|
+
ctx.key = OpenSSL::PKey::RSA.new(@apn_cert, @opts[:cert_pass])
|
96
|
+
else
|
97
|
+
ctx.key = OpenSSL::PKey::RSA.new(@apn_cert)
|
98
|
+
end
|
93
99
|
|
94
100
|
@socket_tcp = TCPSocket.new(apn_host, apn_port)
|
95
101
|
@socket = OpenSSL::SSL::SSLSocket.new(@socket_tcp, ctx)
|
data/lib/apn/feedback.rb
CHANGED
@@ -16,14 +16,14 @@ module APN
|
|
16
16
|
token
|
17
17
|
end
|
18
18
|
end
|
19
|
-
|
19
|
+
|
20
20
|
# When supplied with the certificate path and the desired environment, connects to the {APN Feedback Service}[http://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingWIthAPS/CommunicatingWIthAPS.html#//apple_ref/doc/uid/TP40008194-CH101-SW3]
|
21
21
|
# and returns any response as an array of APN::FeedbackItem elements.
|
22
|
-
#
|
22
|
+
#
|
23
23
|
# See README for usage and details.
|
24
24
|
class Feedback
|
25
25
|
include APN::Connection::Base
|
26
|
-
|
26
|
+
|
27
27
|
# Returns array of APN::FeedbackItem elements read from Apple. Connects to Apple once and caches the
|
28
28
|
# data, continues to returns cached data unless called with <code>data(true)</code>, which clears the
|
29
29
|
# existing feedback array. Note that once you force resetting the cache you loose all previous feedback,
|
@@ -32,25 +32,25 @@ module APN
|
|
32
32
|
@feedback = nil if force
|
33
33
|
@feedback ||= receive
|
34
34
|
end
|
35
|
-
|
35
|
+
|
36
36
|
# Wrapper around +data+ returning just an array of token strings.
|
37
37
|
def tokens(force = nil)
|
38
38
|
data(force).map(&:token)
|
39
39
|
end
|
40
|
-
|
40
|
+
|
41
41
|
# Prettify to return meaningful status information when printed. Can't add these directly to connection/base, because Resque depends on decoding to_s
|
42
42
|
def inspect
|
43
43
|
"#<#{self.class.name}: #{to_s}>"
|
44
44
|
end
|
45
|
-
|
45
|
+
|
46
46
|
# Prettify to return meaningful status information when printed. Can't add these directly to connection/base, because Resque depends on decoding to_s
|
47
47
|
def to_s
|
48
48
|
"#{@socket ? 'Connected' : 'Connection not currently established'} to #{apn_host} on #{apn_port}"
|
49
49
|
end
|
50
|
-
|
50
|
+
|
51
51
|
protected
|
52
|
-
|
53
|
-
# Connects to Apple's Feedback Service and checks if there's anything there for us.
|
52
|
+
|
53
|
+
# Connects to Apple's Feedback Service and checks if there's anything there for us.
|
54
54
|
# Returns an array of APN::FeedbackItem pairs
|
55
55
|
def receive
|
56
56
|
feedback = []
|
@@ -59,9 +59,8 @@ module APN
|
|
59
59
|
setup_connection
|
60
60
|
|
61
61
|
# Unpacking code borrowed from http://github.com/jpoz/APNS/blob/master/lib/apns/core.rb
|
62
|
-
while
|
63
|
-
|
64
|
-
f = line.unpack('N1n1H140')
|
62
|
+
while bunch = socket.read(38) # Read data from the socket
|
63
|
+
f = bunch.strip.unpack('N1n1H140')
|
65
64
|
feedback << APN::FeedbackItem.new(Time.at(f[0]), f[2])
|
66
65
|
end
|
67
66
|
|
@@ -70,23 +69,15 @@ module APN
|
|
70
69
|
|
71
70
|
return feedback
|
72
71
|
end
|
73
|
-
|
74
|
-
|
72
|
+
|
73
|
+
|
75
74
|
def apn_host
|
76
75
|
@apn_host ||= apn_production? ? "feedback.push.apple.com" : "feedback.sandbox.push.apple.com"
|
77
76
|
end
|
78
|
-
|
77
|
+
|
79
78
|
def apn_port
|
80
79
|
2196
|
81
80
|
end
|
82
|
-
|
81
|
+
|
83
82
|
end
|
84
83
|
end
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
__END__
|
89
|
-
# Testing from irb
|
90
|
-
irb -r 'lib/apn/feedback'
|
91
|
-
|
92
|
-
a=APN::Feedback.new(:cert_path => '/Users/kali/Code/insurrection/certs/', :environment => :production)
|
data/lib/apn/notification.rb
CHANGED
@@ -10,8 +10,8 @@ module APN
|
|
10
10
|
#
|
11
11
|
# APN::Notification.new(token, {:alert => 'Stuff', :custom => {:code => 23}})
|
12
12
|
# # Writes this JSON to servers: {"aps" => {"alert" => "Stuff"}, "custom" => {"code" => 23}}
|
13
|
-
#
|
14
|
-
# As a shortcut, APN::Notification.new also accepts a string as the second argument, which it converts into the alert to send. The
|
13
|
+
#
|
14
|
+
# As a shortcut, APN::Notification.new also accepts a string as the second argument, which it converts into the alert to send. The
|
15
15
|
# following two lines are equivalent:
|
16
16
|
#
|
17
17
|
# APN::Notification.new(token, 'Some Alert')
|
@@ -19,13 +19,13 @@ module APN
|
|
19
19
|
#
|
20
20
|
class Notification
|
21
21
|
# Available to help clients determine before they create the notification if their message will be too large.
|
22
|
-
# Each iPhone Notification payload must be 256 or fewer characters. Encoding a null message has a 57
|
22
|
+
# Each iPhone Notification payload must be 256 or fewer characters. Encoding a null message has a 57
|
23
23
|
# character overhead, so there are 199 characters available for the alert string.
|
24
|
-
MAX_ALERT_LENGTH = 199
|
25
|
-
|
24
|
+
MAX_ALERT_LENGTH = 199
|
25
|
+
|
26
26
|
attr_accessor :options, :token
|
27
27
|
def initialize(token, opts)
|
28
|
-
@options =
|
28
|
+
@options = opts.is_a?(Hash) ? opts.symbolize_keys : {:alert => opts}
|
29
29
|
@token = token
|
30
30
|
|
31
31
|
raise "The maximum size allowed for a notification payload is 256 bytes." if packaged_notification.size.to_i > 256
|
@@ -34,20 +34,20 @@ module APN
|
|
34
34
|
def to_s
|
35
35
|
packaged_notification
|
36
36
|
end
|
37
|
-
|
37
|
+
|
38
38
|
# Ensures at least one of <code>%w(alert badge sound)</code> is present
|
39
39
|
def valid?
|
40
|
-
return true if
|
40
|
+
return true if [:alert, :badge, :sound].any?{|key| options.keys.include?(key) }
|
41
41
|
false
|
42
42
|
end
|
43
|
-
|
43
|
+
|
44
44
|
protected
|
45
45
|
|
46
46
|
# Completed encoded notification, ready to send down the wire to Apple
|
47
47
|
def packaged_notification
|
48
48
|
pt = packaged_token
|
49
49
|
pm = packaged_message
|
50
|
-
[0, 0, 32, pt, 0, pm.size, pm].pack("ccca*cca*")
|
50
|
+
[0, 0, 32, pt, 0, pm.size, pm].pack("ccca*cca*")
|
51
51
|
end
|
52
52
|
|
53
53
|
# Device token, compressed and hex-ified
|
@@ -61,7 +61,10 @@ module APN
|
|
61
61
|
def packaged_message
|
62
62
|
opts = @options.clone # Don't destroy our pristine copy
|
63
63
|
hsh = {'aps' => {}}
|
64
|
-
|
64
|
+
if alert = opts.delete(:alert)
|
65
|
+
alert = alert.to_s unless alert.is_a?(Hash)
|
66
|
+
hsh['aps']['alert'] = alert
|
67
|
+
end
|
65
68
|
hsh['aps']['badge'] = opts.delete(:badge).to_i if opts[:badge]
|
66
69
|
if sound = opts.delete(:sound)
|
67
70
|
hsh['aps']['sound'] = sound.is_a?(TrueClass) ? 'default' : sound.to_s
|
@@ -69,18 +72,6 @@ module APN
|
|
69
72
|
hsh.merge!(opts)
|
70
73
|
ActiveSupport::JSON::encode(hsh)
|
71
74
|
end
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
if hash.respond_to?(:symbolize_keys)
|
76
|
-
return hash.symbolize_keys
|
77
|
-
else
|
78
|
-
hash.inject({}) do |opt, (key, value)|
|
79
|
-
opt[(key.to_sym rescue key) || key] = value
|
80
|
-
opt
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
end
|
86
|
-
end
|
75
|
+
|
76
|
+
end
|
77
|
+
end
|
data/lib/apn/notification_job.rb
CHANGED
@@ -11,7 +11,8 @@ module APN
|
|
11
11
|
msg = APN::Notification.new(token, opts)
|
12
12
|
raise "Invalid notification options (did you provide :alert, :badge, or :sound?): #{opts.inspect}" unless msg.valid?
|
13
13
|
|
14
|
-
worker.
|
14
|
+
raise "APN::NotificationJob was picked up by a non-APN:Sender resque worker. Aborting." unless worker
|
15
|
+
worker.send_to_apple(msg)
|
15
16
|
end
|
16
17
|
|
17
18
|
|
data/lib/apn/sender.rb
CHANGED
@@ -6,14 +6,14 @@ module APN
|
|
6
6
|
# End result: single persistent TCP connection to Apple, so they don't ban you for frequently opening and closing connections,
|
7
7
|
# which they apparently view as a DOS attack.
|
8
8
|
#
|
9
|
-
# Accepts <code>:environment</code> (production vs anything else) and <code>:cert_path</code> options on initialization. If called in a
|
10
|
-
# Rails context,
|
11
|
-
# from Rails.env (production if Rails.env is production, development for any other Rails.env value).
|
12
|
-
#
|
9
|
+
# Accepts <code>:environment</code> (production vs anything else), <code>:cert_pass</code> and <code>:cert_path</code> options on initialization. If called in a
|
10
|
+
# Rails context, will default to RAILS_ENV and RAILS_ROOT/config/certs. :environment will default to development.
|
13
11
|
# APN::Sender expects two files to exist in the specified <code>:cert_path</code> directory:
|
14
12
|
# <code>apn_production.pem</code> and <code>apn_development.pem</code>.
|
15
13
|
#
|
16
|
-
#
|
14
|
+
# Use the <code>:cert_pass</code> option if your certificates require a password
|
15
|
+
#
|
16
|
+
# If a socket error is encountered, will teardown the connection and retry again twice before admitting defeat.
|
17
17
|
class Sender < ::Resque::Worker
|
18
18
|
include APN::Connection::Base
|
19
19
|
TIMES_TO_RETRY_SOCKET_ERROR = 2
|
data/lib/apn/sender_daemon.rb
CHANGED
@@ -32,6 +32,9 @@ module APN
|
|
32
32
|
opts.on('c', '--full-cert-path=NAME', 'Full path to desired .pem certificate (overrides environment selector).') do |path|
|
33
33
|
@options[:full_cert_path] = path
|
34
34
|
end
|
35
|
+
opts.on('--cert-pass=PASSWORD', 'Password for the apn .pem certificates.') do |pass|
|
36
|
+
@options[:cert_pass] = pass
|
37
|
+
end
|
35
38
|
opts.on('-n', '--number-of-workers=WORKERS', "Number of unique workers to spawn") do |worker_count|
|
36
39
|
@options[:worker_count] = worker_count.to_i rescue 1
|
37
40
|
end
|
data/lib/apn/tasks.rb
CHANGED
@@ -8,16 +8,9 @@ namespace :apn do
|
|
8
8
|
task :sender => :setup do
|
9
9
|
require 'apn'
|
10
10
|
|
11
|
-
worker =
|
12
|
-
|
13
|
-
|
14
|
-
worker = APN::Sender.new(:full_cert_path => ENV['FULL_CERT_PATH'], :cert_path => ENV['CERT_PATH'], :environment => ENV['ENVIRONMENT'])
|
15
|
-
worker.verbose = ENV['LOGGING'] || ENV['VERBOSE']
|
16
|
-
worker.very_verbose = ENV['VVERBOSE']
|
17
|
-
rescue Exception => e
|
18
|
-
raise e
|
19
|
-
# abort "set QUEUE env var, e.g. $ QUEUE=critical,high rake resque:work"
|
20
|
-
end
|
11
|
+
worker = APN::Sender.new(:full_cert_path => ENV['FULL_CERT_PATH'], :cert_path => ENV['CERT_PATH'], :environment => ENV['ENVIRONMENT'], :cert_pass => ENV['CERT_PASS'])
|
12
|
+
worker.verbose = ENV['LOGGING'] || ENV['VERBOSE']
|
13
|
+
worker.very_verbose = ENV['VVERBOSE']
|
21
14
|
|
22
15
|
puts "*** Starting worker to send apple notifications in the background from #{worker}"
|
23
16
|
|
data/lib/apn/version.rb
ADDED
metadata
CHANGED
@@ -1,72 +1,74 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: apn_sender
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.6
|
5
5
|
prerelease:
|
6
|
-
segments:
|
7
|
-
- 1
|
8
|
-
- 0
|
9
|
-
- 5
|
10
|
-
version: 1.0.5
|
11
6
|
platform: ruby
|
12
|
-
authors:
|
7
|
+
authors:
|
13
8
|
- Kali Donovan
|
9
|
+
- Arthur Neves
|
14
10
|
autorequire:
|
15
11
|
bindir: bin
|
16
12
|
cert_chain: []
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
dependencies:
|
21
|
-
- !ruby/object:Gem::Dependency
|
13
|
+
date: 2011-05-15 00:00:00.000000000 Z
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
22
16
|
name: resque
|
23
|
-
|
24
|
-
requirement: &id001 !ruby/object:Gem::Requirement
|
17
|
+
requirement: !ruby/object:Gem::Requirement
|
25
18
|
none: false
|
26
|
-
requirements:
|
27
|
-
- -
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
|
30
|
-
segments:
|
31
|
-
- 0
|
32
|
-
version: "0"
|
19
|
+
requirements:
|
20
|
+
- - ! '>='
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '0'
|
33
23
|
type: :runtime
|
34
|
-
|
35
|
-
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
none: false
|
27
|
+
requirements:
|
28
|
+
- - ! '>='
|
29
|
+
- !ruby/object:Gem::Version
|
30
|
+
version: '0'
|
31
|
+
- !ruby/object:Gem::Dependency
|
36
32
|
name: resque-access_worker_from_job
|
33
|
+
requirement: !ruby/object:Gem::Requirement
|
34
|
+
none: false
|
35
|
+
requirements:
|
36
|
+
- - ! '>='
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
type: :runtime
|
37
40
|
prerelease: false
|
38
|
-
|
41
|
+
version_requirements: !ruby/object:Gem::Requirement
|
42
|
+
none: false
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: active_support
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
39
50
|
none: false
|
40
|
-
requirements:
|
41
|
-
- -
|
42
|
-
- !ruby/object:Gem::Version
|
43
|
-
|
44
|
-
segments:
|
45
|
-
- 0
|
46
|
-
version: "0"
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
47
55
|
type: :runtime
|
48
|
-
|
49
|
-
|
50
|
-
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
59
|
+
requirements:
|
60
|
+
- - ! '>='
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '0'
|
63
|
+
description: Resque-based background worker to send Apple Push Notifications over
|
64
|
+
a persistent TCP socket. Includes Resque tweaks to allow persistent sockets between
|
65
|
+
jobs, helper methods for enqueueing APN notifications, and a background daemon to
|
66
|
+
send them.
|
67
|
+
email: arthurnn@gmail.com
|
51
68
|
executables: []
|
52
|
-
|
53
69
|
extensions: []
|
54
|
-
|
55
|
-
|
56
|
-
- LICENSE
|
57
|
-
- README.rdoc
|
58
|
-
files:
|
59
|
-
- .document
|
60
|
-
- LICENSE
|
61
|
-
- README.rdoc
|
62
|
-
- Rakefile
|
63
|
-
- VERSION
|
64
|
-
- apn_sender.gemspec
|
65
|
-
- contrib/apn_sender.monitrc
|
66
|
-
- generators/apn_sender_generator.rb
|
67
|
-
- generators/templates/script
|
68
|
-
- init.rb
|
69
|
-
- lib/apn.rb
|
70
|
+
extra_rdoc_files: []
|
71
|
+
files:
|
70
72
|
- lib/apn/connection/base.rb
|
71
73
|
- lib/apn/feedback.rb
|
72
74
|
- lib/apn/notification.rb
|
@@ -76,44 +78,37 @@ files:
|
|
76
78
|
- lib/apn/sender.rb
|
77
79
|
- lib/apn/sender_daemon.rb
|
78
80
|
- lib/apn/tasks.rb
|
81
|
+
- lib/apn/version.rb
|
82
|
+
- lib/apn.rb
|
79
83
|
- lib/resque/hooks/before_unregister_worker.rb
|
80
|
-
-
|
81
|
-
-
|
82
|
-
-
|
83
|
-
|
84
|
-
homepage: http://github.com/
|
85
|
-
licenses:
|
86
|
-
|
84
|
+
- CHANGELOG
|
85
|
+
- LICENSE
|
86
|
+
- README.md
|
87
|
+
- Rakefile
|
88
|
+
homepage: http://github.com/arthurnn/apn_sender
|
89
|
+
licenses:
|
90
|
+
- MIT
|
87
91
|
post_install_message:
|
88
92
|
rdoc_options: []
|
89
|
-
|
90
|
-
require_paths:
|
93
|
+
require_paths:
|
91
94
|
- lib
|
92
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
96
|
none: false
|
94
|
-
requirements:
|
95
|
-
- -
|
96
|
-
- !ruby/object:Gem::Version
|
97
|
-
|
98
|
-
|
99
|
-
- 0
|
100
|
-
version: "0"
|
101
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ! '>='
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '1.9'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
102
|
none: false
|
103
|
-
requirements:
|
104
|
-
- -
|
105
|
-
- !ruby/object:Gem::Version
|
106
|
-
|
107
|
-
segments:
|
108
|
-
- 0
|
109
|
-
version: "0"
|
103
|
+
requirements:
|
104
|
+
- - ! '>='
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: 1.3.6
|
110
107
|
requirements: []
|
111
|
-
|
112
108
|
rubyforge_project:
|
113
|
-
rubygems_version: 1.
|
109
|
+
rubygems_version: 1.8.24
|
114
110
|
signing_key:
|
115
111
|
specification_version: 3
|
116
|
-
summary: Resque-based background worker to send Apple Push Notifications over a persistent
|
117
|
-
|
118
|
-
|
119
|
-
- test/test_apple_push_notification.rb
|
112
|
+
summary: Resque-based background worker to send Apple Push Notifications over a persistent
|
113
|
+
TCP socket.
|
114
|
+
test_files: []
|
data/.document
DELETED
data/README.rdoc
DELETED
@@ -1,137 +0,0 @@
|
|
1
|
-
== Synopsis
|
2
|
-
|
3
|
-
Need to send background notifications to an iPhone application over a <em>persistent</em> connection in Ruby? Keep reading...
|
4
|
-
|
5
|
-
== The Story
|
6
|
-
|
7
|
-
So you're building the server component of an iPhone application in Ruby. And you want to send background notifications through the Apple Push Notification servers, which doesn't seem too bad at first. But then you read in the {Apple Documentation}[https://developer.apple.com/iphone/library/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/WhatAreRemoteNotif/WhatAreRemoteNotif.html#//apple_ref/doc/uid/TP40008194-CH102-SW7] that Apple's servers may treat non-persistent connections as a Denial of Service attack, and you realize that Rails has no easy way to maintain a persistent connection internally, and things start looking more complicated.
|
8
|
-
|
9
|
-
The apn_sender gem includes a background daemon which processes background messages from your application and sends them along to Apple <em>over a single, persistent socket</em>. It also includes the ability to query the Feedback service, helper methods for enqueueing your jobs, and a sample monit config to make sure the background worker is around when you need it.
|
10
|
-
|
11
|
-
== Yet another ApplePushNotification interface?
|
12
|
-
|
13
|
-
Yup. There's some great code out there already, but we didn't like the idea of getting banned from the APN gateway for establishing a new connection each time we needed to send a batch of messages, and none of the libraries I found handled maintaining a persistent connection.
|
14
|
-
|
15
|
-
== Current Status
|
16
|
-
|
17
|
-
This gem has been in production use since early May, 2010. There are no guarantees of complete functionality, of course, but it's working for us. :)
|
18
|
-
** UPDATE: our site is now defunct (for completely-unrelated reasons), but the many forks and watchers make me think something's going right. **
|
19
|
-
|
20
|
-
== Usage
|
21
|
-
|
22
|
-
=== 1. Queueing Messages From Your Application
|
23
|
-
|
24
|
-
To queue a message for sending through Apple's Push Notification service from your Rails application:
|
25
|
-
|
26
|
-
APN.notify(token, opts_hash)
|
27
|
-
|
28
|
-
where +token+ is the unique identifier of the iPhone to receive the notification and +opts_hash+ can have any of the following keys:
|
29
|
-
|
30
|
-
# :alert #=> The alert to send
|
31
|
-
# :badge #=> The badge number to send
|
32
|
-
# :sound #=> The sound file to play on receipt, or true to play the default sound installed with your app
|
33
|
-
|
34
|
-
If any other keys are present they'll be be passed along as custom data to your application.
|
35
|
-
|
36
|
-
=== 2. Sending Queued Messages
|
37
|
-
|
38
|
-
Put your <code>apn_development.pem</code> and <code>apn_production.pem</code> certificates from Apple in your <code>RAILS_ROOT/config/certs</code> directory.
|
39
|
-
|
40
|
-
Once this is done, you can fire off a background worker with
|
41
|
-
|
42
|
-
$ rake apn:sender
|
43
|
-
|
44
|
-
For production, you're probably better off running a dedicated daemon and setting up monit to watch over it for you. Luckily, that's pretty easy:
|
45
|
-
|
46
|
-
# To generate daemon
|
47
|
-
./script/generate apn_sender
|
48
|
-
|
49
|
-
# To run daemon. Pass --help to print all options
|
50
|
-
./script/apn_sender --environment=production --verbose start
|
51
|
-
|
52
|
-
Note the --environment must be explicitly set (separately from your <code>Rails.env</code>) to production in order to send messages via the production APN servers. Any other environment sends messages through Apple's sandbox servers at <code>gateway.sandbox.push.apple.com</code>.
|
53
|
-
|
54
|
-
Also, there are two similar options <code>:cert_path</code> and <code>:full_cert_path</code>. The former specifies the directory in which to file the .pem file (either apn_production.pem or apn_development.pem, depending on the environment). The latter specifies a .pem file explicitly, allowing customized certificate names for those that need them.
|
55
|
-
|
56
|
-
Check <code>logs/apn_sender.log</code> for debugging output. In addition to logging any major errors there, apn_sender hooks into the Resque::Worker logging to display any verbose or very_verbose worker output in apn_sender.log file as well.
|
57
|
-
|
58
|
-
|
59
|
-
=== 3. Checking Apple's Feedback Service
|
60
|
-
|
61
|
-
Since push notifications are a fire-and-forget sorta deal, where you get no indication if your message was received (or if the specified recipient even exists), Apple needed to come up with some other way to ensure their network isn't clogged with thousands of bogus messages (e.g. from developers sending messages to phones where their application <em>used</em> to be installed, but where the user has since removed it). Hence, the Feedback Service.
|
62
|
-
|
63
|
-
It's actually really simple - you connect to them periodically and they give you a big dump of tokens you shouldn't send to anymore. The gem wraps this up nicely -- just call:
|
64
|
-
|
65
|
-
# APN::Feedback accepts the same optional :environment and :cert_path / :full_cert_path options as APN::Sender
|
66
|
-
feedback = APN::Feedback.new()
|
67
|
-
|
68
|
-
tokens = feedback.tokens # => Array of device tokens
|
69
|
-
tokens.each do |token|
|
70
|
-
# ... custom logic here to stop you app from
|
71
|
-
# sending further notifications to this token
|
72
|
-
end
|
73
|
-
|
74
|
-
If you're interested in knowing exactly <em>when</em> Apple determined each token was expired (which can be useful in determining if the application re-registered with your service since it first appeared in the expired queue):
|
75
|
-
|
76
|
-
items = feedback.data # => Array of APN::FeedbackItem elements
|
77
|
-
items.each do |item|
|
78
|
-
item.token
|
79
|
-
item.timestamp
|
80
|
-
# ... custom logic here
|
81
|
-
end
|
82
|
-
|
83
|
-
The Feedback Service works as a big queue. When you connect it pops off all its data and sends it over the wire at once, which means connecting a second time will return an empty array, so for ease of use a call to either +tokens+ or +data+ will connect once and cache the data. If you call either one again it'll continue to use its cached version (rather than connecting to Apple a second time to retrieve an empty array, which is probably not what you want).
|
84
|
-
|
85
|
-
Forcing a reconnect is as easy as calling either method with the single parameter +true+, but be sure you've already used the existing data because you'll never get it back.
|
86
|
-
|
87
|
-
|
88
|
-
==== Warning: No really, check Apple's Feedback Service occasionally
|
89
|
-
|
90
|
-
If you're sending notifications, you should definitely call one of the <code>receive</code> methods periodically, as Apple's policies require it and they apparently monitor providers for compliance. I'd definitely recommend throwing together a quick rake task to take care of this for you (the {whenever library}[http://github.com/javan/whenever] provides a nice wrapper around scheduling tasks to run at certain times (for systems with cron enabled)).
|
91
|
-
|
92
|
-
Just for the record, this is essentially what you want to have whenever run periodically for you:
|
93
|
-
|
94
|
-
def self.clear_uninstalled_applications
|
95
|
-
feedback_data = APN::Feedback.new(:environment => :production).data
|
96
|
-
|
97
|
-
feedback_data.each do |item|
|
98
|
-
user = User.find_by_iphone_token( item.token )
|
99
|
-
|
100
|
-
if user.iphone_token_updated_at && user.iphone_token_updated_at > item.timestamp
|
101
|
-
return true # App has been reregistered since Apple determined it'd been uninstalled
|
102
|
-
else
|
103
|
-
user.update_attributes(:iphone_token => nil, :iphone_token_updated_at => Time.now)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
=== Keeping Your Workers Working
|
112
|
-
|
113
|
-
There's also an included sample <code>apn_sender.monitrc</code> file in the <code>contrib/</code> folder to help monit handle server restarts and unexpected disasters.
|
114
|
-
|
115
|
-
|
116
|
-
== Installation
|
117
|
-
|
118
|
-
APN is built on top of {Resque}[http://github.com/defunkt/resque] (an awesome {Redis}[http://code.google.com/p/redis/]-based background runner similar to {delayed_job}[http://github.com/collectiveidea/delayed_job]). Read through the {Resque README}[http://github.com/defunkt/resque#readme] to get a feel for what's going on, follow the installation instructions there, and then run:
|
119
|
-
|
120
|
-
$ gem install apn_sender
|
121
|
-
|
122
|
-
In your Rails app, add (2.3.x):
|
123
|
-
|
124
|
-
config.gem 'apn_sender', :lib => 'apn'
|
125
|
-
|
126
|
-
or (3.x) to your Gemfile:
|
127
|
-
|
128
|
-
gem 'apn_sender', :require => 'apn'
|
129
|
-
|
130
|
-
To add a few useful rake tasks for running workers, add the following line to your Rakefile:
|
131
|
-
|
132
|
-
require 'apn/tasks'
|
133
|
-
|
134
|
-
|
135
|
-
== Copyright
|
136
|
-
|
137
|
-
Copyright (c) 2010 Kali Donovan. See LICENSE for details.
|
data/VERSION
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
1.0.5
|
data/apn_sender.gemspec
DELETED
@@ -1,69 +0,0 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
-
# -*- encoding: utf-8 -*-
|
5
|
-
|
6
|
-
Gem::Specification.new do |s|
|
7
|
-
s.name = %q{apn_sender}
|
8
|
-
s.version = "1.0.5"
|
9
|
-
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = ["Kali Donovan"]
|
12
|
-
s.date = %q{2011-05-15}
|
13
|
-
s.description = %q{Resque-based background worker to send Apple Push Notifications over a persistent TCP socket. Includes Resque tweaks to allow persistent sockets between jobs, helper methods for enqueueing APN notifications, and a background daemon to send them.}
|
14
|
-
s.email = %q{kali.donovan@gmail.com}
|
15
|
-
s.extra_rdoc_files = [
|
16
|
-
"LICENSE",
|
17
|
-
"README.rdoc"
|
18
|
-
]
|
19
|
-
s.files = [
|
20
|
-
".document",
|
21
|
-
"LICENSE",
|
22
|
-
"README.rdoc",
|
23
|
-
"Rakefile",
|
24
|
-
"VERSION",
|
25
|
-
"apn_sender.gemspec",
|
26
|
-
"contrib/apn_sender.monitrc",
|
27
|
-
"generators/apn_sender_generator.rb",
|
28
|
-
"generators/templates/script",
|
29
|
-
"init.rb",
|
30
|
-
"lib/apn.rb",
|
31
|
-
"lib/apn/connection/base.rb",
|
32
|
-
"lib/apn/feedback.rb",
|
33
|
-
"lib/apn/notification.rb",
|
34
|
-
"lib/apn/notification_job.rb",
|
35
|
-
"lib/apn/queue_manager.rb",
|
36
|
-
"lib/apn/queue_name.rb",
|
37
|
-
"lib/apn/sender.rb",
|
38
|
-
"lib/apn/sender_daemon.rb",
|
39
|
-
"lib/apn/tasks.rb",
|
40
|
-
"lib/resque/hooks/before_unregister_worker.rb",
|
41
|
-
"rails/init.rb",
|
42
|
-
"test/helper.rb",
|
43
|
-
"test/test_apple_push_notification.rb"
|
44
|
-
]
|
45
|
-
s.homepage = %q{http://github.com/kdonovan/apple_push_notification}
|
46
|
-
s.require_paths = ["lib"]
|
47
|
-
s.rubygems_version = %q{1.5.2}
|
48
|
-
s.summary = %q{Resque-based background worker to send Apple Push Notifications over a persistent TCP socket.}
|
49
|
-
s.test_files = [
|
50
|
-
"test/helper.rb",
|
51
|
-
"test/test_apple_push_notification.rb"
|
52
|
-
]
|
53
|
-
|
54
|
-
if s.respond_to? :specification_version then
|
55
|
-
s.specification_version = 3
|
56
|
-
|
57
|
-
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
58
|
-
s.add_runtime_dependency(%q<resque>, [">= 0"])
|
59
|
-
s.add_runtime_dependency(%q<resque-access_worker_from_job>, [">= 0"])
|
60
|
-
else
|
61
|
-
s.add_dependency(%q<resque>, [">= 0"])
|
62
|
-
s.add_dependency(%q<resque-access_worker_from_job>, [">= 0"])
|
63
|
-
end
|
64
|
-
else
|
65
|
-
s.add_dependency(%q<resque>, [">= 0"])
|
66
|
-
s.add_dependency(%q<resque-access_worker_from_job>, [">= 0"])
|
67
|
-
end
|
68
|
-
end
|
69
|
-
|
data/contrib/apn_sender.monitrc
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
# An example Monit configuration file for running the apn_sender background daemon
|
2
|
-
#
|
3
|
-
# 1. Replace #{app_name} with your application name
|
4
|
-
# 2. Add any arguments between apn_sender the and start/stop command
|
5
|
-
# 3. Install as a monitrc file (paste to bottom of /etc/monit/monitrc, or save as a .monitrc file and include in the main config)
|
6
|
-
|
7
|
-
check process redis
|
8
|
-
with pidfile /var/run/redis.pid
|
9
|
-
group apn_sender
|
10
|
-
start program = "/usr/bin/redis-server /etc/redis/redis.conf"
|
11
|
-
stop program = "/bin/echo SHUTDOWN | nc localhost 6379"
|
12
|
-
if failed host 127.0.0.1 port 6379 then restart
|
13
|
-
if 5 restarts within 5 cycles then timeout
|
14
|
-
|
15
|
-
|
16
|
-
check process apn_sender
|
17
|
-
with pidfile /var/www/#{app_name}/shared/pids/apn_sender.pid
|
18
|
-
group apn_sender
|
19
|
-
start program = "/var/www/#{app_name}/current/script/apn_sender --environment=production --verbose start"
|
20
|
-
stop program = "/var/www/{#app_name}/current/script/apn_sender --environment=production --verbose stop"
|
21
|
-
if 2 restarts within 3 cycles then timeout
|
22
|
-
depends_on redis
|
data/generators/templates/script
DELETED
@@ -1,10 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
# Daemons sets pwd to /, so we have to explicitly set RAILS_ROOT
|
4
|
-
RAILS_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
5
|
-
|
6
|
-
require 'rubygems'
|
7
|
-
require 'apn'
|
8
|
-
require 'apn/sender_daemon'
|
9
|
-
|
10
|
-
APN::SenderDaemon.new(ARGV).daemonize
|
data/init.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require File.dirname(__FILE__) + "/rails/init.rb"
|
data/rails/init.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require "apn"
|
data/test/helper.rb
DELETED