apns_dispatch 0.1.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.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .rvmrc
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in apns_dispatch.gemspec
4
+ gemspec
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 SCVNGR, Inc.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # ApnsDispatch
2
+
3
+ A simple Ruby framework for sending push notifications and receiving feedback using the Apple Push Notification service
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'apns_dispatch'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install apns_dispatch
18
+
19
+ ## Sending Notifications
20
+
21
+ connection = ApnsConnection.new(CERTIFICATE, production: true)
22
+ ApnNotification.new(connection, DEVICE_TOKEN, 'a message to a device').send_notification
23
+
24
+ ## Checking the Feedback Service
25
+
26
+ [Apple recommends](https://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingWIthAPS/CommunicatingWIthAPS.html#//apple_ref/doc/uid/TP40008194-CH101-SW3)
27
+ periodically checking their feedback service for device tokens that have failed delivery. The feedback
28
+ service also returns the time of the failure. If a device token was registered before the failure time,
29
+ it should be removed from your system.
30
+
31
+ connection = ApnsConnection.new(CERTIFICATE, production: true, feedback: true)
32
+ ApnFeedback.new(connection).failed_device_tokens.each do |device_token, failure_time|
33
+ # Delete this device token from your system, if the registration time is before failure_time
34
+ end
35
+
36
+ ## Connection Pooling
37
+
38
+ If notifications are sent frequently, latency will be improved by reusing connections to the Push
39
+ Notification service. ApnsDispatch also provides connection pooling:
40
+
41
+ # Initiates a new connection to the APNs
42
+ connection = ApnsConnectionPool.connection(CERTIFICATE, production: true)
43
+
44
+ # This will return the same connection, rather than initiating a new one
45
+ connection = ApnsConnectionPool.connection(CERTIFICATE, production: true)
46
+
47
+ ## Requirements
48
+
49
+ `apns_dispatch` requires Ruby 1.9+.
50
+
51
+ ## License
52
+
53
+ `apns_dispatch` is written by Costa Walcott, and is Copyright 2012 SCVNGR, Inc. It is free software,
54
+ and may be redistributed under the terms specified in the MIT-LICENSE file.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require 'bundler/setup'
2
+ require 'bundler/gem_tasks'
3
+ require 'rspec/core/rake_task'
4
+
5
+ RSpec::Core::RakeTask.new do |t|
6
+ t.pattern = 'spec/**/*_spec.rb'
7
+ end
8
+
9
+ desc 'Default: run the rspec examples'
10
+ task :default => [:spec]
@@ -0,0 +1,25 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'apns_dispatch/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = 'apns_dispatch'
8
+ gem.version = ApnsDispatch::VERSION
9
+ gem.authors = ['Costa Walcott']
10
+ gem.email = ['costa@scvngr.com']
11
+ gem.summary = 'A simple Ruby framework for communicating with the APNs'
12
+ gem.description = 'A simple Ruby framework for sending push notifications and receiving feedback using the Apple Push Notification service'
13
+ gem.homepage = 'http://github.com/TheLevelUp/apns_dispatch'
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
17
+
18
+ gem.add_dependency 'json', '~> 1.7.5'
19
+
20
+ gem.add_development_dependency 'rake'
21
+ gem.add_development_dependency 'rspec', '~> 2.0'
22
+
23
+ gem.required_ruby_version = Gem::Requirement.new('>= 1.9.2')
24
+ gem.require_paths = ["lib"]
25
+ end
@@ -0,0 +1,16 @@
1
+ # This gem provides a simple Ruby wrapper for communicating with the Apple Push Notification service
2
+ # (APNs). It handles SSL, sending push notifications to APNs using Apple's binary protocol, and
3
+ # retriving failed device tokens using the APN feedback service. Both development and production
4
+ # certificates are handled.
5
+ #
6
+ # Author:: Costa Walcott (mailto:costa@scvngr.com)
7
+ # Copyright:: Copyright (c) 2012 SCVNGR, Inc.
8
+ # License:: MIT
9
+
10
+ require 'json'
11
+
12
+ require 'apns_dispatch/apn_feedback'
13
+ require 'apns_dispatch/apn_notification'
14
+ require 'apns_dispatch/apns_connection'
15
+ require 'apns_dispatch/apns_connection_pool'
16
+ require 'apns_dispatch/version'
@@ -0,0 +1,30 @@
1
+ module ApnsDispatch
2
+ # Retrieves and returns failed device tokens from production and development APNs connections.
3
+
4
+ class ApnFeedback
5
+ # Constants
6
+ FEEDBACK_PACKET_FORMAT = 'N1n1H140'
7
+ FEEDBACK_PACKET_LENGTH = 38
8
+
9
+ def initialize(connection)
10
+ @connection = connection
11
+ end
12
+
13
+ def failed_device_tokens
14
+ apns_feedback = []
15
+
16
+ while line = @connection.read(FEEDBACK_PACKET_LENGTH)
17
+ apns_feedback << unpackage(line)
18
+ end
19
+
20
+ apns_feedback
21
+ end
22
+
23
+ private
24
+
25
+ def unpackage(line)
26
+ feedback = line.unpack(FEEDBACK_PACKET_FORMAT)
27
+ [feedback[2], Time.at(feedback[0])]
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,43 @@
1
+ module ApnsDispatch
2
+ # Creates a packaged notification for the APNs. Based on code from https://github.com/jpoz/APNS.
3
+
4
+ class ApnNotification
5
+ def initialize(connection, token, message, options = {})
6
+ @connection = connection
7
+ @token = token
8
+ @message = message
9
+ @options = options
10
+ end
11
+
12
+ def send_notification
13
+ @connection.write notification_packet
14
+ end
15
+
16
+ private
17
+
18
+ def binary_token
19
+ [@token].pack 'H*'
20
+ end
21
+
22
+ def command
23
+ 0
24
+ end
25
+
26
+ def message_information
27
+ {
28
+ aps: {
29
+ alert: @message,
30
+ sound: 'default'
31
+ }
32
+ }
33
+ end
34
+
35
+ def notification_packet
36
+ [command, binary_token.bytesize, binary_token, payload.bytesize, payload].pack 'cna*na*'
37
+ end
38
+
39
+ def payload
40
+ message_information.merge(@options).to_json
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,100 @@
1
+ require 'openssl'
2
+ require 'socket'
3
+
4
+ module ApnsDispatch
5
+ # Handles communication with the Apple Push Notification service (APNs).
6
+ # Based on code from https://github.com/itspriddle/apnd.
7
+
8
+ class ApnsConnection
9
+ # Constants
10
+ FEEDBACK_PORT = 2196
11
+ GATEWAY_PORT = 2195
12
+ PRODUCTION_FEEDBACK_HOST = 'feedback.push.apple.com'
13
+ PRODUCTION_GATEWAY_HOST = 'gateway.push.apple.com'
14
+ SANDBOX_FEEDBACK_HOST = 'feedback.sandbox.push.apple.com'
15
+ SANDBOX_GATEWAY_HOST = 'gateway.sandbox.push.apple.com'
16
+
17
+ # Attributes
18
+ attr_reader :certificate, :host, :options, :port
19
+
20
+ def initialize(certificate, options={})
21
+ if options[:feedback]
22
+ @port = FEEDBACK_PORT
23
+
24
+ if options[:production]
25
+ @host = PRODUCTION_FEEDBACK_HOST
26
+ else
27
+ @host = SANDBOX_FEEDBACK_HOST
28
+ end
29
+ else
30
+ @port = GATEWAY_PORT
31
+
32
+ if options[:production]
33
+ @host = PRODUCTION_GATEWAY_HOST
34
+ else
35
+ @host = SANDBOX_GATEWAY_HOST
36
+ end
37
+ end
38
+
39
+ @certificate = certificate
40
+ @options = options
41
+ end
42
+
43
+ def read(length)
44
+ with_connection { @ssl.read(length) }
45
+ end
46
+
47
+ def write(message)
48
+ with_connection { @ssl.write(message) }
49
+ end
50
+
51
+ private
52
+
53
+ def connect!
54
+ context = OpenSSL::SSL::SSLContext.new
55
+ context.cert = OpenSSL::X509::Certificate.new(@certificate)
56
+ context.key = OpenSSL::PKey::RSA.new(@certificate)
57
+
58
+ @sock = TCPSocket.new(host, port)
59
+ @ssl = OpenSSL::SSL::SSLSocket.new(@sock, context)
60
+ @ssl.sync = true
61
+ @ssl.connect
62
+ end
63
+
64
+ def connected?
65
+ !@ssl.nil?
66
+ end
67
+
68
+ def disconnect!
69
+ @ssl.close
70
+ @sock.close
71
+ @ssl = nil
72
+ @sock = nil
73
+ end
74
+
75
+ def reconnect!
76
+ disconnect!
77
+ connect!
78
+ end
79
+
80
+ def with_connection
81
+ unless connected?
82
+ connect!
83
+ end
84
+
85
+ attempted = false
86
+
87
+ begin
88
+ if block_given?
89
+ yield
90
+ end
91
+ rescue Errno::EPIPE, OpenSSL::SSL::SSLError
92
+ unless attempted
93
+ reconnect!
94
+ attempted = true
95
+ retry
96
+ end
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,25 @@
1
+ require 'digest/md5'
2
+ require 'apns_dispatch/null_apns_connection'
3
+
4
+ module ApnsDispatch
5
+ # Caches and returns individual unique ApnsConnections in memory.
6
+
7
+ class ApnsConnectionPool
8
+ def self.connection(certificate, options={})
9
+ if certificate
10
+ cache_key = cache_key(certificate, options)
11
+
12
+ @connections ||= {}
13
+ @connections[cache_key] ||= ApnsDispatch::ApnsConnection.new(certificate, options)
14
+ else
15
+ ApnsDispatch::NullApnsConnection.new
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def self.cache_key(certificate, options)
22
+ [Digest::MD5.hexdigest(certificate), options.hash]
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,9 @@
1
+ module ApnsDispatch
2
+ class NullApnsConnection
3
+ def read(length)
4
+ end
5
+
6
+ def write(message)
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module ApnsDispatch
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe ApnsDispatch::ApnFeedback do
4
+ # Helpers
5
+ def packed_feedback(feedback)
6
+ [feedback[1].to_i, 140, feedback[0]].pack 'N1n1H*'
7
+ end
8
+
9
+ before do
10
+ @failed_token = ['ced238e503ece0f6d664e1b8bdae7d9dffc39c2303bf7466cd83cb2783ad40b2',
11
+ Time.new(2012, 9, 1)]
12
+
13
+ apn_connection = stub
14
+ apn_connection.stub(:read).with(38).and_return packed_feedback(@failed_token), nil
15
+
16
+ @apn_feedback = ApnsDispatch::ApnFeedback.new(apn_connection)
17
+ end
18
+
19
+ it 'returns the failed tokens' do
20
+ [@failed_token].should == @apn_feedback.failed_device_tokens
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe ApnsDispatch::ApnNotification do
4
+ before do
5
+ token = '7307dea69c58e9625250aabb040594e1a031f574dba5d97549fbe211327aff4c'
6
+ message = 'test message'
7
+ options = { order_id: 12345 }
8
+ @connection = stub
9
+ @apn_notificiation = ApnsDispatch::ApnNotification.new(@connection, token, message, options)
10
+
11
+ payload = { aps: { alert: message, sound: 'default'} }.merge(options).to_json
12
+ @packet = [0, 32, [token].pack('H*'), payload.bytesize, payload].pack('cna*na*')
13
+ end
14
+
15
+ it 'sends a notification packet to the connection' do
16
+ @connection.should_receive(:write).with(@packet)
17
+
18
+ @apn_notificiation.send_notification
19
+ end
20
+ end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+
3
+ describe ApnsDispatch::ApnsConnectionPool do
4
+ before do
5
+ @options = { production: true }
6
+ end
7
+
8
+ context 'when a certificate is given' do
9
+ before do
10
+ @certificate = 'test-certificate'
11
+ @connection = ApnsDispatch::ApnsConnectionPool.connection(@certificate, @options)
12
+ end
13
+
14
+ context 'when called for the first time' do
15
+ it 'returns a new ApnsConnection with the correct values' do
16
+ @connection.certificate.should == @certificate
17
+ @connection.options.should == @options
18
+ end
19
+ end
20
+
21
+ context 'when called after the first time' do
22
+ it 'returns the same connection' do
23
+ ApnsDispatch::ApnsConnectionPool.connection(@certificate, @options).should == @connection
24
+ end
25
+ end
26
+ end
27
+
28
+ context 'when a nil certificate is given' do
29
+ it 'returns a null connection' do
30
+ result = ApnsDispatch::ApnsConnectionPool.connection(nil)
31
+ result.should be_kind_of(ApnsDispatch::NullApnsConnection)
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,122 @@
1
+ require 'spec_helper'
2
+
3
+ describe ApnsDispatch::ApnsConnection do
4
+ # Helpers
5
+ def self.should_run_command_with_connection(command, argument)
6
+ before do
7
+ TCPSocket.stub new: stub(close: true)
8
+
9
+ certificate = 'test-certificate'
10
+ @ssl = stub(close: true, connect: true, read: true, 'sync=' => true, write: true)
11
+ OpenSSL::SSL::SSLSocket.stub new: @ssl
12
+ OpenSSL::X509::Certificate.stub new: certificate
13
+ OpenSSL::PKey::RSA.stub new: certificate
14
+
15
+ @apns_connection = ApnsDispatch::ApnsConnection.new(certificate)
16
+ end
17
+
18
+ it 'runs the given block' do
19
+ @ssl.should_receive(command).with(argument)
20
+
21
+ @apns_connection.send command, argument
22
+ end
23
+
24
+ it 'retries once' do
25
+ @ssl.should_receive(command).with(argument).twice.and_return do
26
+ unless @run
27
+ @run = true
28
+ raise OpenSSL::SSL::SSLError
29
+ end
30
+ end
31
+
32
+ @apns_connection.send command, argument
33
+ end
34
+
35
+ it 'uses the correct host and port' do
36
+ TCPSocket.should_receive(:new).with(@apns_connection.host, @apns_connection.port)
37
+
38
+ @apns_connection.send command, argument
39
+ end
40
+
41
+ context 'when there is no connection' do
42
+ it 'initiates a connection' do
43
+ @ssl.should_receive :connect
44
+
45
+ @apns_connection.send command, argument
46
+ end
47
+ end
48
+
49
+ context 'when there is already a connection' do
50
+ before do
51
+ @apns_connection.instance_variable_set :@ssl, @ssl
52
+ end
53
+
54
+ it 'does not reconnect' do
55
+ @ssl.should_receive(:connect).never
56
+
57
+ @apns_connection.send command, argument
58
+ end
59
+ end
60
+ end
61
+
62
+ it 'has the correct APNs hosts and ports' do
63
+ ApnsDispatch::ApnsConnection::FEEDBACK_PORT.should == 2196
64
+ ApnsDispatch::ApnsConnection::GATEWAY_PORT.should == 2195
65
+ ApnsDispatch::ApnsConnection::PRODUCTION_FEEDBACK_HOST.should == 'feedback.push.apple.com'
66
+ ApnsDispatch::ApnsConnection::PRODUCTION_GATEWAY_HOST.should == 'gateway.push.apple.com'
67
+ ApnsDispatch::ApnsConnection::SANDBOX_FEEDBACK_HOST.should == 'feedback.sandbox.push.apple.com'
68
+ ApnsDispatch::ApnsConnection::SANDBOX_GATEWAY_HOST.should == 'gateway.sandbox.push.apple.com'
69
+ end
70
+
71
+ context 'with :feedback option and :production option' do
72
+ before do
73
+ @connection = ApnsDispatch::ApnsConnection.new('test', feedback: true, production: true)
74
+ end
75
+
76
+ it 'uses the production feedback host and feedback port' do
77
+ @connection.host.should == ApnsDispatch::ApnsConnection::PRODUCTION_FEEDBACK_HOST
78
+ @connection.port.should == ApnsDispatch::ApnsConnection::FEEDBACK_PORT
79
+ end
80
+ end
81
+
82
+ context 'with :feedback option only' do
83
+ before do
84
+ @connection = ApnsDispatch::ApnsConnection.new('test', feedback: true)
85
+ end
86
+
87
+ it 'uses the sandbox feedback host and feedback port' do
88
+ @connection.host.should == ApnsDispatch::ApnsConnection::SANDBOX_FEEDBACK_HOST
89
+ @connection.port.should == ApnsDispatch::ApnsConnection::FEEDBACK_PORT
90
+ end
91
+ end
92
+
93
+ context 'with :production option only' do
94
+ before do
95
+ @connection = ApnsDispatch::ApnsConnection.new('test', production: true)
96
+ end
97
+
98
+ it 'uses the production gateway host and gateway port' do
99
+ @connection.host.should == ApnsDispatch::ApnsConnection::PRODUCTION_GATEWAY_HOST
100
+ @connection.port.should == ApnsDispatch::ApnsConnection::GATEWAY_PORT
101
+ end
102
+ end
103
+
104
+ context 'with no options' do
105
+ before do
106
+ @connection = ApnsDispatch::ApnsConnection.new('test')
107
+ end
108
+
109
+ it 'uses the sandbox gateway host and gateway port' do
110
+ @connection.host.should == ApnsDispatch::ApnsConnection::SANDBOX_GATEWAY_HOST
111
+ @connection.port.should == ApnsDispatch::ApnsConnection::GATEWAY_PORT
112
+ end
113
+ end
114
+
115
+ context 'with read command' do
116
+ should_run_command_with_connection :read, 10
117
+ end
118
+
119
+ context 'with write command' do
120
+ should_run_command_with_connection :write, 'test message'
121
+ end
122
+ end
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'apns_dispatch'
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: apns_dispatch
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Costa Walcott
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-11-05 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: json
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 1.7.5
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 1.7.5
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rspec
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ~>
52
+ - !ruby/object:Gem::Version
53
+ version: '2.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '2.0'
62
+ description: A simple Ruby framework for sending push notifications and receiving
63
+ feedback using the Apple Push Notification service
64
+ email:
65
+ - costa@scvngr.com
66
+ executables: []
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - .gitignore
71
+ - Gemfile
72
+ - MIT-LICENSE
73
+ - README.md
74
+ - Rakefile
75
+ - apns_dispatch.gemspec
76
+ - lib/apns_dispatch.rb
77
+ - lib/apns_dispatch/apn_feedback.rb
78
+ - lib/apns_dispatch/apn_notification.rb
79
+ - lib/apns_dispatch/apns_connection.rb
80
+ - lib/apns_dispatch/apns_connection_pool.rb
81
+ - lib/apns_dispatch/null_apns_connection.rb
82
+ - lib/apns_dispatch/version.rb
83
+ - spec/apns_dispatch/apn_feedback_spec.rb
84
+ - spec/apns_dispatch/apn_notification_spec.rb
85
+ - spec/apns_dispatch/apns_connection_pool_spec.rb
86
+ - spec/apns_dispatch/apns_connection_spec.rb
87
+ - spec/spec_helper.rb
88
+ homepage: http://github.com/TheLevelUp/apns_dispatch
89
+ licenses: []
90
+ post_install_message:
91
+ rdoc_options: []
92
+ require_paths:
93
+ - lib
94
+ required_ruby_version: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: 1.9.2
100
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 1.8.24
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: A simple Ruby framework for communicating with the APNs
112
+ test_files:
113
+ - spec/apns_dispatch/apn_feedback_spec.rb
114
+ - spec/apns_dispatch/apn_notification_spec.rb
115
+ - spec/apns_dispatch/apns_connection_pool_spec.rb
116
+ - spec/apns_dispatch/apns_connection_spec.rb
117
+ - spec/spec_helper.rb