markbates-apn_on_rails 0.0.1.20090723180641

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2009 markbates
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,76 @@
1
+ APN on Rails
2
+ (Apple Push Notifications on Rails)
3
+ =====================
4
+
5
+ Acknowledgements
6
+ ----------------
7
+ This gem is a re-write of a plugin that was written by Fabien Penso and Sam Soffes.
8
+ Their plugin was a great start, but it just didn't quite reach the level I hoped it would.
9
+ I've re-written, as a gem, added a ton of tests, and I would like to think that I made it
10
+ a little nicer and easier to use.
11
+
12
+ Converting Your Certificate
13
+ ---------------------------
14
+ Once you have the certificate from Apple for your application, export your key
15
+ and the apple certificate as p12 files. Here is a quick walkthrough on how to do this:
16
+
17
+ 1. Click the disclosure arrow next to your certificate in Keychain Access and select the certificate and the key.
18
+ 2. Right click and choose `Export 2 items...`.
19
+ 3. Choose the p12 format from the drop down and name it `cert.p12`.
20
+
21
+ Now covert the p12 file to a pem file:
22
+
23
+ $ openssl pkcs12 -in cert.p12 -out apple_push_notification_production.pem -nodes -clcerts
24
+
25
+ Put 'apple_push_notification_production.pem' in config/
26
+
27
+ If you are using a development certificate, then change the name to apple_push_notification_development.pem instead.
28
+
29
+ Installing
30
+ ----------
31
+
32
+ From RubyForge:
33
+ $ sudo gem install apn_on_rails
34
+
35
+ Or, if you like to live on the edge:
36
+ $ sudo gem install markbates-apn_on_rails --source=http://gems.github.com
37
+
38
+ Then you just add the following require, wherever it makes sense to you:
39
+ require 'apn_on_rails'
40
+
41
+ If you like to use the built in Rails gem management:
42
+ config.gem 'apn_on_rails'
43
+
44
+ Or, if you like to live on the edge:
45
+ 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 you need to add the following to your Rakefile so you can use the
50
+ Rake tasks that ship with APN on Rails:
51
+ begin
52
+ require 'apn_on_rails_tasks'
53
+ rescue MissingSourceFile => e
54
+ puts e.message
55
+ end
56
+
57
+ Now, to create the tables you need for APN on Rails, run the following task:
58
+ $ rake apn:db:migrate
59
+
60
+ That's it, now you're ready to start creating notifications.
61
+
62
+ Example
63
+ -------
64
+
65
+ *Note: the spaces in `device_token` are optional.*
66
+
67
+ $ ./script/console
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
75
+
76
+ Copyright (c) 2009 Fabien Penso. Released under the MIT license. Modified by [Sam Soffes](http://samsoff.es).
@@ -0,0 +1,51 @@
1
+ require 'socket'
2
+ require 'openssl'
3
+ require 'configatron'
4
+
5
+ rails_root = File.join(FileUtils.pwd, 'rails_root')
6
+ if defined?(RAILS_ROOT)
7
+ rails_root = RAILS_ROOT
8
+ end
9
+
10
+ rails_env = 'development'
11
+ if defined?(RAILS_ENV)
12
+ rails_env = RAILS_ENV
13
+ end
14
+
15
+ configatron.apn.set_default(:passphrase, '')
16
+ configatron.apn.set_default(:port, 2195)
17
+ configatron.apn.set_default(:host, 'gateway.sandbox.push.apple.com')
18
+ configatron.apn.set_default(:cert, File.join(rails_root, 'config', 'apple_push_notification_development.pem'))
19
+
20
+ if rails_env == 'production'
21
+ configatron.apn.set_default(:host, 'gateway.push.apple.com')
22
+ configatron.apn.set_default(:cert, File.join(rails_root, 'config', 'apple_push_notification_production.pem'))
23
+ end
24
+
25
+ module APN
26
+
27
+ module Errors
28
+
29
+ class ExceededMessageSizeError < StandardError
30
+
31
+ def initialize(message)
32
+ super("The maximum size allowed for a notification payload is 256 bytes: '#{message}'")
33
+ end
34
+
35
+ end
36
+
37
+ end # Errors
38
+
39
+ end # APN
40
+
41
+ Dir.glob(File.join(File.dirname(__FILE__), 'app', 'models', 'apn', '*.rb')).sort.each do |f|
42
+ require f
43
+ end
44
+
45
+ %w{ models controllers helpers }.each do |dir|
46
+ path = File.join(File.dirname(__FILE__), 'app', dir)
47
+ $LOAD_PATH << path
48
+ # puts "Adding #{path}"
49
+ ActiveSupport::Dependencies.load_paths << path
50
+ ActiveSupport::Dependencies.load_once_paths.delete(path)
51
+ end
@@ -0,0 +1,25 @@
1
+ class APN::Device < ActiveRecord::Base
2
+ set_table_name 'apn_devices'
3
+
4
+ has_many :notifications, :class_name => 'APN::Notification'
5
+
6
+ validates_uniqueness_of :token
7
+ 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
+
9
+ # If the token comes in like this:
10
+ # '<5gxadhy6 6zmtxfl6 5zpbcxmw ez3w7ksf qscpr55t trknkzap 7yyt45sc g6jrw7qz>'
11
+ # Then the '<' and '>' will be stripped off.
12
+ def token=(token)
13
+ res = token.scan(/\<(.+)\>/).first
14
+ unless res.nil? || res.empty?
15
+ token = res.first
16
+ end
17
+ write_attribute('token', token)
18
+ end
19
+
20
+ # Returns the hexadecimal representation of the device's token.
21
+ def to_hexa
22
+ [self.token.delete(' ')].pack('H*')
23
+ end
24
+
25
+ end
@@ -0,0 +1,79 @@
1
+ class APN::Notification < ActiveRecord::Base
2
+ include ::ActionView::Helpers::TextHelper
3
+ set_table_name 'apn_notifications'
4
+
5
+ belongs_to :device, :class_name => 'APN::Device'
6
+
7
+ def alert=(message)
8
+ if !message.blank? && message.size > 150
9
+ message = truncate(message, :length => 150)
10
+ end
11
+ write_attribute('alert', message)
12
+ end
13
+
14
+ # Creates a Hash that will be the payload of an APN.
15
+ #
16
+ # Example:
17
+ # apn = APN::Notification.new
18
+ # apn.badge = 5
19
+ # apn.sound = 'my_sound.aiff'
20
+ # apn.alert = 'Hello!'
21
+ # apn.apple_hash # => {"aps" => {"badge" => 5, "sound" => "my_sound.aiff", "alert" => "Hello!"}}
22
+ def apple_hash
23
+ result = {}
24
+ result['aps'] = {}
25
+ result['aps']['alert'] = self.alert if self.alert
26
+ result['aps']['badge'] = self.badge.to_i if self.badge
27
+ if self.sound
28
+ result['aps']['sound'] = self.sound if self.sound.is_a? String
29
+ result['aps']['sound'] = "1.aiff" if self.sound.is_a?(TrueClass)
30
+ end
31
+ result
32
+ end
33
+
34
+ # Creates the JSON string required for an APN message.
35
+ #
36
+ # Example:
37
+ # apn = APN::Notification.new
38
+ # apn.badge = 5
39
+ # apn.sound = 'my_sound.aiff'
40
+ # apn.alert = 'Hello!'
41
+ # apn.to_apple_json # => '{"aps":{"badge":5,"sound":"my_sound.aiff","alert":"Hello!"}}'
42
+ def to_apple_json
43
+ self.apple_hash.to_json
44
+ end
45
+
46
+ # Creates the binary message needed to send to Apple.
47
+ def message_for_sending
48
+ json = self.to_apple_json
49
+ message = "\0\0 #{self.device.to_hexa}\0#{json.length.chr}#{json}"
50
+ raise APN::Errors::ExceededMessageSizeError.new(message) if message.size.to_i > 256
51
+ message
52
+ end
53
+
54
+ class << self
55
+
56
+ def send_notifications(notifications)
57
+ cert = File.read(configatron.apn.cert)
58
+ ctx = OpenSSL::SSL::SSLContext.new
59
+ ctx.key = OpenSSL::PKey::RSA.new(cert, configatron.apn.passphrase)
60
+ ctx.cert = OpenSSL::X509::Certificate.new(cert)
61
+
62
+ s = TCPSocket.new(configatron.apn.host, configatron.apn.port)
63
+ ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
64
+ ssl.sync = true
65
+ ssl.connect
66
+
67
+ notifications.each do |noty|
68
+ ssl.write(noty.message_for_sending)
69
+ noty.sent_at = Time.now
70
+ noty.save
71
+ end
72
+
73
+ ssl.close
74
+ s.close
75
+ end
76
+
77
+ end # class << self
78
+
79
+ end # APN::Notification
@@ -0,0 +1,94 @@
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
@@ -0,0 +1,13 @@
1
+ class CreateApnDevices < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :apn_devices do |t|
4
+ t.text :token, :size => 71, :null => false
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+
10
+ def self.down
11
+ drop_table :apn_devices
12
+ end
13
+ end
@@ -0,0 +1,23 @@
1
+ class CreateApnNotifications < ActiveRecord::Migration
2
+
3
+ def self.up
4
+
5
+ create_table :apn_notifications do |t|
6
+ t.integer :device_id, :null => false
7
+ t.integer :errors_nb, :default => 0 # used for storing errors from apple feedbacks
8
+ t.string :device_language, :size => 5 # if you don't want to send localized strings
9
+ t.string :sound
10
+ t.string :alert, :size => 150
11
+ t.integer :badge
12
+ t.datetime :sent_at
13
+ t.timestamps
14
+ end
15
+
16
+ add_index :apn_notifications, :device_id
17
+ end
18
+
19
+ def self.down
20
+ drop_table :apn_notifications
21
+ end
22
+
23
+ end
@@ -0,0 +1,17 @@
1
+ namespace :apn do
2
+
3
+ namespace :notifications do
4
+
5
+ desc "Deliver all unsent APN notifications."
6
+ task :deliver => [:environment] do
7
+ notifications = APN::Notification.all(:conditions => {:sent_at => nil})
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
13
+ end
14
+
15
+ end # notifications
16
+
17
+ end # apn
@@ -0,0 +1,13 @@
1
+ namespace :apn do
2
+
3
+ namespace :db do
4
+
5
+ desc 'Runs the migrations for apn_on_rails.'
6
+ task :migrate => [:environment] do
7
+ puts File.join(File.dirname(__FILE__), '..', '..', 'apn_on_rails', 'db', 'migrate')
8
+ ActiveRecord::Migrator.up(File.join(File.dirname(__FILE__), '..', '..', 'apn_on_rails', 'db', 'migrate'))
9
+ end
10
+
11
+ end # db
12
+
13
+ end # apn
@@ -0,0 +1,4 @@
1
+ Dir.glob(File.join(File.dirname(__FILE__), 'apn_on_rails', '**/*.rb')).sort.each do |f|
2
+ require File.expand_path(f)
3
+ end
4
+
@@ -0,0 +1,3 @@
1
+ Dir.glob(File.join(File.dirname(__FILE__), 'apn_on_rails', 'tasks', '**/*.rake')).each do |f|
2
+ load File.expand_path(f)
3
+ end
metadata ADDED
@@ -0,0 +1,74 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: markbates-apn_on_rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.20090723180641
5
+ platform: ruby
6
+ authors:
7
+ - markbates
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-07-23 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: configatron
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: "0"
24
+ version:
25
+ description: "apn_on_rails was developed by: markbates"
26
+ email: ""
27
+ executables: []
28
+
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - README
33
+ - LICENSE
34
+ files:
35
+ - lib/apn_on_rails/apn_on_rails.rb
36
+ - lib/apn_on_rails/app/models/apn/device.rb
37
+ - lib/apn_on_rails/app/models/apn/notification.rb
38
+ - lib/apn_on_rails/app/models/apple_push_notification.rb
39
+ - lib/apn_on_rails/db/migrate/20090723132058_create_apn_devices.rb
40
+ - lib/apn_on_rails/db/migrate/20090723132059_create_apn_notifications.rb
41
+ - lib/apn_on_rails/tasks/apn.rake
42
+ - lib/apn_on_rails/tasks/db.rake
43
+ - lib/apn_on_rails.rb
44
+ - lib/apn_on_rails_tasks.rb
45
+ - README
46
+ - LICENSE
47
+ has_rdoc: false
48
+ homepage: ""
49
+ post_install_message:
50
+ rdoc_options: []
51
+
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ requirements: []
67
+
68
+ rubyforge_project: magrathea
69
+ rubygems_version: 1.2.0
70
+ signing_key:
71
+ specification_version: 3
72
+ summary: apn_on_rails
73
+ test_files: []
74
+