pushr-apns 1.0.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cc63a9d097c133a4caf7e2fe309d18c1d344440f
4
+ data.tar.gz: 4da07aeccda83b5c40ee1c0833ad54a25ba6af74
5
+ SHA512:
6
+ metadata.gz: 9d21d622874f347b12241dc570c4f48506cd34570e4d8ccf68ee41e413f5a739fc865a07f47a7be1e389906a9f2112de3910078641aca96a212f3380f9980f83
7
+ data.tar.gz: 743a1c257e31d7ec9236ce437f77bed12a17a87a6800728c0590032119298f125cd09c9d91554c53630c1f6a5073437a9d6b0c65ef37fab5f4386e3a6b465bd3
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
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,7 @@
1
+ # PushrApns
2
+
3
+ [![Build Status](https://travis-ci.org/9to5/pushr-apns.svg?branch=master)](https://travis-ci.org/9to5/pushr-apns)
4
+ [![Code Climate](https://codeclimate.com/github/9to5/pushr-apns.png)](https://codeclimate.com/github/9to5/pushr-apns)
5
+ [![Coverage Status](https://coveralls.io/repos/9to5/pushr-apns/badge.png)](https://coveralls.io/r/9to5/pushr-apns)
6
+
7
+ Please see [pushr-core](https://github.com/tompesman/pushr-core) for more information.
data/lib/pushr/apns.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'socket'
2
+ require 'pathname'
3
+ require 'pushr-apns/version'
4
+ require 'pushr/configuration'
5
+ require 'pushr/configuration_apns'
6
+ require 'pushr/message'
7
+ require 'pushr/message_apns'
8
+ require 'pushr/feedback'
9
+ require 'pushr/feedback_apns'
10
+ require 'pushr/daemon/apns'
11
+ require 'pushr/daemon/apns_support/interruptible_sleep'
12
+ require 'pushr/daemon/apns_support/disconnection_error'
13
+ require 'pushr/daemon/apns_support/connection_apns'
14
+ require 'pushr/daemon/apns_support/feedback_receiver'
@@ -0,0 +1,21 @@
1
+ module Pushr
2
+ class ConfigurationApns < Pushr::Configuration
3
+ attr_accessor :id, :type, :app, :enabled, :connections, :certificate, :certificate_password,
4
+ :sandbox, :feedback_poll, :skip_check_for_error
5
+ validates :certificate, presence: true
6
+ validates :sandbox, inclusion: { in: [true, false] }
7
+ validates :feedback_poll, presence: true
8
+ validates :skip_check_for_error, inclusion: { in: [true, false] }, allow_blank: true
9
+
10
+ def name
11
+ :apns
12
+ end
13
+
14
+ def to_json
15
+ hsh = { type: self.class.to_s, app: app, enabled: enabled, connections: connections, certificate: certificate,
16
+ certificate_password: certificate_password, sandbox: sandbox, feedback_poll: feedback_poll,
17
+ skip_check_for_error: skip_check_for_error }
18
+ MultiJson.dump(hsh)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,30 @@
1
+ module Pushr
2
+ module Daemon
3
+ class Apns
4
+ attr_accessor :configuration
5
+
6
+ def initialize(options)
7
+ self.configuration = options
8
+
9
+ @feedback_receiver = ApnsSupport::FeedbackReceiver.new(configuration)
10
+ start_feedback
11
+ end
12
+
13
+ def connectiontype
14
+ ApnsSupport::ConnectionApns
15
+ end
16
+
17
+ def start_feedback
18
+ @feedback_receiver.start
19
+ end
20
+
21
+ def stop_feedback
22
+ @feedback_receiver.stop
23
+ end
24
+
25
+ def stop
26
+ stop_feedback
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,154 @@
1
+ module Pushr
2
+ module Daemon
3
+ module ApnsSupport
4
+ class ConnectionError < StandardError; end
5
+
6
+ class ConnectionApns
7
+ attr_reader :name, :configuration
8
+ attr_accessor :last_write
9
+ IDLE_PERIOD = 30 * 60
10
+ SELECT_TIMEOUT = 0.2
11
+ ERROR_TUPLE_BYTES = 6
12
+ APN_ERRORS = {
13
+ 1 => 'Processing error',
14
+ 2 => 'Missing device token',
15
+ 3 => 'Missing topic',
16
+ 4 => 'Missing payload',
17
+ 5 => 'Missing token size',
18
+ 6 => 'Missing topic size',
19
+ 7 => 'Missing payload size',
20
+ 8 => 'Invalid token',
21
+ 255 => 'None (unknown error)'
22
+ }
23
+
24
+ def initialize(configuration, i = nil)
25
+ @configuration = configuration
26
+ if i
27
+ # Apns push connection
28
+ @name = "#{@configuration.app}: ConnectionApns #{i}"
29
+ @host = "gateway.#{configuration.sandbox ? 'sandbox.' : ''}push.apple.com"
30
+ @port = 2195
31
+ else
32
+ @name = "#{@configuration.app}: FeedbackReceiver"
33
+ @host = "feedback.#{configuration.sandbox ? 'sandbox.' : ''}push.apple.com"
34
+ @port = 2196
35
+ end
36
+ written
37
+ end
38
+
39
+ def connect
40
+ @ssl_context = setup_ssl_context
41
+ @tcp_socket, @ssl_socket = connect_socket
42
+ end
43
+
44
+ def close
45
+ @ssl_socket.close if @ssl_socket
46
+ @tcp_socket.close if @tcp_socket
47
+ rescue IOError
48
+ end
49
+
50
+ def read(num_bytes)
51
+ @ssl_socket.read(num_bytes)
52
+ end
53
+
54
+ def select(timeout)
55
+ IO.select([@ssl_socket], nil, nil, timeout)
56
+ end
57
+
58
+ def write(data)
59
+ reconnect_idle if idle_period_exceeded?
60
+
61
+ retry_count = 0
62
+
63
+ begin
64
+ write_data(data)
65
+ rescue Errno::EPIPE, Errno::ETIMEDOUT, Errno::ECONNRESET, OpenSSL::SSL::SSLError => e
66
+ retry_count += 1
67
+
68
+ if retry_count == 1
69
+ Pushr::Daemon.logger.error("[#{@name}] Lost connection to #{@host}:#{@port} (#{e.class.name}), reconnecting...")
70
+ end
71
+
72
+ if retry_count <= 3
73
+ reconnect
74
+ sleep 1
75
+ retry
76
+ else
77
+ raise ConnectionError, "#{@name} tried #{retry_count - 1} times to reconnect but failed (#{e.class.name})."
78
+ end
79
+ end
80
+ check_for_error(data)
81
+ end
82
+
83
+ def reconnect
84
+ close
85
+ @tcp_socket, @ssl_socket = connect_socket
86
+ end
87
+
88
+ def check_for_error(notification)
89
+ # check for true, because check_for_error can be nil
90
+ return if @configuration.skip_check_for_error == true
91
+
92
+ if select(SELECT_TIMEOUT)
93
+ error = nil
94
+
95
+ if tuple = read(ERROR_TUPLE_BYTES)
96
+ cmd, code, notification_id = tuple.unpack('ccN')
97
+
98
+ description = APN_ERRORS[code.to_i] || 'Unknown error. Possible push bug?'
99
+ error = Pushr::Daemon::DeliveryError.new(code, notification_id, description, 'APNS')
100
+ else
101
+ error = DisconnectionError.new
102
+ end
103
+
104
+ begin
105
+ Pushr::Daemon.logger.error("[#{@name}] Error received, reconnecting...")
106
+ reconnect
107
+ ensure
108
+ fail error if error
109
+ end
110
+ end
111
+ end
112
+
113
+ protected
114
+
115
+ def reconnect_idle
116
+ Pushr::Daemon.logger.info("[#{@name}] Idle period exceeded, reconnecting...")
117
+ reconnect
118
+ end
119
+
120
+ def idle_period_exceeded?
121
+ Time.now - last_write > IDLE_PERIOD
122
+ end
123
+
124
+ def write_data(data)
125
+ @ssl_socket.write(data.to_message)
126
+ @ssl_socket.flush
127
+ written
128
+ end
129
+
130
+ def written
131
+ self.last_write = Time.now
132
+ end
133
+
134
+ def setup_ssl_context
135
+ ssl_context = OpenSSL::SSL::SSLContext.new
136
+ ssl_context.key = OpenSSL::PKey::RSA.new(configuration.certificate, configuration.certificate_password)
137
+ ssl_context.cert = OpenSSL::X509::Certificate.new(configuration.certificate)
138
+ ssl_context
139
+ end
140
+
141
+ def connect_socket
142
+ tcp_socket = TCPSocket.new(@host, @port)
143
+ tcp_socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, 1)
144
+ tcp_socket.setsockopt(Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1)
145
+ ssl_socket = OpenSSL::SSL::SSLSocket.new(tcp_socket, @ssl_context)
146
+ ssl_socket.sync = true
147
+ ssl_socket.connect
148
+ Pushr::Daemon.logger.info("[#{@name}] Connected to #{@host}:#{@port}")
149
+ [tcp_socket, ssl_socket]
150
+ end
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,18 @@
1
+ module Pushr
2
+ module Daemon
3
+ module ApnsSupport
4
+ class DisconnectionError < StandardError
5
+ attr_reader :code, :description
6
+
7
+ def initialize
8
+ @code = nil
9
+ @description = 'APNs disconnected without returning an error.'
10
+ end
11
+
12
+ def message
13
+ 'The APNs disconnected without returning an error. This may indicate you are using an invalid certificate for the host.'
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,61 @@
1
+ module Pushr
2
+ module Daemon
3
+ module ApnsSupport
4
+ class FeedbackReceiver
5
+
6
+ FEEDBACK_TUPLE_BYTES = 38
7
+
8
+ def initialize(configuration)
9
+ @configuration = configuration
10
+ @interruptible_sleep = InterruptibleSleep.new
11
+ end
12
+
13
+ def start
14
+ @thread = Thread.new do
15
+ loop do
16
+ break if @stop
17
+ check_for_feedback
18
+ @interruptible_sleep.sleep @configuration.feedback_poll
19
+ end
20
+ end
21
+ end
22
+
23
+ def stop
24
+ @stop = true
25
+ @interruptible_sleep.interrupt_sleep
26
+ @thread.join if @thread
27
+ end
28
+
29
+ def check_for_feedback
30
+ connection = nil
31
+ begin
32
+ connection = ConnectionApns.new(@configuration)
33
+ connection.connect
34
+
35
+ while tuple = connection.read(FEEDBACK_TUPLE_BYTES)
36
+ timestamp, device = parse_tuple(tuple)
37
+ create_feedback(connection, timestamp, device)
38
+ end
39
+ rescue StandardError => e
40
+ Pushr::Daemon.logger.error(e)
41
+ ensure
42
+ connection.close if connection
43
+ end
44
+ end
45
+
46
+ protected
47
+
48
+ def parse_tuple(tuple)
49
+ failed_at, _, device = tuple.unpack('N1n1H*')
50
+ [Time.at(failed_at).utc, device]
51
+ end
52
+
53
+ def create_feedback(connection, failed_at, device)
54
+ formatted_failed_at = failed_at.strftime('%Y-%m-%d %H:%M:%S UTC')
55
+ Pushr::Daemon.logger.info("[#{connection.name}: Delivery failed at #{formatted_failed_at} for #{device}")
56
+ Pushr::FeedbackApns.new(app: @configuration.app, failed_at: failed_at, device: device, follow_up: 'delete').save
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,48 @@
1
+ module Pushr
2
+ module Daemon
3
+ module ApnsSupport
4
+ class InterruptibleSleep
5
+
6
+ def initialize
7
+ @sleep_reader, @wake_writer = IO.pipe
8
+ end
9
+
10
+ # wait for the given timeout in seconds, or data was written to the pipe.
11
+ # @return [boolean] true if the sleep was interrupted, or false
12
+ def sleep(timeout)
13
+ read_ports = [@sleep_reader]
14
+ rs, = IO.select(read_ports, nil, nil, timeout) rescue nil
15
+
16
+ # consume all data on the readable io's so that our next call will wait for more data
17
+ perform_io(rs, @sleep_reader, :read_nonblock)
18
+
19
+ !rs.nil? && rs.any?
20
+ end
21
+
22
+ # writing to the pipe will wake the sleeping thread
23
+ def interrupt_sleep
24
+ @wake_writer.write('.')
25
+ end
26
+
27
+ def close
28
+ @sleep_reader.close rescue nil
29
+ @wake_writer.close rescue nil
30
+ end
31
+
32
+ private
33
+
34
+ def perform_io(selected, io, meth)
35
+ if selected && selected.include?(io)
36
+ while true
37
+ begin
38
+ io.__send__(meth, 1)
39
+ rescue Errno::EAGAIN, IO::WaitReadable
40
+ break
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,12 @@
1
+ module Pushr
2
+ class FeedbackApns < Pushr::Feedback
3
+ attr_accessor :type, :app, :device, :follow_up, :failed_at
4
+
5
+ validates :device, format: { with: /\A[a-z0-9]{64}\z/ }
6
+ validates :follow_up, inclusion: { in: %w(delete), message: '%{value} is not a valid follow-up' }
7
+
8
+ def to_json
9
+ MultiJson.dump(type: 'Pushr::FeedbackApns', app: app, device: device, follow_up: follow_up, failed_at: failed_at)
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,65 @@
1
+ module Pushr
2
+ class MessageApns < Pushr::Message
3
+ POSTFIX = 'apns'
4
+
5
+ attr_accessor :type, :app, :device, :alert, :badge, :sound, :expiry, :attributes_for_device
6
+
7
+ validates :badge, numericality: true, allow_nil: true
8
+ validates :expiry, numericality: true, presence: true
9
+ validates :device, format: { with: /\A[a-z0-9]{64}\z/ }
10
+ validate :max_payload_size
11
+
12
+ def alert=(alert)
13
+ if alert.is_a?(Hash)
14
+ @alert = MultiJson.dump(alert)
15
+ else
16
+ @alert = alert
17
+ end
18
+ end
19
+
20
+ def alert
21
+ string_or_json = @alert
22
+ return MultiJson.load(string_or_json)
23
+ rescue
24
+ return string_or_json
25
+ end
26
+
27
+ # This method conforms to the enhanced binary format.
28
+ # http://developer.apple.com/library/ios/#documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingWIthAPS/CommunicatingWIthAPS.html#//apple_ref/doc/uid/TP40008194-CH101-SW4
29
+ def to_message
30
+ [1, 0, expiry, 0, 32, device, payload_size, payload].pack('cNNccH*na*')
31
+ end
32
+
33
+ def payload
34
+ MultiJson.dump(as_json)
35
+ end
36
+
37
+ def payload_size
38
+ payload.bytesize
39
+ end
40
+
41
+ def to_json
42
+ hsh = { type: self.class.to_s, app: app, device: device, alert: alert, badge: badge,
43
+ sound: sound, expiry: expiry, attributes_for_device: attributes_for_device }
44
+ MultiJson.dump(hsh)
45
+ end
46
+
47
+ private
48
+
49
+ def as_json
50
+ json = ActiveSupport::OrderedHash.new
51
+ json['aps'] = ActiveSupport::OrderedHash.new
52
+ json['aps']['alert'] = alert if alert
53
+ json['aps']['badge'] = badge if badge
54
+ json['aps']['sound'] = sound if sound
55
+ attributes_for_device.each { |k, v| json[k.to_s] = v.to_s } if attributes_for_device
56
+ json
57
+ end
58
+
59
+ def max_payload_size
60
+ if payload_size > 256
61
+ errors.add(:payload, 'APN notification cannot be larger than 256 bytes. Try condensing your alert and device attributes.')
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,3 @@
1
+ module PushrApns
2
+ VERSION = '1.0.0.pre.1'
3
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+ require 'pushr/daemon'
3
+ require 'pushr/daemon/apns'
4
+
5
+ describe Pushr::Daemon::Apns do
6
+ pending 'add test'
7
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+ require 'pushr/configuration_apns'
3
+ require 'pushr/message_apns'
4
+ require 'pushr/daemon'
5
+ require 'pushr/daemon/apns'
6
+ require 'pushr/daemon/apns_support/connection_apns'
7
+
8
+ describe Pushr::Daemon::ApnsSupport::ConnectionApns do
9
+ pending 'add test'
10
+
11
+ before(:each) do
12
+ Pushr::Core.configure do |config|
13
+ config.redis = ConnectionPool.new(size: 1, timeout: 1) { MockRedis.new }
14
+ end
15
+
16
+ logger = double('logger')
17
+ allow(logger).to receive(:info)
18
+ allow(logger).to receive(:error)
19
+ allow(logger).to receive(:warn)
20
+ Pushr::Daemon.logger = logger
21
+
22
+ allow(TCPSocket).to receive(:new).and_return(tcpsocket)
23
+ allow(OpenSSL::SSL::SSLSocket).to receive(:new).and_return(sslsocket)
24
+ end
25
+
26
+ let(:tcpsocket) { double('TCPSocket').as_null_object }
27
+ let(:sslsocket) { double('SSLSocket').as_null_object }
28
+ let(:certificate) { File.read(File.join(File.dirname(__FILE__), '..', '..', '..', 'support', 'cert_without_password.pem')) }
29
+ let(:config) do
30
+ Pushr::ConfigurationApns.new(app: 'app_name', connections: 2, enabled: true, certificate: certificate)
31
+ end
32
+ let(:message) do
33
+ hsh = { app: 'app_name', device: 'a' * 64, alert: 'message',
34
+ badge: 1, sound: '1.aiff', expiry: 24 * 60 * 60, attributes_for_device: { key: 'test' } }
35
+ Pushr::MessageApns.new(hsh)
36
+ end
37
+ let(:connection) { Pushr::Daemon::ApnsSupport::ConnectionApns.new(config, 1) }
38
+
39
+ describe 'sends a message' do
40
+ it 'succesful' do
41
+ expect(IO).to receive(:select).with([sslsocket], nil, nil, 0.2)
42
+ expect(sslsocket).to receive(:write).with(message.to_message)
43
+ connection.connect
44
+ connection.write(message)
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+ require 'pushr/daemon'
3
+ require 'pushr/daemon/apns'
4
+ require 'pushr/daemon/apns_support/disconnection_error'
5
+
6
+ describe Pushr::Daemon::ApnsSupport::DisconnectionError do
7
+ let(:error) { Pushr::Daemon::ApnsSupport::DisconnectionError.new }
8
+
9
+ it 'returns a nil error code' do
10
+ expect(error.code).to be_nil
11
+ end
12
+
13
+ it 'contains an error description' do
14
+ expect(error.description).not_to be_nil
15
+ end
16
+
17
+ it 'returns a message' do
18
+ expect(error.message).not_to be_nil
19
+ expect(error.to_s).not_to be_nil
20
+ end
21
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+ require 'pushr/daemon'
3
+ require 'pushr/daemon/apns'
4
+ require 'pushr/daemon/apns_support/interruptible_sleep'
5
+ require 'pushr/daemon/apns_support/feedback_receiver'
6
+
7
+ describe Pushr::Daemon::ApnsSupport::FeedbackReceiver do
8
+ pending 'add test'
9
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ require 'pushr/daemon'
3
+ require 'pushr/daemon/apns'
4
+ require 'pushr/daemon/apns_support/interruptible_sleep'
5
+
6
+ describe Pushr::Daemon::ApnsSupport::InterruptibleSleep do
7
+ let(:rd) { double(close: nil) }
8
+ let(:wr) { double(close: nil) }
9
+
10
+ subject { Pushr::Daemon::ApnsSupport::InterruptibleSleep.new }
11
+
12
+ it 'creates a new pipe' do
13
+ expect(IO).to receive(:pipe).and_return([rd, wr])
14
+ subject
15
+ end
16
+
17
+ it 'selects on the reader' do
18
+ allow(IO).to receive(:pipe).and_return([rd, wr])
19
+ expect(IO).to receive(:select).with([rd], nil, nil, 1)
20
+ subject.sleep(1)
21
+ end
22
+
23
+ it 'closes the writer' do
24
+ allow(IO).to receive(:pipe).and_return([rd, wr])
25
+ expect(rd).to receive(:close)
26
+ expect(wr).to receive(:close)
27
+ subject.close
28
+ end
29
+
30
+ it 'returns false when timeout occurs' do
31
+ expect(subject.sleep(0.01)).to eql false
32
+ end
33
+
34
+ it 'returns true when sleep does not timeout' do
35
+ subject.interrupt_sleep
36
+ expect(subject.sleep(0.01)).to eql true
37
+ end
38
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+ require 'pushr/configuration_apns'
3
+
4
+ describe Pushr::ConfigurationApns do
5
+
6
+ before(:each) do
7
+ Pushr::Core.configure do |config|
8
+ config.redis = ConnectionPool.new(size: 1, timeout: 1) { MockRedis.new }
9
+ end
10
+ end
11
+
12
+ describe 'all' do
13
+ it 'returns all configurations' do
14
+ expect(Pushr::Configuration.all).to eql([])
15
+ end
16
+ end
17
+
18
+ describe 'create' do
19
+ it 'should create a configuration' do
20
+ config = Pushr::ConfigurationApns.new(app: 'app_name', connections: 2, enabled: true, certificate: 'test', certificate_password: nil,
21
+ sandbox: true, feedback_poll: 100, skip_check_for_error: true)
22
+ expect(config.key).to eql('app_name:apns')
23
+ end
24
+ end
25
+
26
+ describe 'save' do
27
+ let(:config) do
28
+ Pushr::ConfigurationApns.new(app: 'app_name', connections: 2, enabled: true, certificate: 'test', certificate_password: nil,
29
+ sandbox: true, feedback_poll: 100, skip_check_for_error: true)
30
+ end
31
+ it 'should save a configuration' do
32
+ config.save
33
+ expect(Pushr::Configuration.all.count).to eql(1)
34
+ expect(Pushr::Configuration.all[0].class).to eql(Pushr::ConfigurationApns)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+ require 'pushr/feedback_apns'
3
+
4
+ describe Pushr::FeedbackApns do
5
+
6
+ before(:each) do
7
+ Pushr::Core.configure do |config|
8
+ config.redis = ConnectionPool.new(size: 1, timeout: 1) { MockRedis.new }
9
+ end
10
+ end
11
+
12
+ describe 'create' do
13
+ it 'should create a feedback' do
14
+ feedback = Pushr::FeedbackApns.new(app: 'app_name', device: 'a' * 64, follow_up: 'delete', failed_at: Time.now)
15
+ expect(feedback.app).to eql('app_name')
16
+ end
17
+ end
18
+
19
+ describe 'save' do
20
+ let(:feedback) { Pushr::FeedbackApns.new(app: 'app_name', device: 'a' * 64, follow_up: 'delete', failed_at: Time.now) }
21
+ it 'should save a feedback' do
22
+ feedback.save
23
+ feedback2 = Pushr::Feedback.next
24
+ expect(feedback2.class).to eql(Pushr::FeedbackApns)
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+ require 'pushr/message_apns'
3
+
4
+ describe Pushr::MessageApns do
5
+
6
+ before(:each) do
7
+ Pushr::Core.configure do |config|
8
+ config.redis = ConnectionPool.new(size: 1, timeout: 1) { MockRedis.new }
9
+ end
10
+ end
11
+
12
+ describe 'next' do
13
+ it 'returns next message' do
14
+ expect(Pushr::Message.next('pushr:app_name:apns')).to eql(nil)
15
+ end
16
+ end
17
+
18
+ describe 'save' do
19
+ let(:message) do
20
+ hsh = { app: 'app_name', device: 'a' * 64, alert: 'message',
21
+ badge: 1, sound: '1.aiff', expiry: 24 * 60 * 60, attributes_for_device: { key: 'test' } }
22
+ Pushr::MessageApns.new(hsh)
23
+ end
24
+
25
+ it 'should return true' do
26
+ expect(message.save).to eql true
27
+ end
28
+ it 'should save a message' do
29
+ message.save
30
+ expect(Pushr::Message.next('pushr:app_name:apns')).to be_kind_of(Pushr::MessageApns)
31
+ end
32
+ it 'should respond to to_message' do
33
+ expect(message.to_message).to be_kind_of(String)
34
+ end
35
+
36
+ # TODO: add more tests
37
+ end
38
+ end
@@ -0,0 +1,30 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+
6
+ require 'simplecov'
7
+ require 'coveralls'
8
+
9
+ SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
10
+ SimpleCov::Formatter::HTMLFormatter,
11
+ Coveralls::SimpleCov::Formatter
12
+ ]
13
+ SimpleCov.start
14
+
15
+ require 'pushr/core'
16
+ require 'mock_redis'
17
+
18
+ Dir['./spec/support/**/*.rb'].sort.each { |f| require f }
19
+
20
+ RSpec.configure do |config|
21
+ config.raise_errors_for_deprecations!
22
+ config.run_all_when_everything_filtered = true
23
+ # config.filter_run :focus
24
+
25
+ # Run specs in random order to surface order dependencies. If you find an
26
+ # order dependency and want to debug it, you can fix the order by providing
27
+ # the seed, which is printed after each run.
28
+ # --seed 1234
29
+ config.order = 'random'
30
+ end
@@ -0,0 +1,90 @@
1
+ Bag Attributes
2
+ localKeyID: A4 1A DB 3E 3E 45 D9 C7 51 1E E6 DC 4E BC 29 19 E4 22 95 35
3
+ subject=/C=AU/ST=Some-State/O=Internet Widgits Pty Ltd
4
+ issuer=/C=UK/ST=Some-State/O=Internet Widgits Pty Ltd
5
+ -----BEGIN CERTIFICATE-----
6
+ MIIE/jCCAuYCAQEwDQYJKoZIhvcNAQEFBQAwRTELMAkGA1UEBhMCVUsxEzARBgNV
7
+ BAgTClNvbWUtU3RhdGUxITAfBgNVBAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0
8
+ ZDAeFw0xMjEyMjgxMzU4NTdaFw0xNDEyMjgxMzU4NTdaMEUxCzAJBgNVBAYTAkFV
9
+ MRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBXaWRnaXRz
10
+ IFB0eSBMdGQwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDYCSfYP0P7
11
+ F+JbxUHYsl/9Plr2bFZGMBygqs5RWpw1X977p4FqcNtnRmGENQ/I5vE3KkAKCAlS
12
+ gmus7CjB9/FVzoJq65cT3wBkcqAAzPgIQuI0UjP+rLktV84eUup7Pt6f/Bmt/fBj
13
+ h3fvf80nBQcdK2ztu1xtTs7EsRgyudUDDEIUZw9ddK21RbCMYE0PFY0QapNVvevs
14
+ 8lDVUJX60bZqVqzhvlTzmEaBF+E66J/DhHuhcWZV588hVKjHMNED9Aq4PxRy78vI
15
+ 54v1buX2Y9C6dHfIzfu7zz0Zv5NAY1BZKpaglVGKArOrX+K6O7Bq+DwckTm1AbUf
16
+ pQqi10ghveOtVrm4GXJ4OYW8ohdYKMFjzwhgTTr/NQ+EVlcZ+8AOPVPPJk0HgPu/
17
+ eMNiLytvcSnB09OzIUAzcxOof2a1zfxn7aPzBTEC6kkoDJC/BDG8wxySUz0zRyyM
18
+ jUN2+J1mBJZX1h1sM/4ycAzsX1EYm2II5GGJaFngiV45Qv7wTC7W31kvih7FsdUX
19
+ rEYMkevB5AFPUtIvzevLUObLbPW4yWvU2CMcKLZdIaPTvtPMba91t1YOdufpPRDd
20
+ HTmT42h0aUWDSrcWDAuZPPPqEIQNjRVCudtHtobZDeaUfvbx05CvypyuNFaPonuQ
21
+ l5h0LpZWPetvfsrOlMxPm2kfVcxm8mWjzQIDAQABMA0GCSqGSIb3DQEBBQUAA4IC
22
+ AQBiwdPfLU0kjbU1hM6grUHduLqOPDqLT5gefGRrL5fDqb86G08+Wz+Tnn1YmH+7
23
+ rfiQhCrdi4zxRv6ZEaKZeEm/q1UMttuUdXuzd25BEEtav4R0POdR+H1q3trlS0ol
24
+ EcWlAbRgVaT2tTyKGAW54fH8vZPqS6IP+mXIzfaOFECPEgAO8BL8t7hBDpkL4ASO
25
+ HbU7ktYamsr6PAei3kXAnJ1thXyQqrhelwLrQJyM8RVOJYUgVbl6Fpdtgu7YQF5G
26
+ kxTAfshSmDCQkf+tbUEs44rZ6BZ2TWnbXjQGkRntHcMCP/1rjsPPdX3oeZ7P3jMQ
27
+ XER3drdm1mOiSdDUKbem9GzQ9Dx7WkwKNLYAZ+IWzVRACzGxnkxXyxOEFIgesDgg
28
+ RIhczN+eLIR8iwcUxEVKFmmsbEIve3Uh1/NE6xGudbfZDNfhyOhiNYIBnQqVkk8l
29
+ c3gw2UDR3lXTayiiXhK2l1etsyxtYncT3pgDsCe72RODrGKbASt3FzfBbalzN0GG
30
+ 9tiPSNtGqCch9q4eHfViUh9s3+8n4bknAYcwzQ96+gMEn8PUVtDBv9F8Kxffn/Jt
31
+ XMWKX76nTVuAuCLkXxrKwc01lq8SeuvCH650xsv0LBvxj9h6vR34vHGrj0C3sH2E
32
+ VQpelKNv8IEwkSiQcwDtU8H0jaPJNqmYlkrxasSrSeg6PA==
33
+ -----END CERTIFICATE-----
34
+ Bag Attributes
35
+ localKeyID: A4 1A DB 3E 3E 45 D9 C7 51 1E E6 DC 4E BC 29 19 E4 22 95 35
36
+ Key Attributes: <No Attributes>
37
+ -----BEGIN RSA PRIVATE KEY-----
38
+ Proc-Type: 4,ENCRYPTED
39
+ DEK-Info: DES-EDE3-CBC,874F2C982467BF08
40
+
41
+ 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
90
+ -----END RSA PRIVATE KEY-----
@@ -0,0 +1,59 @@
1
+ Bag Attributes
2
+ friendlyName: test certificate
3
+ localKeyID: 00 93 8F E4 A3 C3 75 64 3D 7E EA 14 0B 0A EA DD 15 85 8A D5
4
+ subject=/CN=test certificate/O=Example/OU=Example/ST=QLD/C=AU/L=Example/emailAddress=user@example.com
5
+ issuer=/CN=test certificate/O=Example/OU=Example/ST=QLD/C=AU/L=Example/emailAddress=user@example.com
6
+ -----BEGIN CERTIFICATE-----
7
+ MIID5jCCAs6gAwIBAgIBATALBgkqhkiG9w0BAQswgY0xGTAXBgNVBAMMEHRlc3Qg
8
+ Y2VydGlmaWNhdGUxEDAOBgNVBAoMB0V4YW1wbGUxEDAOBgNVBAsMB0V4YW1wbGUx
9
+ DDAKBgNVBAgMA1FMRDELMAkGA1UEBhMCQVUxEDAOBgNVBAcMB0V4YW1wbGUxHzAd
10
+ BgkqhkiG9w0BCQEWEHVzZXJAZXhhbXBsZS5jb20wHhcNMTIwOTA5MDMxODMyWhcN
11
+ MjIwOTA3MDMxODMyWjCBjTEZMBcGA1UEAwwQdGVzdCBjZXJ0aWZpY2F0ZTEQMA4G
12
+ A1UECgwHRXhhbXBsZTEQMA4GA1UECwwHRXhhbXBsZTEMMAoGA1UECAwDUUxEMQsw
13
+ CQYDVQQGEwJBVTEQMA4GA1UEBwwHRXhhbXBsZTEfMB0GCSqGSIb3DQEJARYQdXNl
14
+ ckBleGFtcGxlLmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKF+
15
+ UDsN1sLen8g+97PNTiWju9+wkSv+H5rQlvb6YFLPx11YvqpK8ms6kFU1OmWeLfmh
16
+ cpsT+bZtKupC7aGPoSG3RXzzf/YUMgs/ZSXA0idZHA6tkReAEzIX6jL5otfPWbaP
17
+ luCTUoVMeP4u9ywk628zlqh9IQHC1Agl0R1xGCpULDk8kn1gPyEisl38wI5aDbzy
18
+ 6lYQGNUKOqt1xfVjtIFe/jyY/v0sxFjIJlRLcAFBuJx4sRV+PwRBkusOQtYwcwpI
19
+ loMxJj+GQe66ueATW81aC4iOU66DAFFEuGzwIwm3bOilimGGQbGb92F339RfmSOo
20
+ TPAvVhsakI3mzESb4lkCAwEAAaNRME8wDgYDVR0PAQH/BAQDAgeAMCAGA1UdJQEB
21
+ /wQWMBQGCCsGAQUFBwMCBggrBgEFBQcDATAbBgNVHREEFDASgRB1c2VyQGV4YW1w
22
+ bGUuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQA5UbNR+83ZdI2DiaB4dRmy0V5RDAqJ
23
+ k9+QskcTV4gBTjsOBS46Dw1tI6iTrfTyjYJdnyH0Y2Y2YVWBnvtON41UCZak+4ed
24
+ /IqyzU0dtfZ+frWa0RY4reyl80TwqnzyJfni0nDo4zGGvz70cxyaz2u1BWqwLjqb
25
+ dh8Dxvt+aHW2MQi0iGKh/HNbgwVanR4+ubNwziK9sR1Rnq9MkHWtwBw16SXQG6ao
26
+ SZKASWNaH8VL08Zz0E98cwd137UJkPsldCwJ8kHR5OzkcjPdXvnGD3d64yy2TC1Z
27
+ Gy1Aazt98wPcTYBytlhK8Rvzg9OoY9QmsdpmWxz1ZCXECJNqCa3IKsqO
28
+ -----END CERTIFICATE-----
29
+ Bag Attributes
30
+ friendlyName: test certificate
31
+ localKeyID: 00 93 8F E4 A3 C3 75 64 3D 7E EA 14 0B 0A EA DD 15 85 8A D5
32
+ Key Attributes: <No Attributes>
33
+ -----BEGIN RSA PRIVATE KEY-----
34
+ MIIEpQIBAAKCAQEAoX5QOw3Wwt6fyD73s81OJaO737CRK/4fmtCW9vpgUs/HXVi+
35
+ qkryazqQVTU6ZZ4t+aFymxP5tm0q6kLtoY+hIbdFfPN/9hQyCz9lJcDSJ1kcDq2R
36
+ F4ATMhfqMvmi189Zto+W4JNShUx4/i73LCTrbzOWqH0hAcLUCCXRHXEYKlQsOTyS
37
+ fWA/ISKyXfzAjloNvPLqVhAY1Qo6q3XF9WO0gV7+PJj+/SzEWMgmVEtwAUG4nHix
38
+ FX4/BEGS6w5C1jBzCkiWgzEmP4ZB7rq54BNbzVoLiI5TroMAUUS4bPAjCbds6KWK
39
+ YYZBsZv3YXff1F+ZI6hM8C9WGxqQjebMRJviWQIDAQABAoIBAQCTiLIDQUFSBdAz
40
+ QFNLD+S0vkCEuunlJuP4q1c/ir006l1YChsluBJ/o6D4NwiCjV+zDquEwVsALftm
41
+ yH4PewfZpXT2Ef508T5GyEO/mchj6iSXxDkpHvhqay6qIyWBwwxSnBtaTzy0Soi+
42
+ rmlhCtmLXbXld2sQEM1kJChGnWtWPtvSyrn+mapNPZviGRtgRNK+YsrAti1nUext
43
+ 2syO5mTdHf1D8GR7I98OaX6odREuSocEV9PzfapWZx2GK5tvRiS1skiug5ciieTd
44
+ Am5/C+bb31h4drFslihLb5BRGO5SFQJvMJL2Sx1f19BCC4XikS01P4/zZbxQNq79
45
+ kxEQuDGBAoGBANP4pIYZ5xshCkx7cTYqmxzWLClGKE2S7Oa8N89mtOwfmqT9AFun
46
+ t9Us9Ukbi8BaKlKhGpQ1HlLf/KVcpyW0x2qLou6AyIWYH+/5VaR3graNgUnzpK9f
47
+ 1F5HoaNHbhlAoebqhzhASFlJI2aqUdQjdOv73z+s9szJU4gpILNwGDFnAoGBAMMJ
48
+ j+vIxtG9J2jldyoXzpg5mbMXSj9u/wFLBVdjXWyOoiqVMMBto53RnoqAom7Ifr9D
49
+ 49LxRAT1Q3l4vs/YnM3ziMsIg2vQK1EbrLsY9OnD/kvPaLXOlNIOdfLM8UeVWZMc
50
+ I4LPbbZrhv/7CC8RjbRhMoWWdGYPvxmvD6V4ZDY/AoGBALoI6OxA45Htx4okdNHj
51
+ RstiNNPsnQaoQn6nBhxiubraafEPkzbd1fukP4pwQJELEUX/2sHkdL6rkqLW1GPF
52
+ a5dZAiBsqpCFWNJWdBGqSfBJ9QSgbxLz+gDcwUH6OOi0zuNJRm/aCyVBiW5bYQHc
53
+ NIvAPMk31ksZDtTbs7WIVdNVAoGBALZ1+KWNxKqs+fSBT5UahpUUtfy8miJz9a7A
54
+ /3M8q0cGvSF3Rw+OwpW/aEGMi+l2OlU27ykFuyukRAac9m296RwnbF79TO2M5ylO
55
+ 6a5zb5ROXlWP6RbE96b4DlIidssQJqegmHwlEC+rsrVBpOtb0aThlYEyOxzMOGyP
56
+ wOR9l8rDAoGADZ4TUHFM6VrvPlUZBkGbqiyXH9IM/y9JWk+22JQCEGnM6RFZemSs
57
+ jxWqQiPAdJtb3xKryJSCMtFPH9azedoCrSgaMflJ1QgoXgpiKZyoEXWraVUggh/0
58
+ CEavgZcTZ6SvMuayqJdGGB+zb1V8XwXMtCjApR/kTm47DjxO4DmpOPs=
59
+ -----END RSA PRIVATE KEY-----
metadata ADDED
@@ -0,0 +1,231 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pushr-apns
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0.pre.1
5
+ platform: ruby
6
+ authors:
7
+ - Tom Pesman
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-05-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: multi_json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pushr-core
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: activemodel
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 3.0.0.beta2
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '='
67
+ - !ruby/object:Gem::Version
68
+ version: 3.0.0.beta2
69
+ - !ruby/object:Gem::Dependency
70
+ name: guard
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: guard-rspec
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: mock_redis
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: simplecov
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rake
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ - !ruby/object:Gem::Dependency
140
+ name: coveralls
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: '0'
146
+ type: :development
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - ">="
151
+ - !ruby/object:Gem::Version
152
+ version: '0'
153
+ - !ruby/object:Gem::Dependency
154
+ name: rubocop
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ description: APNS support for the modular push daemon.
168
+ email:
169
+ - tom@tnux.net
170
+ executables: []
171
+ extensions: []
172
+ extra_rdoc_files: []
173
+ files:
174
+ - lib/pushr-apns/version.rb
175
+ - lib/pushr/apns.rb
176
+ - lib/pushr/configuration_apns.rb
177
+ - lib/pushr/daemon/apns.rb
178
+ - lib/pushr/daemon/apns_support/connection_apns.rb
179
+ - lib/pushr/daemon/apns_support/disconnection_error.rb
180
+ - lib/pushr/daemon/apns_support/feedback_receiver.rb
181
+ - lib/pushr/daemon/apns_support/interruptible_sleep.rb
182
+ - lib/pushr/feedback_apns.rb
183
+ - lib/pushr/message_apns.rb
184
+ - README.md
185
+ - MIT-LICENSE
186
+ - spec/lib/pushr/apns_spec.rb
187
+ - spec/lib/pushr/apns_support/connection_apns_spec.rb
188
+ - spec/lib/pushr/apns_support/disconnection_error_spec.rb
189
+ - spec/lib/pushr/apns_support/feedback_receiver_spec.rb
190
+ - spec/lib/pushr/apns_support/interruptible_sleep_spec.rb
191
+ - spec/lib/pushr/configuration_apns_spec.rb
192
+ - spec/lib/pushr/feedback_apns_spec.rb
193
+ - spec/lib/pushr/message_apns_spec.rb
194
+ - spec/spec_helper.rb
195
+ - spec/support/cert_with_password.pem
196
+ - spec/support/cert_without_password.pem
197
+ homepage: https://github.com/tompesman/pushr-apns
198
+ licenses: []
199
+ metadata: {}
200
+ post_install_message:
201
+ rdoc_options: []
202
+ require_paths:
203
+ - lib
204
+ required_ruby_version: !ruby/object:Gem::Requirement
205
+ requirements:
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: '0'
209
+ required_rubygems_version: !ruby/object:Gem::Requirement
210
+ requirements:
211
+ - - ">"
212
+ - !ruby/object:Gem::Version
213
+ version: 1.3.1
214
+ requirements: []
215
+ rubyforge_project:
216
+ rubygems_version: 2.1.11
217
+ signing_key:
218
+ specification_version: 4
219
+ summary: APNS (iOS/Apple) part of the modular push daemon.
220
+ test_files:
221
+ - spec/lib/pushr/apns_spec.rb
222
+ - spec/lib/pushr/apns_support/connection_apns_spec.rb
223
+ - spec/lib/pushr/apns_support/disconnection_error_spec.rb
224
+ - spec/lib/pushr/apns_support/feedback_receiver_spec.rb
225
+ - spec/lib/pushr/apns_support/interruptible_sleep_spec.rb
226
+ - spec/lib/pushr/configuration_apns_spec.rb
227
+ - spec/lib/pushr/feedback_apns_spec.rb
228
+ - spec/lib/pushr/message_apns_spec.rb
229
+ - spec/spec_helper.rb
230
+ - spec/support/cert_with_password.pem
231
+ - spec/support/cert_without_password.pem