pushr-apns 1.0.0.pre.1

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.
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