markbates-apn_on_rails 0.0.1.20090723180641 → 0.1.1.20090724152309
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.
- data/README +57 -30
- data/lib/apn_on_rails/apn_on_rails.rb +4 -3
- data/lib/apn_on_rails/app/models/apn/device.rb +7 -0
- data/lib/apn_on_rails/app/models/apn/notification.rb +50 -16
- data/lib/apn_on_rails/db/migrate/20090723132058_create_apn_devices.rb +1 -1
- data/lib/apn_on_rails/db/migrate/20090723132059_create_apn_notifications.rb +1 -1
- data/lib/apn_on_rails/tasks/apn.rake +1 -6
- metadata +2 -3
- data/lib/apn_on_rails/app/models/apple_push_notification.rb +0 -94
data/README
CHANGED
@@ -1,16 +1,17 @@
|
|
1
|
-
APN on Rails
|
2
|
-
|
3
|
-
|
1
|
+
=APN on Rails (Apple Push Notifications on Rails)
|
2
|
+
|
3
|
+
APN on Rails is a Ruby on Rails gem that allows you to easily add Apple Push Notification (iPhone)
|
4
|
+
support to your Rails application.
|
5
|
+
|
6
|
+
==Acknowledgements:
|
4
7
|
|
5
|
-
Acknowledgements
|
6
|
-
----------------
|
7
8
|
This gem is a re-write of a plugin that was written by Fabien Penso and Sam Soffes.
|
8
9
|
Their plugin was a great start, but it just didn't quite reach the level I hoped it would.
|
9
10
|
I've re-written, as a gem, added a ton of tests, and I would like to think that I made it
|
10
11
|
a little nicer and easier to use.
|
11
12
|
|
12
|
-
Converting Your Certificate
|
13
|
-
|
13
|
+
==Converting Your Certificate:
|
14
|
+
|
14
15
|
Once you have the certificate from Apple for your application, export your key
|
15
16
|
and the apple certificate as p12 files. Here is a quick walkthrough on how to do this:
|
16
17
|
|
@@ -26,28 +27,38 @@ Put 'apple_push_notification_production.pem' in config/
|
|
26
27
|
|
27
28
|
If you are using a development certificate, then change the name to apple_push_notification_development.pem instead.
|
28
29
|
|
29
|
-
Installing
|
30
|
-
|
30
|
+
==Installing:
|
31
|
+
|
32
|
+
===Stable (RubyForge):
|
31
33
|
|
32
|
-
From RubyForge:
|
33
34
|
$ sudo gem install apn_on_rails
|
34
35
|
|
35
|
-
|
36
|
+
===Edge (GitHub):
|
37
|
+
|
36
38
|
$ sudo gem install markbates-apn_on_rails --source=http://gems.github.com
|
37
|
-
|
38
|
-
|
39
|
-
require 'apn_on_rails'
|
39
|
+
|
40
|
+
===Rails Gem Management:
|
40
41
|
|
41
42
|
If you like to use the built in Rails gem management:
|
43
|
+
|
42
44
|
config.gem 'apn_on_rails'
|
43
45
|
|
44
46
|
Or, if you like to live on the edge:
|
47
|
+
|
45
48
|
config.gem 'markbates-apn_on_rails', :lib => 'apn_on_rails', :source => 'http://gems.github.com'
|
46
|
-
|
47
|
-
Setup
|
48
|
-
|
49
|
-
Once you have the gem installed
|
49
|
+
|
50
|
+
==Setup and Configuration:
|
51
|
+
|
52
|
+
Once you have the gem installed via your favorite gem installation, you need to require it so you can
|
53
|
+
start to use it:
|
54
|
+
|
55
|
+
Add the following require, wherever it makes sense to you:
|
56
|
+
|
57
|
+
require 'apn_on_rails'
|
58
|
+
|
59
|
+
You also need to add the following to your Rakefile so you can use the
|
50
60
|
Rake tasks that ship with APN on Rails:
|
61
|
+
|
51
62
|
begin
|
52
63
|
require 'apn_on_rails_tasks'
|
53
64
|
rescue MissingSourceFile => e
|
@@ -55,22 +66,38 @@ Rake tasks that ship with APN on Rails:
|
|
55
66
|
end
|
56
67
|
|
57
68
|
Now, to create the tables you need for APN on Rails, run the following task:
|
69
|
+
|
58
70
|
$ rake apn:db:migrate
|
59
71
|
|
72
|
+
APN on Rails uses the Configatron gem, http://github.com/markbates/configatron/tree/master,
|
73
|
+
to configure itself. APN on Rails has the following default configurations that you change as you
|
74
|
+
see fit:
|
75
|
+
|
76
|
+
configatron.apn.passphrase # => ''
|
77
|
+
configatron.apn.port # => 2195
|
78
|
+
configatron.apn.host # => 'gateway.sandbox.push.apple.com'
|
79
|
+
configatron.apn.cert #=> File.join(RAILS_ROOT, 'config', 'apple_push_notification_development.pem')
|
80
|
+
|
81
|
+
# production:
|
82
|
+
configatron.apn.host # => 'gateway.push.apple.com'
|
83
|
+
configatron.apn.cert #=> File.join(RAILS_ROOT, 'config', 'apple_push_notification_production.pem')
|
84
|
+
|
60
85
|
That's it, now you're ready to start creating notifications.
|
61
86
|
|
62
|
-
Example
|
63
|
-
|
87
|
+
==Example:
|
88
|
+
|
89
|
+
$ ./script/console
|
90
|
+
>> device = APN::Device.create(:token => "XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX")
|
91
|
+
>> notification = APN::Notification.new
|
92
|
+
>> notification.device = device
|
93
|
+
>> notification.badge = 5
|
94
|
+
>> notification.sound = true
|
95
|
+
>> notification.alert = "foobar"
|
96
|
+
>> notification.save
|
97
|
+
>> APN::Notification.send_notifications
|
64
98
|
|
65
|
-
|
99
|
+
You can also run the following Rake task to send your notifications:
|
66
100
|
|
67
|
-
|
68
|
-
>> a = ApplePushNotification.new
|
69
|
-
>> a.device_token = "XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX XXXXXXXX"
|
70
|
-
>> a.badge = 5
|
71
|
-
>> a.sound = true
|
72
|
-
>> a.alert = "foobar"
|
73
|
-
>> a.send_notification
|
74
|
-
=> nil
|
101
|
+
$ rake apn:notifications:deliver
|
75
102
|
|
76
|
-
|
103
|
+
Released under the MIT license.
|
@@ -22,13 +22,14 @@ if rails_env == 'production'
|
|
22
22
|
configatron.apn.set_default(:cert, File.join(rails_root, 'config', 'apple_push_notification_production.pem'))
|
23
23
|
end
|
24
24
|
|
25
|
-
module APN
|
25
|
+
module APN # :nodoc:
|
26
26
|
|
27
|
-
module Errors
|
27
|
+
module Errors # :nodoc:
|
28
28
|
|
29
|
+
# Raised when a notification message to Apple is longer than 256 bytes.
|
29
30
|
class ExceededMessageSizeError < StandardError
|
30
31
|
|
31
|
-
def initialize(message)
|
32
|
+
def initialize(message) # :nodoc:
|
32
33
|
super("The maximum size allowed for a notification payload is 256 bytes: '#{message}'")
|
33
34
|
end
|
34
35
|
|
@@ -1,3 +1,8 @@
|
|
1
|
+
# Represents an iPhone (or other APN enabled device).
|
2
|
+
# An APN::Device can have many APN::Notification.
|
3
|
+
#
|
4
|
+
# Example:
|
5
|
+
# Device.create(:token => '5gxadhy6 6zmtxfl6 5zpbcxmw ez3w7ksf qscpr55t trknkzap 7yyt45sc g6jrw7qz')
|
1
6
|
class APN::Device < ActiveRecord::Base
|
2
7
|
set_table_name 'apn_devices'
|
3
8
|
|
@@ -6,6 +11,8 @@ class APN::Device < ActiveRecord::Base
|
|
6
11
|
validates_uniqueness_of :token
|
7
12
|
validates_format_of :token, :with => /^[a-z0-9]{8}\s[a-z0-9]{8}\s[a-z0-9]{8}\s[a-z0-9]{8}\s[a-z0-9]{8}\s[a-z0-9]{8}\s[a-z0-9]{8}\s[a-z0-9]{8}$/
|
8
13
|
|
14
|
+
# Stores the token (Apple's device ID) of the iPhone (device).
|
15
|
+
#
|
9
16
|
# If the token comes in like this:
|
10
17
|
# '<5gxadhy6 6zmtxfl6 5zpbcxmw ez3w7ksf qscpr55t trknkzap 7yyt45sc g6jrw7qz>'
|
11
18
|
# Then the '<' and '>' will be stripped off.
|
@@ -1,9 +1,31 @@
|
|
1
|
+
# Represents the message you wish to send.
|
2
|
+
# An APN::Notification belongs to an APN::Device.
|
3
|
+
#
|
4
|
+
# Example:
|
5
|
+
# apn = APN::Notification.new
|
6
|
+
# apn.badge = 5
|
7
|
+
# apn.sound = 'my_sound.aiff'
|
8
|
+
# apn.alert = 'Hello!'
|
9
|
+
# apn.device = APN::Device.find(1)
|
10
|
+
# apn.save
|
11
|
+
#
|
12
|
+
# To deliver call the following method:
|
13
|
+
# APN::Notification.send_notifications
|
14
|
+
#
|
15
|
+
# As each APN::Notification is sent the <tt>sent_at</tt> column will be timestamped,
|
16
|
+
# so as to not be sent again.
|
1
17
|
class APN::Notification < ActiveRecord::Base
|
2
18
|
include ::ActionView::Helpers::TextHelper
|
19
|
+
extend ::ActionView::Helpers::TextHelper
|
20
|
+
|
3
21
|
set_table_name 'apn_notifications'
|
4
22
|
|
5
23
|
belongs_to :device, :class_name => 'APN::Device'
|
6
24
|
|
25
|
+
# Stores the text alert message you want to send to the device.
|
26
|
+
#
|
27
|
+
# If the message is over 150 characters long it will get truncated
|
28
|
+
# to 150 characters with a <tt>...</tt>
|
7
29
|
def alert=(message)
|
8
30
|
if !message.blank? && message.size > 150
|
9
31
|
message = truncate(message, :length => 150)
|
@@ -53,25 +75,37 @@ class APN::Notification < ActiveRecord::Base
|
|
53
75
|
|
54
76
|
class << self
|
55
77
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
78
|
+
# Opens a connection to the Apple APN server and attempts to batch deliver
|
79
|
+
# an Array of notifications.
|
80
|
+
#
|
81
|
+
# This method expects an Array of APN::Notifications. If no parameter is passed
|
82
|
+
# in then it will use the following:
|
83
|
+
# APN::Notification.all(:conditions => {:sent_at => nil})
|
84
|
+
#
|
85
|
+
# As each APN::Notification is sent the <tt>sent_at</tt> column will be timestamped,
|
86
|
+
# so as to not be sent again.
|
87
|
+
def send_notifications(notifications = APN::Notification.all(:conditions => {:sent_at => nil}))
|
88
|
+
unless notifications.nil? || notifications.empty?
|
89
|
+
logger.info "APN: Attempting to deliver #{pluralize(notifications.size, 'notification')}."
|
90
|
+
cert = File.read(configatron.apn.cert)
|
91
|
+
ctx = OpenSSL::SSL::SSLContext.new
|
92
|
+
ctx.key = OpenSSL::PKey::RSA.new(cert, configatron.apn.passphrase)
|
93
|
+
ctx.cert = OpenSSL::X509::Certificate.new(cert)
|
61
94
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
95
|
+
s = TCPSocket.new(configatron.apn.host, configatron.apn.port)
|
96
|
+
ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
|
97
|
+
ssl.sync = true
|
98
|
+
ssl.connect
|
66
99
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
100
|
+
notifications.each do |noty|
|
101
|
+
ssl.write(noty.message_for_sending)
|
102
|
+
noty.sent_at = Time.now
|
103
|
+
noty.save
|
104
|
+
end
|
72
105
|
|
73
|
-
|
74
|
-
|
106
|
+
ssl.close
|
107
|
+
s.close
|
108
|
+
end
|
75
109
|
end
|
76
110
|
|
77
111
|
end # class << self
|
@@ -4,12 +4,7 @@ namespace :apn do
|
|
4
4
|
|
5
5
|
desc "Deliver all unsent APN notifications."
|
6
6
|
task :deliver => [:environment] do
|
7
|
-
|
8
|
-
unless notifications.empty?
|
9
|
-
include ActionView::Helpers::TextHelper
|
10
|
-
RAILS_DEFAULT_LOGGER.info "APN: Attempting to deliver #{pluralize(notifications.size, 'notification')}."
|
11
|
-
APN::Notification.send_notifications(notifications)
|
12
|
-
end
|
7
|
+
APN::Notification.send_notifications
|
13
8
|
end
|
14
9
|
|
15
10
|
end # notifications
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: markbates-apn_on_rails
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.1.20090724152309
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- markbates
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-07-
|
12
|
+
date: 2009-07-24 00:00:00 -07:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -35,7 +35,6 @@ files:
|
|
35
35
|
- lib/apn_on_rails/apn_on_rails.rb
|
36
36
|
- lib/apn_on_rails/app/models/apn/device.rb
|
37
37
|
- lib/apn_on_rails/app/models/apn/notification.rb
|
38
|
-
- lib/apn_on_rails/app/models/apple_push_notification.rb
|
39
38
|
- lib/apn_on_rails/db/migrate/20090723132058_create_apn_devices.rb
|
40
39
|
- lib/apn_on_rails/db/migrate/20090723132059_create_apn_notifications.rb
|
41
40
|
- lib/apn_on_rails/tasks/apn.rake
|
@@ -1,94 +0,0 @@
|
|
1
|
-
# #
|
2
|
-
# # Fabien Penso <fabien.penso@conovae.com>
|
3
|
-
# # April 6th, 2009.
|
4
|
-
# #
|
5
|
-
#
|
6
|
-
# require 'socket'
|
7
|
-
# require 'openssl'
|
8
|
-
#
|
9
|
-
# class ApplePushNotification < ActiveRecord::Base
|
10
|
-
#
|
11
|
-
# HOST = "gateway.sandbox.push.apple.com"
|
12
|
-
# PATH = '/'
|
13
|
-
# PORT = 2195
|
14
|
-
# path = File.join(RAILS_ROOT, 'config', 'apple_push_notification_development.pem')
|
15
|
-
# CERT = File.read(path) if File.exists?(path)
|
16
|
-
# PASSPHRASE = "foobar"
|
17
|
-
# CACERT = File.expand_path(File.dirname(__FILE__) + "certs/ca.gateway.sandbox.push.apple.com.crt")
|
18
|
-
# USERAGENT = 'Mozilla/5.0 (apple_push_notification Ruby on Rails 0.1)'
|
19
|
-
#
|
20
|
-
# # attr_accessor :paylod, :sound, :badge, :alert, :appdata
|
21
|
-
# attr_accessible :device_token
|
22
|
-
#
|
23
|
-
# # validates_uniqueness_of :device_token
|
24
|
-
#
|
25
|
-
# # def send_notification
|
26
|
-
# #
|
27
|
-
# # ctx = OpenSSL::SSL::SSLContext.new
|
28
|
-
# # ctx.key = OpenSSL::PKey::RSA.new(CERT, PASSPHRASE)
|
29
|
-
# # ctx.cert = OpenSSL::X509::Certificate.new(CERT)
|
30
|
-
# #
|
31
|
-
# # s = TCPSocket.new(HOST, PORT)
|
32
|
-
# # ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
|
33
|
-
# # ssl.sync = true
|
34
|
-
# # ssl.connect
|
35
|
-
# #
|
36
|
-
# # ssl.write(self.apn_message_for_sending)
|
37
|
-
# #
|
38
|
-
# # ssl.close
|
39
|
-
# # s.close
|
40
|
-
# #
|
41
|
-
# # rescue SocketError => error
|
42
|
-
# # raise "Error while sending notifications: #{error}"
|
43
|
-
# # end
|
44
|
-
#
|
45
|
-
# def self.send_notifications(notifications)
|
46
|
-
# ctx = OpenSSL::SSL::SSLContext.new
|
47
|
-
# ctx.key = OpenSSL::PKey::RSA.new(CERT, PASSPHRASE)
|
48
|
-
# ctx.cert = OpenSSL::X509::Certificate.new(CERT)
|
49
|
-
#
|
50
|
-
# s = TCPSocket.new(HOST, PORT)
|
51
|
-
# ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
|
52
|
-
# ssl.sync = true
|
53
|
-
# ssl.connect
|
54
|
-
#
|
55
|
-
# for notif in notifications do
|
56
|
-
# ssl.write(notif.apn_message_for_sending)
|
57
|
-
# notif.sent_at = Time.now
|
58
|
-
# notif.save
|
59
|
-
# end
|
60
|
-
#
|
61
|
-
# ssl.close
|
62
|
-
# s.close
|
63
|
-
# rescue SocketError => error
|
64
|
-
# raise "Error while sending notifications: #{error}"
|
65
|
-
# end
|
66
|
-
#
|
67
|
-
# def to_apple_json
|
68
|
-
# logger.debug "Sending #{self.apple_hash.to_json}"
|
69
|
-
# self.apple_hash.to_json
|
70
|
-
# end
|
71
|
-
#
|
72
|
-
# def apn_message_for_sending
|
73
|
-
# json = self.to_apple_json
|
74
|
-
# message = "\0\0 #{self.device_token_hexa}\0#{json.length.chr}#{json}"
|
75
|
-
# raise "The maximum size allowed for a notification payload is 256 bytes." if message.size.to_i > 256
|
76
|
-
# message
|
77
|
-
# end
|
78
|
-
#
|
79
|
-
# def device_token_hexa
|
80
|
-
# [self.device_token.delete(' ')].pack('H*')
|
81
|
-
# end
|
82
|
-
#
|
83
|
-
# def apple_hash
|
84
|
-
# result = {}
|
85
|
-
# result['aps'] = {}
|
86
|
-
# result['aps']['alert'] = alert if alert
|
87
|
-
# result['aps']['badge'] = badge.to_i if badge
|
88
|
-
# result['aps']['sound'] = sound if sound and sound.is_a? String
|
89
|
-
# result['aps']['sound'] = "1.aiff" if sound and sound.is_a?(TrueClass)
|
90
|
-
# result.merge appdata if appdata
|
91
|
-
#
|
92
|
-
# result
|
93
|
-
# end
|
94
|
-
# end
|