grocer 0.0.6 → 0.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -33,20 +33,21 @@ gem 'grocer'
33
33
  # Information on obtaining a `.pem` file for use with `certificate` is shown
34
34
  # later.
35
35
  pusher = Grocer.pusher(
36
- certificate: "/path/to/cert.pem", # required
37
- passphrase: "", # optional
38
- gateway: "gateway.push.apple.com", # optional; See note below.
39
- port: 2195, # optional
40
- retries: 3 # optional
36
+ certificate: "/path/to/cert.pem", # required
37
+ passphrase: "", # optional
38
+ gateway: "gateway.push.apple.com", # optional; See note below.
39
+ port: 2195, # optional
40
+ retries: 3 # optional
41
41
  )
42
42
  ```
43
43
 
44
44
  #### Notes
45
45
 
46
- * `gateway`: Defaults to `gateway.push.apple.com` **only** when running in a
47
- production environment, as determined by either the `RAILS_ENV` or
48
- `RACK_ENV` environment variables. In all other cases, it defaults to the
49
- sandbox gateway, `gateway.sandbox.push.apple.com`.
46
+ * `gateway`: Defaults to different values depending on the `RAILS_ENV` or
47
+ `RACK_ENV` environment variables. If set to `production`, defaults to
48
+ `gateway.push.apple.com`, if set to `test`, defaults to `localhost` (see
49
+ "Acceptance Testing" later), otherwise defaults to
50
+ `gateway.sandbox.push.apple.com`.
50
51
  * `retries`: The number of times **grocer** will retry writing to or reading
51
52
  from the Apple Push Notification Service before raising any errors to client
52
53
  code.
@@ -175,3 +176,43 @@ openssl pkcs12 -in exported_certificate.p12 -out certificate.pem -nodes -clcerts
175
176
  ```
176
177
 
177
178
  The `certificate.pem` file that is generated can be used with **grocer**.
179
+
180
+ ## Acceptance Testing
181
+
182
+ ** YET TO BE IMPLEMENTED **
183
+
184
+ Grocer ships with framework to setup a real looking APNS server. It listens on
185
+ a real SSL-capable socket bound to localhost.
186
+
187
+ You can setup an APNS client to talk to it, then inspect the notifications the
188
+ server received.
189
+
190
+ The server simply exposes a blocking queue where notifications are placed when
191
+ they are received. It is your responsibility to timeout if a message is not
192
+ received in a reasonable amount of time.
193
+
194
+ For example, in RSpec:
195
+
196
+ ```ruby
197
+ require 'timeout'
198
+
199
+ describe "apple push notifications" do
200
+ before do
201
+ @server = Grocer.server(port: 2195)
202
+ @server.accept # starts listening in background
203
+ end
204
+
205
+ after do
206
+ @server.close
207
+ end
208
+
209
+ specify "As a user, I receive notifications on my phone when awesome things happen" do
210
+ # ... exercise code that would send APNS notifications ...
211
+
212
+ Timeout.timeout(3) {
213
+ notification = @server.notifications.pop # blocking
214
+ notification.alert.should == "An awesome thing happened"
215
+ }
216
+ end
217
+ end
218
+ ```
data/grocer.gemspec CHANGED
@@ -25,7 +25,7 @@ Gem::Specification.new do |gem|
25
25
  gem.require_paths = ["lib"]
26
26
  gem.version = Grocer::VERSION
27
27
 
28
- gem.add_development_dependency 'rspec', '~> 2.9.0'
28
+ gem.add_development_dependency 'rspec', '~> 2.10.0'
29
29
  gem.add_development_dependency 'pry', '~> 0.9.8'
30
30
  gem.add_development_dependency 'mocha'
31
31
  gem.add_development_dependency 'bourne'
data/lib/grocer.rb CHANGED
@@ -4,6 +4,7 @@ require_relative 'grocer/feedback_connection'
4
4
  require_relative 'grocer/push_connection'
5
5
  require_relative 'grocer/pusher'
6
6
  require_relative 'grocer/version'
7
+ require_relative 'grocer/server'
7
8
 
8
9
  module Grocer
9
10
 
@@ -21,4 +22,9 @@ module Grocer
21
22
  Pusher.new(connection)
22
23
  end
23
24
 
25
+ def self.server(options = { })
26
+ ssl_server = SSLServer.new(options)
27
+ Server.new(ssl_server)
28
+ end
29
+
24
30
  end
@@ -1,5 +1,4 @@
1
1
  require 'grocer'
2
- require 'grocer/no_certificate_error'
3
2
  require 'grocer/no_gateway_error'
4
3
  require 'grocer/no_port_error'
5
4
  require 'grocer/ssl_connection'
@@ -9,7 +8,7 @@ module Grocer
9
8
  attr_reader :certificate, :passphrase, :gateway, :port, :retries
10
9
 
11
10
  def initialize(options = {})
12
- @certificate = options.fetch(:certificate) { fail NoCertificateError }
11
+ @certificate = options.fetch(:certificate) { nil }
13
12
  @gateway = options.fetch(:gateway) { fail NoGatewayError }
14
13
  @port = options.fetch(:port) { fail NoPortError }
15
14
  @passphrase = options.fetch(:passphrase) { nil }
@@ -0,0 +1,39 @@
1
+ require 'json'
2
+ require_relative 'notification'
3
+
4
+ module Grocer
5
+ class NotificationReader
6
+ include Enumerable
7
+
8
+ def initialize(io)
9
+ @io = io
10
+ end
11
+
12
+ def each
13
+ while notification = read_notification
14
+ yield notification
15
+ end
16
+ end
17
+
18
+ private
19
+
20
+ def read_notification
21
+ @io.read(1) # version (not used for now)
22
+
23
+ payload = { }
24
+ payload[:identifier] = @io.read(4).unpack("N").first
25
+ payload[:expiry] = Time.at(@io.read(4).unpack("N").first)
26
+
27
+ @io.read(2) # device token length (always 32, so not used)
28
+ payload[:device_token] = @io.read(32).unpack("H*").first
29
+
30
+ payload_length = @io.read(2).unpack("n").first
31
+ payload_hash = JSON.parse(@io.read(payload_length), symbolize_names: true)
32
+
33
+ payload.merge!(payload_hash.delete(:aps) || { })
34
+ payload[:custom] = payload_hash
35
+
36
+ Grocer::Notification.new(payload)
37
+ end
38
+ end
39
+ end
@@ -5,6 +5,7 @@ module Grocer
5
5
  class PushConnection < SimpleDelegator
6
6
 
7
7
  PRODUCTION_GATEWAY = 'gateway.push.apple.com'
8
+ LOCAL_GATEWAY = '127.0.0.1'
8
9
  SANDBOX_GATEWAY = 'gateway.sandbox.push.apple.com'
9
10
 
10
11
  def initialize(options)
@@ -22,7 +23,14 @@ module Grocer
22
23
  end
23
24
 
24
25
  def find_default_gateway
25
- Grocer.env == 'production' ? PRODUCTION_GATEWAY : SANDBOX_GATEWAY
26
+ case Grocer.env
27
+ when 'production'
28
+ PRODUCTION_GATEWAY
29
+ when 'test'
30
+ LOCAL_GATEWAY
31
+ else
32
+ SANDBOX_GATEWAY
33
+ end
26
34
  end
27
35
 
28
36
  end
@@ -0,0 +1,39 @@
1
+ require 'thread'
2
+ require_relative 'notification_reader'
3
+ require_relative 'ssl_server'
4
+
5
+ module Grocer
6
+ class Server
7
+ attr_reader :notifications
8
+
9
+ def initialize(server)
10
+ @server = server
11
+
12
+ @clients = []
13
+ @notifications = Queue.new
14
+ end
15
+
16
+ def accept
17
+ Thread.new {
18
+ @server.accept { |client|
19
+ @clients << client
20
+
21
+ Thread.new {
22
+ # Read from client into queue
23
+ NotificationReader.new(client).each(&notifications.method(:push))
24
+ }
25
+ }
26
+ }
27
+ end
28
+
29
+ def close
30
+ if @server
31
+ @server.close
32
+ @server = nil
33
+ end
34
+
35
+ @clients.each(&:close)
36
+ @clients = []
37
+ end
38
+ end
39
+ end
@@ -20,10 +20,13 @@ module Grocer
20
20
  end
21
21
 
22
22
  def connect
23
- cert_data = File.read(certificate)
24
23
  context = OpenSSL::SSL::SSLContext.new
25
- context.key = OpenSSL::PKey::RSA.new(cert_data, passphrase)
26
- context.cert = OpenSSL::X509::Certificate.new(cert_data)
24
+
25
+ if certificate
26
+ cert_data = File.read(certificate)
27
+ context.key = OpenSSL::PKey::RSA.new(cert_data, passphrase)
28
+ context.cert = OpenSSL::X509::Certificate.new(cert_data)
29
+ end
27
30
 
28
31
  @sock = TCPSocket.new(gateway, port)
29
32
  @ssl = OpenSSL::SSL::SSLSocket.new(@sock, context)
@@ -0,0 +1,59 @@
1
+ require 'openssl'
2
+ require 'socket'
3
+ require 'thread'
4
+
5
+ module Grocer
6
+ class SSLServer
7
+ attr_accessor :port
8
+
9
+ def initialize(options = {})
10
+ options = defaults.merge(options)
11
+ options.each { |k, v| send("#{k}=", v) }
12
+ end
13
+
14
+ def defaults
15
+ {
16
+ port: 2195
17
+ }
18
+ end
19
+
20
+ def accept
21
+ while socket = ssl_socket.accept
22
+ yield socket if block_given?
23
+ end
24
+ end
25
+
26
+ def close
27
+ if @ssl_socket
28
+ @ssl_socket.close
29
+ @ssl_socket = nil
30
+ @socket = nil
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def ssl_socket
37
+ @ssl_socket ||= OpenSSL::SSL::SSLServer.new(socket, context)
38
+ end
39
+
40
+ def socket
41
+ @socket ||= TCPServer.new('127.0.0.1', port)
42
+ end
43
+
44
+ def context
45
+ @context ||= OpenSSL::SSL::SSLContext.new.tap do |c|
46
+ c.cert = OpenSSL::X509::Certificate.new(File.read(crt_path))
47
+ c.key = OpenSSL::PKey::RSA.new(File.read(key_path))
48
+ end
49
+ end
50
+
51
+ def crt_path
52
+ File.join(File.dirname(__FILE__), "test", "server.crt")
53
+ end
54
+
55
+ def key_path
56
+ File.join(File.dirname(__FILE__), "test", "server.key")
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,15 @@
1
+ -----BEGIN CERTIFICATE-----
2
+ MIICVzCCAcACCQDW8lrgRNBDkDANBgkqhkiG9w0BAQUFADBwMQswCQYDVQQGEwJV
3
+ UzELMAkGA1UECBMCR0ExEDAOBgNVBAcTB0F0bGFudGExGzAZBgNVBAoTEkhpZ2hn
4
+ cm9vdmUgU3R1ZGlvczElMCMGA1UEAxMcZmFrZS5ncm9jZXIuYXBucy5leGFtcGxl
5
+ LmNvbTAeFw0xMjA1MDgxNzAxMDNaFw0yMjA1MDYxNzAxMDNaMHAxCzAJBgNVBAYT
6
+ AlVTMQswCQYDVQQIEwJHQTEQMA4GA1UEBxMHQXRsYW50YTEbMBkGA1UEChMSSGln
7
+ aGdyb292ZSBTdHVkaW9zMSUwIwYDVQQDExxmYWtlLmdyb2Nlci5hcG5zLmV4YW1w
8
+ bGUuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCXB2pln5tJUImFg7T1
9
+ +qaN7ngtpZUUCC//n96Cs6Kih90XW1RRNHSK+oYB66JzErzgDjF81eApYNzPN89F
10
+ pvFpd0IWIRM4qnw9fruA3Nub6XUljtCu4n1t4+/1o6VNCl7zcz778qSsDFJYALxn
11
+ jECl0oUSG46T3kI8Su/Mm6mv6QIDAQABMA0GCSqGSIb3DQEBBQUAA4GBAEn783m/
12
+ BdbRH/PJkGeYPWzUP05hmloo5VT3kbopmRUslqGMVM9vbPpdx7eTGxdPMUfVglK1
13
+ 7vqOwFG6dmzH6gRb2d4+TRafjCK1NBm7fPhKQmxc8WgsqQusMW7SbZ6+OoIMxLbY
14
+ 4s+EqIePtUoY+XqbCKXVdyUI1Kw99oph2UXN
15
+ -----END CERTIFICATE-----
@@ -0,0 +1,12 @@
1
+ -----BEGIN CERTIFICATE REQUEST-----
2
+ MIIBsDCCARkCAQAwcDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkdBMRAwDgYDVQQH
3
+ EwdBdGxhbnRhMRswGQYDVQQKExJIaWdoZ3Jvb3ZlIFN0dWRpb3MxJTAjBgNVBAMT
4
+ HGZha2UuZ3JvY2VyLmFwbnMuZXhhbXBsZS5jb20wgZ8wDQYJKoZIhvcNAQEBBQAD
5
+ gY0AMIGJAoGBAJcHamWfm0lQiYWDtPX6po3ueC2llRQIL/+f3oKzoqKH3RdbVFE0
6
+ dIr6hgHronMSvOAOMXzV4Clg3M83z0Wm8Wl3QhYhEziqfD1+u4Dc25vpdSWO0K7i
7
+ fW3j7/WjpU0KXvNzPvvypKwMUlgAvGeMQKXShRIbjpPeQjxK78ybqa/pAgMBAAGg
8
+ ADANBgkqhkiG9w0BAQUFAAOBgQBjcLRdwZpWwp7mBrn2Nw8opFtVbB/jlPTSh794
9
+ vdRuVrYAUs11q4qg1viNvZ0qoa6J//U8MkuzTI4JwpIKKUCenUZDC+es7KjwjiGE
10
+ 93RmiT0fqzdIw/TVaKpbsdqmI5ZeQyKpN/UXImftO5xG0ARmQRxVpKnlsiZPTIuR
11
+ BroEgQ==
12
+ -----END CERTIFICATE REQUEST-----
@@ -0,0 +1,15 @@
1
+ -----BEGIN RSA PRIVATE KEY-----
2
+ MIICXAIBAAKBgQCXB2pln5tJUImFg7T1+qaN7ngtpZUUCC//n96Cs6Kih90XW1RR
3
+ NHSK+oYB66JzErzgDjF81eApYNzPN89FpvFpd0IWIRM4qnw9fruA3Nub6XUljtCu
4
+ 4n1t4+/1o6VNCl7zcz778qSsDFJYALxnjECl0oUSG46T3kI8Su/Mm6mv6QIDAQAB
5
+ AoGBAIfLRSEmhws+fMgtihH5UrQfDLOORCKE0hN3fSvrtHmKy4Hqvj9deMRVSRSE
6
+ 98WbvXN/j4N9ElZiH2e5+IXZ+wjFddSmksKCJEj8IV0oPikNYwEg1pFo5vMSHMTA
7
+ 4kBcx6tgKH8kjPxGz1w5RkxvwdqkKyFDask2Xxn4DDfYI6ZhAkEAxdPun7Yf3EOz
8
+ /OrHAEC1WXaQlrojcBheNSJiON+joiPeXSqbKXDAkHu8pQWawilIm7M/fIGADT9c
9
+ BkWqlbCopQJBAMNwj4bSpNKa83URN8YWJYPocvqb8ZqoS7oZaJLklrRIxnzPACKa
10
+ GyJlj2z6ADotJTbdMu0hBjjXf/BAX117AvUCQACRRxH2N8kt+Io1MjTx+pMzH98O
11
+ 0aM0rrCAVL/NBG8mozCpOqC3zhWcBUKD7Zm4/JhVv0zgIjnngKAT+xVK2HECQCJp
12
+ KCwx3GlkdOcwz+QltBdEjzIG0QRNC4BJxvrOGqbFhYUmITz2az6kKRuj7PRRTJMb
13
+ YUMVJHZPoywW+XOJHB0CQBtW+13o60wQfluoQZs1DeHRTVSV5sEUUTZuLdp88dxA
14
+ XZPsIY2e3fgrwpgohGvCZ8q7Em9PP9UiWCnEDRhjRW8=
15
+ -----END RSA PRIVATE KEY-----
@@ -1,3 +1,3 @@
1
1
  module Grocer
2
- VERSION = '0.0.6'
2
+ VERSION = '0.0.7'
3
3
  end
@@ -11,11 +11,6 @@ describe Grocer::Connection do
11
11
  Grocer::SSLConnection.stubs(:new).returns(ssl)
12
12
  end
13
13
 
14
- it 'requires a certificate' do
15
- connection_options.delete(:certificate)
16
- -> { described_class.new(connection_options) }.should raise_error(Grocer::NoCertificateError)
17
- end
18
-
19
14
  it 'can be initialized with a certificate' do
20
15
  subject.certificate.should == '/path/to/cert.pem'
21
16
  end
@@ -0,0 +1,66 @@
1
+ require 'spec_helper'
2
+ require 'stringio'
3
+ require 'grocer/notification_reader'
4
+
5
+ describe Grocer::NotificationReader do
6
+ let(:io) { StringIO.new }
7
+ subject { described_class.new(io) }
8
+
9
+ context "Version 1 messages" do
10
+ it "reads identifier" do
11
+ io.write(Grocer::Notification.new(identifier: 1234, alert: "Foo").to_bytes)
12
+ io.rewind
13
+
14
+ notification = subject.first
15
+ notification.identifier.should == 1234
16
+ end
17
+
18
+ it "reads expiry" do
19
+ io.write(Grocer::Notification.new(expiry: Time.utc(2013, 3, 24), alert: "Foo").to_bytes)
20
+ io.rewind
21
+
22
+ notification = subject.first
23
+ notification.expiry.should == Time.utc(2013, 3, 24)
24
+ end
25
+
26
+ it "reads device token" do
27
+ io.write(Grocer::Notification.new(device_token: 'fe15a27d5df3c34778defb1f4f3880265cc52c0c047682223be59fb68500a9a2', alert: "Foo").to_bytes)
28
+ io.rewind
29
+
30
+ notification = subject.first
31
+ notification.device_token.should == 'fe15a27d5df3c34778defb1f4f3880265cc52c0c047682223be59fb68500a9a2'
32
+ end
33
+
34
+ it "reads alert" do
35
+ io.write(Grocer::Notification.new(alert: "Foo").to_bytes)
36
+ io.rewind
37
+
38
+ notification = subject.first
39
+ notification.alert.should == "Foo"
40
+ end
41
+
42
+ it "reads badge" do
43
+ io.write(Grocer::Notification.new(alert: "Foo", badge: 5).to_bytes)
44
+ io.rewind
45
+
46
+ notification = subject.first
47
+ notification.badge.should == 5
48
+ end
49
+
50
+ it "reads sound" do
51
+ io.write(Grocer::Notification.new(alert: "Foo", sound: "foo.aiff").to_bytes)
52
+ io.rewind
53
+
54
+ notification = subject.first
55
+ notification.sound.should == "foo.aiff"
56
+ end
57
+
58
+ it "reads custom attributes" do
59
+ io.write(Grocer::Notification.new(alert: "Foo", custom: { foo: "bar" }).to_bytes)
60
+ io.rewind
61
+
62
+ notification = subject.first
63
+ notification.custom.should == { foo: "bar" }
64
+ end
65
+ end
66
+ end
@@ -35,9 +35,9 @@ describe Grocer::PushConnection do
35
35
  subject.gateway.should == 'gateway.sandbox.push.apple.com'
36
36
  end
37
37
 
38
- it 'defaults to the sandboxed Apple push gateway in test environment' do
38
+ it 'defaults to the localhost Apple push gateway in test environment' do
39
39
  Grocer.stubs(:env).returns('test')
40
- subject.gateway.should == 'gateway.sandbox.push.apple.com'
40
+ subject.gateway.should == '127.0.0.1'
41
41
  end
42
42
 
43
43
  it 'defaults to the sandboxed Apple push gateway for other random values' do
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+ require 'timeout'
3
+ require 'stringio'
4
+ require 'grocer/server'
5
+ require 'grocer/notification'
6
+
7
+ describe Grocer::Server do
8
+ let(:ssl_server) { stub_everything }
9
+ let(:mock_client) { StringIO.new }
10
+ subject { described_class.new(ssl_server) }
11
+
12
+ before do
13
+ ssl_server.stubs(:accept).yields(mock_client)
14
+ mock_client.stubs(:close)
15
+ end
16
+
17
+ after do
18
+ subject.close
19
+ end
20
+
21
+ it "accepts client connections and reads notifications into a queue" do
22
+ mock_client.write(Grocer::Notification.new(alert: "Hi!").to_bytes)
23
+ mock_client.rewind
24
+
25
+ subject.accept
26
+ Timeout.timeout(5) {
27
+ notification = subject.notifications.pop
28
+ notification.alert.should == "Hi!"
29
+ }
30
+ end
31
+
32
+ it "closes the socket" do
33
+ ssl_server.expects(:close).at_least(1)
34
+
35
+ subject.close
36
+ end
37
+ end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+ require 'grocer/ssl_server'
3
+
4
+ describe Grocer::SSLServer do
5
+ subject { described_class.new(options) }
6
+ let(:options) { { port: 12345 } }
7
+ let(:mock_server) { stub_everything }
8
+ let(:mock_ssl_server) { stub_everything }
9
+ let(:mock_client) { stub_everything }
10
+
11
+ before do
12
+ TCPServer.stubs(:new).returns(mock_server)
13
+ OpenSSL::SSL::SSLServer.stubs(:new).returns(mock_ssl_server)
14
+
15
+ mock_ssl_server.stubs(:accept).returns(mock_client).then.returns(nil)
16
+ end
17
+
18
+ it "is constructed with a port option" do
19
+ subject.port.should == 12345
20
+ end
21
+
22
+
23
+ describe "#accept" do
24
+ it "accepts client connections, yielding the client socket" do
25
+ clients = []
26
+ subject.accept { |c| clients << c }
27
+
28
+ clients.should == [mock_client]
29
+ end
30
+ end
31
+
32
+ describe "#close" do
33
+ it "closes the SSL socket" do
34
+ mock_ssl_server.expects(:close)
35
+
36
+ subject.accept # "open" socket
37
+ subject.close
38
+ end
39
+
40
+ it "is a no-op if the server has not been started" do
41
+ mock_server.expects(:close).never
42
+ mock_ssl_server.expects(:close).never
43
+
44
+ subject.close
45
+ end
46
+ end
47
+ end
data/spec/grocer_spec.rb CHANGED
@@ -59,6 +59,22 @@ describe Grocer do
59
59
  end
60
60
  end
61
61
 
62
+ describe '.server' do
63
+ let(:ssl_server) { stub_everything('SSLServer') }
64
+ before do
65
+ Grocer::SSLServer.stubs(:new).returns(ssl_server)
66
+ end
67
+
68
+ it 'gets Server' do
69
+ subject.server(connection_options).should be_a Grocer::Server
70
+ end
71
+
72
+ it 'passes the connection options on to the underlying SSLServer' do
73
+ subject.server(connection_options)
74
+ Grocer::SSLServer.should have_received(:new).with(connection_options)
75
+ end
76
+ end
77
+
62
78
  end
63
79
 
64
80
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: grocer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.6
4
+ version: 0.0.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -11,22 +11,22 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2012-04-04 00:00:00.000000000 Z
14
+ date: 2012-05-11 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: rspec
18
- requirement: &70100208337060 !ruby/object:Gem::Requirement
18
+ requirement: &70262264103600 !ruby/object:Gem::Requirement
19
19
  none: false
20
20
  requirements:
21
21
  - - ~>
22
22
  - !ruby/object:Gem::Version
23
- version: 2.9.0
23
+ version: 2.10.0
24
24
  type: :development
25
25
  prerelease: false
26
- version_requirements: *70100208337060
26
+ version_requirements: *70262264103600
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: pry
29
- requirement: &70100208503620 !ruby/object:Gem::Requirement
29
+ requirement: &70262264103100 !ruby/object:Gem::Requirement
30
30
  none: false
31
31
  requirements:
32
32
  - - ~>
@@ -34,10 +34,10 @@ dependencies:
34
34
  version: 0.9.8
35
35
  type: :development
36
36
  prerelease: false
37
- version_requirements: *70100208503620
37
+ version_requirements: *70262264103100
38
38
  - !ruby/object:Gem::Dependency
39
39
  name: mocha
40
- requirement: &70100208502540 !ruby/object:Gem::Requirement
40
+ requirement: &70262264102720 !ruby/object:Gem::Requirement
41
41
  none: false
42
42
  requirements:
43
43
  - - ! '>='
@@ -45,10 +45,10 @@ dependencies:
45
45
  version: '0'
46
46
  type: :development
47
47
  prerelease: false
48
- version_requirements: *70100208502540
48
+ version_requirements: *70262264102720
49
49
  - !ruby/object:Gem::Dependency
50
50
  name: bourne
51
- requirement: &70100208497180 !ruby/object:Gem::Requirement
51
+ requirement: &70262264102260 !ruby/object:Gem::Requirement
52
52
  none: false
53
53
  requirements:
54
54
  - - ! '>='
@@ -56,10 +56,10 @@ dependencies:
56
56
  version: '0'
57
57
  type: :development
58
58
  prerelease: false
59
- version_requirements: *70100208497180
59
+ version_requirements: *70262264102260
60
60
  - !ruby/object:Gem::Dependency
61
61
  name: rake
62
- requirement: &70100208581300 !ruby/object:Gem::Requirement
62
+ requirement: &70262264101840 !ruby/object:Gem::Requirement
63
63
  none: false
64
64
  requirements:
65
65
  - - ! '>='
@@ -67,7 +67,7 @@ dependencies:
67
67
  version: '0'
68
68
  type: :development
69
69
  prerelease: false
70
- version_requirements: *70100208581300
70
+ version_requirements: *70262264101840
71
71
  description: ! " Grocer interfaces with the Apple Push\n
72
72
  \ Notification Service to send push\n notifications
73
73
  to iOS devices and collect\n notification feedback via
@@ -96,14 +96,19 @@ files:
96
96
  - lib/grocer/feedback.rb
97
97
  - lib/grocer/feedback_connection.rb
98
98
  - lib/grocer/invalid_format_error.rb
99
- - lib/grocer/no_certificate_error.rb
100
99
  - lib/grocer/no_gateway_error.rb
101
100
  - lib/grocer/no_payload_error.rb
102
101
  - lib/grocer/no_port_error.rb
103
102
  - lib/grocer/notification.rb
103
+ - lib/grocer/notification_reader.rb
104
104
  - lib/grocer/push_connection.rb
105
105
  - lib/grocer/pusher.rb
106
+ - lib/grocer/server.rb
106
107
  - lib/grocer/ssl_connection.rb
108
+ - lib/grocer/ssl_server.rb
109
+ - lib/grocer/test/server.crt
110
+ - lib/grocer/test/server.csr
111
+ - lib/grocer/test/server.key
107
112
  - lib/grocer/version.rb
108
113
  - spec/fixtures/example.cer
109
114
  - spec/fixtures/example.key
@@ -112,10 +117,13 @@ files:
112
117
  - spec/grocer/failed_delivery_attempt_spec.rb
113
118
  - spec/grocer/feedback_connection_spec.rb
114
119
  - spec/grocer/feedback_spec.rb
120
+ - spec/grocer/notification_reader_spec.rb
115
121
  - spec/grocer/notification_spec.rb
116
122
  - spec/grocer/push_connection_spec.rb
117
123
  - spec/grocer/pusher_spec.rb
124
+ - spec/grocer/server_spec.rb
118
125
  - spec/grocer/ssl_connection_spec.rb
126
+ - spec/grocer/ssl_server_spec.rb
119
127
  - spec/grocer_spec.rb
120
128
  - spec/spec_helper.rb
121
129
  homepage: https://github.com/highgroove/grocer
@@ -132,7 +140,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
132
140
  version: '0'
133
141
  segments:
134
142
  - 0
135
- hash: -942820174361288389
143
+ hash: -259006450942006019
136
144
  required_rubygems_version: !ruby/object:Gem::Requirement
137
145
  none: false
138
146
  requirements:
@@ -141,7 +149,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
149
  version: '0'
142
150
  segments:
143
151
  - 0
144
- hash: -942820174361288389
152
+ hash: -259006450942006019
145
153
  requirements: []
146
154
  rubyforge_project:
147
155
  rubygems_version: 1.8.15
@@ -156,9 +164,12 @@ test_files:
156
164
  - spec/grocer/failed_delivery_attempt_spec.rb
157
165
  - spec/grocer/feedback_connection_spec.rb
158
166
  - spec/grocer/feedback_spec.rb
167
+ - spec/grocer/notification_reader_spec.rb
159
168
  - spec/grocer/notification_spec.rb
160
169
  - spec/grocer/push_connection_spec.rb
161
170
  - spec/grocer/pusher_spec.rb
171
+ - spec/grocer/server_spec.rb
162
172
  - spec/grocer/ssl_connection_spec.rb
173
+ - spec/grocer/ssl_server_spec.rb
163
174
  - spec/grocer_spec.rb
164
175
  - spec/spec_helper.rb
@@ -1,4 +0,0 @@
1
- module Grocer
2
- class NoCertificateError < StandardError
3
- end
4
- end