grocer 0.0.6 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/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