apn_on_rails 0.2.0 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -94,10 +94,15 @@ That's it, now you're ready to start creating notifications.
94
94
  >> notification.sound = true
95
95
  >> notification.alert = "foobar"
96
96
  >> notification.save
97
- >> APN::Notification.send_notifications
98
97
 
99
- You can also run the following Rake task to send your notifications:
98
+ You can use the following Rake task to deliver your notifications:
100
99
 
101
100
  $ rake apn:notifications:deliver
102
101
 
102
+ The Rake task will find any unsent notifications in the database. If there aren't any notifications
103
+ it will simply do nothing. If there are notifications waiting to be delivered it will open a single connection
104
+ to Apple and push all the notifications through that one connection. Apple does not like people opening/closing
105
+ connections constantly, so it's pretty important that you are careful about batching up your notifications so
106
+ Apple doesn't shut you down.
107
+
103
108
  Released under the MIT license.
@@ -4,10 +4,22 @@ class ApnMigrationsGenerator < Rails::Generator::Base
4
4
 
5
5
  def manifest # :nodoc:
6
6
  record do |m|
7
- [:create_apn_devices, :create_apn_notifications].each do |f|
8
- m.migration_template(File.join('apn_migrations', "#{f}.rb"), "db/migrate", {:migration_file_name => f, :collision => :skip})
7
+ timestamp = Time.now.utc.strftime("%Y%m%d%H%M%S")
8
+ db_migrate_path = File.join('db', 'migrate')
9
+
10
+ m.directory(db_migrate_path)
11
+
12
+ ['001_create_apn_devices', '002_create_apn_notifications'].each_with_index do |f, i|
13
+ timestamp = timestamp.succ
14
+ if Dir.glob(File.join(db_migrate_path, "*_#{f}.rb")).empty?
15
+ m.file(File.join('apn_migrations', "#{f}.rb"),
16
+ File.join(db_migrate_path, "#{timestamp}_#{f}.rb"),
17
+ {:collision => :skip})
18
+ end
9
19
  end
10
- end
11
- end
20
+
21
+ end # record
22
+
23
+ end # manifest
12
24
 
13
- end
25
+ end # ApnMigrationsGenerator
@@ -14,12 +14,22 @@ end
14
14
 
15
15
  configatron.apn.set_default(:passphrase, '')
16
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'))
17
+
18
+ configatron.apn.feedback.set_default(:passphrase, configatron.apn.passphrase)
19
+ configatron.apn.feedback.set_default(:port, 2196)
19
20
 
20
21
  if rails_env == 'production'
21
22
  configatron.apn.set_default(:host, 'gateway.push.apple.com')
22
23
  configatron.apn.set_default(:cert, File.join(rails_root, 'config', 'apple_push_notification_production.pem'))
24
+
25
+ configatron.apn.feedback.set_default(:host, 'feedback.push.apple.com')
26
+ configatron.apn.feedback.set_default(:cert, configatron.apn.cert)
27
+ else
28
+ configatron.apn.set_default(:host, 'gateway.sandbox.push.apple.com')
29
+ configatron.apn.set_default(:cert, File.join(rails_root, 'config', 'apple_push_notification_development.pem'))
30
+
31
+ configatron.apn.feedback.set_default(:host, 'feedback.sandbox.push.apple.com')
32
+ configatron.apn.feedback.set_default(:cert, configatron.apn.cert)
23
33
  end
24
34
 
25
35
  module APN # :nodoc:
@@ -0,0 +1,65 @@
1
+ module APN
2
+ module Connection
3
+
4
+ class << self
5
+
6
+ # Yields up an SSL socket to write notifications to.
7
+ # The connections are close automatically.
8
+ #
9
+ # Example:
10
+ # APN::Configuration.open_for_delivery do |conn|
11
+ # conn.write('my cool notification')
12
+ # end
13
+ #
14
+ # Configuration parameters are:
15
+ #
16
+ # configatron.apn.passphrase = ''
17
+ # configatron.apn.port = 2195
18
+ # configatron.apn.host = 'gateway.sandbox.push.apple.com' # Development
19
+ # configatron.apn.host = 'gateway.push.apple.com' # Production
20
+ # configatron.apn.cert = File.join(rails_root, 'config', 'apple_push_notification_development.pem')) # Development
21
+ # configatron.apn.cert = File.join(rails_root, 'config', 'apple_push_notification_production.pem')) # Production
22
+ def open_for_delivery(options = {}, &block)
23
+ open(options, &block)
24
+ end
25
+
26
+ # Yields up an SSL socket to receive feedback from.
27
+ # The connections are close automatically.
28
+ # Configuration parameters are:
29
+ #
30
+ # configatron.apn.feedback.passphrase = ''
31
+ # configatron.apn.feedback.port = 2196
32
+ # configatron.apn.feedback.host = 'feedback.sandbox.push.apple.com' # Development
33
+ # configatron.apn.feedback.host = 'feedback.push.apple.com' # Production
34
+ # configatron.apn.feedback.cert = File.join(rails_root, 'config', 'apple_push_notification_development.pem')) # Development
35
+ # configatron.apn.feedback.cert = File.join(rails_root, 'config', 'apple_push_notification_production.pem')) # Production
36
+ def open_for_feedback(options = {}, &block) # :nodoc:
37
+
38
+ end
39
+
40
+ private
41
+ def open(options = {}, &block) # :nodoc:
42
+ options = {:cert => configatron.apn.cert,
43
+ :passphrase => configatron.apn.passphrase,
44
+ :host => configatron.apn.host,
45
+ :port => configatron.apn.port}.merge(options)
46
+ cert = File.read(options[:cert])
47
+ ctx = OpenSSL::SSL::SSLContext.new
48
+ ctx.key = OpenSSL::PKey::RSA.new(cert, options[:passphrase])
49
+ ctx.cert = OpenSSL::X509::Certificate.new(cert)
50
+
51
+ s = TCPSocket.new(options[:host], options[:port])
52
+ ssl = OpenSSL::SSL::SSLSocket.new(s, ctx)
53
+ ssl.sync = true
54
+ ssl.connect
55
+
56
+ yield ssl if block_given?
57
+
58
+ ssl.close
59
+ s.close
60
+ end
61
+
62
+ end
63
+
64
+ end # Connection
65
+ end # APN
@@ -86,25 +86,15 @@ class APN::Notification < ActiveRecord::Base
86
86
  # so as to not be sent again.
87
87
  def send_notifications(notifications = APN::Notification.all(:conditions => {:sent_at => nil}))
88
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)
94
-
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
99
-
100
- notifications.each do |noty|
101
- ssl.write(noty.message_for_sending)
102
- noty.sent_at = Time.now
103
- noty.save
89
+
90
+ APN::Connection.open_for_delivery do |conn|
91
+ notifications.each do |noty|
92
+ conn.write(noty.message_for_sending)
93
+ noty.sent_at = Time.now
94
+ noty.save
95
+ end
104
96
  end
105
-
106
- ssl.close
107
- s.close
97
+
108
98
  end
109
99
  end
110
100
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apn_on_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.2
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-26 00:00:00 -04:00
12
+ date: 2009-07-30 00:00:00 -04:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -33,6 +33,7 @@ extra_rdoc_files:
33
33
  - LICENSE
34
34
  files:
35
35
  - lib/apn_on_rails/apn_on_rails.rb
36
+ - lib/apn_on_rails/app/models/apn/connection.rb
36
37
  - lib/apn_on_rails/app/models/apn/device.rb
37
38
  - lib/apn_on_rails/app/models/apn/notification.rb
38
39
  - lib/apn_on_rails/tasks/apn.rake
@@ -42,8 +43,8 @@ files:
42
43
  - README
43
44
  - LICENSE
44
45
  - generators/apn_migrations_generator.rb
45
- - generators/templates/apn_migrations/create_apn_devices.rb
46
- - generators/templates/apn_migrations/create_apn_notifications.rb
46
+ - generators/templates/apn_migrations/001_create_apn_devices.rb
47
+ - generators/templates/apn_migrations/002_create_apn_notifications.rb
47
48
  has_rdoc: true
48
49
  homepage: http://www.metabates.com
49
50
  licenses: []