grocer 0.0.13 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|