grocer 0.0.13 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.travis.yml +2 -2
- data/CHANGELOG.md +6 -0
- data/README.md +4 -2
- data/grocer.gemspec +1 -0
- data/lib/grocer/extensions/deep_symbolize_keys.rb +4 -1
- data/lib/grocer/notification.rb +9 -1
- data/lib/grocer/ssl_connection.rb +13 -4
- data/lib/grocer/version.rb +1 -1
- data/spec/grocer/notification_spec.rb +22 -10
- data/spec/grocer/ssl_connection_spec.rb +15 -0
- metadata +6 -5
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,11 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## 0.1.0
|
4
|
+
|
5
|
+
* Enables socket keepalive option on APNS client sockets (Kyle Drake)
|
6
|
+
* Supports non-ASCII characters in notifications (Patrick Van Stee, Andy Lindeman)
|
7
|
+
* Certificate can be any object that responds to #read (Kyle Drake)
|
8
|
+
|
3
9
|
## 0.0.13
|
4
10
|
|
5
11
|
* Fixes a bug where closing a Grocer.server could result in an
|
data/README.md
CHANGED
@@ -13,8 +13,7 @@ cleanest, most extensible, and friendliest.
|
|
13
13
|
|
14
14
|
## Requirements
|
15
15
|
|
16
|
-
* Ruby/MRI 1.9.x
|
17
|
-
problem with `json` not being in the stdlib?)
|
16
|
+
* Ruby/MRI 1.9.x or JRuby 1.7.x in 1.9 mode
|
18
17
|
|
19
18
|
## Installation
|
20
19
|
|
@@ -45,6 +44,9 @@ pusher = Grocer.pusher(
|
|
45
44
|
|
46
45
|
#### Notes
|
47
46
|
|
47
|
+
* `certificate`: If you don't have the certificate stored in a file, you
|
48
|
+
can pass any object that responds to `read`.
|
49
|
+
Example: `certificate: StringIO.new(pem_string)`
|
48
50
|
* `gateway`: Defaults to different values depending on the `RAILS_ENV` or
|
49
51
|
`RACK_ENV` environment variables. If set to `production`, defaults to
|
50
52
|
`gateway.push.apple.com`, if set to `test`, defaults to `localhost` (see
|
data/grocer.gemspec
CHANGED
@@ -17,6 +17,7 @@ Gem::Specification.new do |gem|
|
|
17
17
|
extensible, and friendliest.
|
18
18
|
DESC
|
19
19
|
gem.homepage = 'https://github.com/highgroove/grocer'
|
20
|
+
gem.license = 'MIT'
|
20
21
|
|
21
22
|
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
23
|
gem.files = `git ls-files`.split("\n")
|
@@ -5,7 +5,10 @@ module Grocer
|
|
5
5
|
def deep_symbolize_keys
|
6
6
|
result = {}
|
7
7
|
each do |key, value|
|
8
|
-
|
8
|
+
# Workaround for JRuby defining Fixnum#to_sym even in 1.9 mode
|
9
|
+
symbolized_key = key.is_a?(Fixnum) ? key : (key.to_sym rescue key)
|
10
|
+
|
11
|
+
result[symbolized_key] = value.is_a?(Hash) ?
|
9
12
|
(value.extend DeepSymbolizeKeys).deep_symbolize_keys : value
|
10
13
|
end
|
11
14
|
result
|
data/lib/grocer/notification.rb
CHANGED
@@ -18,7 +18,15 @@ module Grocer
|
|
18
18
|
validate_payload
|
19
19
|
payload = encoded_payload
|
20
20
|
|
21
|
-
[
|
21
|
+
[
|
22
|
+
1,
|
23
|
+
identifier,
|
24
|
+
expiry_epoch_time,
|
25
|
+
device_token_length,
|
26
|
+
sanitized_device_token,
|
27
|
+
payload.bytesize,
|
28
|
+
payload
|
29
|
+
].pack('CNNnH64nA*')
|
22
30
|
end
|
23
31
|
|
24
32
|
private
|
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'socket'
|
2
2
|
require 'openssl'
|
3
3
|
require 'forwardable'
|
4
|
+
require 'stringio'
|
4
5
|
|
5
6
|
module Grocer
|
6
7
|
class SSLConnection
|
@@ -23,14 +24,22 @@ module Grocer
|
|
23
24
|
context = OpenSSL::SSL::SSLContext.new
|
24
25
|
|
25
26
|
if certificate
|
26
|
-
|
27
|
+
|
28
|
+
if certificate.respond_to?(:read)
|
29
|
+
cert_data = certificate.read
|
30
|
+
certificate.rewind if certificate.respond_to?(:rewind)
|
31
|
+
else
|
32
|
+
cert_data = File.read(certificate)
|
33
|
+
end
|
34
|
+
|
27
35
|
context.key = OpenSSL::PKey::RSA.new(cert_data, passphrase)
|
28
36
|
context.cert = OpenSSL::X509::Certificate.new(cert_data)
|
29
37
|
end
|
30
38
|
|
31
|
-
@sock
|
32
|
-
@
|
33
|
-
@ssl.
|
39
|
+
@sock = TCPSocket.new(gateway, port)
|
40
|
+
@sock.setsockopt Socket::SOL_SOCKET, Socket::SO_KEEPALIVE, true
|
41
|
+
@ssl = OpenSSL::SSL::SSLSocket.new(@sock, context)
|
42
|
+
@ssl.sync = true
|
34
43
|
@ssl.connect
|
35
44
|
end
|
36
45
|
|
data/lib/grocer/version.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
1
3
|
require 'spec_helper'
|
2
4
|
require 'grocer/notification'
|
3
5
|
|
@@ -8,47 +10,47 @@ describe Grocer::Notification do
|
|
8
10
|
let(:payload_dictionary_from_bytes) { JSON.parse(payload_from_bytes, symbolize_names: true) }
|
9
11
|
let(:payload_options) { { alert: 'hi', badge: 2, sound: 'siren.aiff' } }
|
10
12
|
|
11
|
-
subject { notification.to_bytes }
|
13
|
+
subject(:bytes) { notification.to_bytes }
|
12
14
|
|
13
15
|
it 'sets the command byte to 1' do
|
14
|
-
|
16
|
+
bytes[0].should == "\x01"
|
15
17
|
end
|
16
18
|
|
17
19
|
it 'defaults the identifer to 0' do
|
18
|
-
|
20
|
+
bytes[1...5].should == "\x00\x00\x00\x00"
|
19
21
|
end
|
20
22
|
|
21
23
|
it 'allows the identifier to be set' do
|
22
24
|
notification.identifier = 1234
|
23
|
-
|
25
|
+
bytes[1...5].should == [1234].pack('N')
|
24
26
|
end
|
25
27
|
|
26
28
|
it 'defaults expiry to zero' do
|
27
|
-
|
29
|
+
bytes[5...9].should == "\x00\x00\x00\x00"
|
28
30
|
end
|
29
31
|
|
30
32
|
it 'allows the expiry to be set' do
|
31
33
|
expiry = notification.expiry = Time.utc(2013, 3, 24, 12, 34, 56)
|
32
|
-
|
34
|
+
bytes[5...9].should == [expiry.to_i].pack('N')
|
33
35
|
end
|
34
36
|
|
35
37
|
it 'encodes the device token length as 32' do
|
36
|
-
|
38
|
+
bytes[9...11].should == "\x00\x20"
|
37
39
|
end
|
38
40
|
|
39
41
|
it 'encodes the device token as a 256-bit integer' do
|
40
42
|
token = notification.device_token = 'fe15a27d5df3c34778defb1f4f3880265cc52c0c047682223be59fb68500a9a2'
|
41
|
-
|
43
|
+
bytes[11...43].should == ['fe15a27d5df3c34778defb1f4f3880265cc52c0c047682223be59fb68500a9a2'].pack('H*')
|
42
44
|
end
|
43
45
|
|
44
46
|
it 'as a convenience, flattens the device token to remove spaces' do
|
45
47
|
token = notification.device_token = 'fe15 a27d 5df3c3 4778defb1f4f3880265cc52c0c047682223be59fb68500a9a2'
|
46
|
-
|
48
|
+
bytes[11...43].should == ['fe15a27d5df3c34778defb1f4f3880265cc52c0c047682223be59fb68500a9a2'].pack('H*')
|
47
49
|
end
|
48
50
|
|
49
51
|
it 'encodes the payload length' do
|
50
52
|
notification.alert = 'Hello World!'
|
51
|
-
|
53
|
+
bytes[43...45].should == [payload_from_bytes.bytesize].pack('n')
|
52
54
|
end
|
53
55
|
|
54
56
|
it 'encodes alert as part of the payload' do
|
@@ -71,6 +73,16 @@ describe Grocer::Notification do
|
|
71
73
|
payload_dictionary_from_bytes[:foo].should == 'bar'
|
72
74
|
end
|
73
75
|
|
76
|
+
it 'encodes UTF-8 characters' do
|
77
|
+
notification.alert = '私'
|
78
|
+
payload_dictionary_from_bytes[:aps][:alert].force_encoding("UTF-8").should == '私'
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'encodes the payload length correctly for multibyte UTF-8 strings' do
|
82
|
+
notification.alert = '私'
|
83
|
+
bytes[43...45].should == [payload_from_bytes.bytesize].pack('n')
|
84
|
+
end
|
85
|
+
|
74
86
|
context 'invalid payload' do
|
75
87
|
let(:payload_options) { Hash.new }
|
76
88
|
|
@@ -24,6 +24,21 @@ describe Grocer::SSLConnection do
|
|
24
24
|
}
|
25
25
|
}
|
26
26
|
|
27
|
+
describe 'configuration with pre-read certificate' do
|
28
|
+
before do
|
29
|
+
stub_certificate
|
30
|
+
end
|
31
|
+
|
32
|
+
subject {
|
33
|
+
string_io = File.read(connection_options[:certificate])
|
34
|
+
described_class.new(connection_options.merge(certificate: string_io))
|
35
|
+
}
|
36
|
+
|
37
|
+
it 'is initialized with a certificate IO' do
|
38
|
+
subject.certificate.should == File.read(connection_options[:certificate])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
27
42
|
subject { described_class.new(connection_options) }
|
28
43
|
|
29
44
|
describe 'configuration' do
|
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
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,7 +11,7 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date: 2012-
|
14
|
+
date: 2012-08-11 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: rspec
|
@@ -155,7 +155,8 @@ files:
|
|
155
155
|
- spec/grocer_spec.rb
|
156
156
|
- spec/spec_helper.rb
|
157
157
|
homepage: https://github.com/highgroove/grocer
|
158
|
-
licenses:
|
158
|
+
licenses:
|
159
|
+
- MIT
|
159
160
|
post_install_message:
|
160
161
|
rdoc_options: []
|
161
162
|
require_paths:
|
@@ -168,7 +169,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
168
169
|
version: '0'
|
169
170
|
segments:
|
170
171
|
- 0
|
171
|
-
hash:
|
172
|
+
hash: 1241833556512094426
|
172
173
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
173
174
|
none: false
|
174
175
|
requirements:
|
@@ -177,7 +178,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
177
178
|
version: '0'
|
178
179
|
segments:
|
179
180
|
- 0
|
180
|
-
hash:
|
181
|
+
hash: 1241833556512094426
|
181
182
|
requirements: []
|
182
183
|
rubyforge_project:
|
183
184
|
rubygems_version: 1.8.24
|