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 CHANGED
@@ -3,5 +3,5 @@ rvm:
3
3
  - 1.9.2
4
4
  - 1.9.3
5
5
  - ruby-head
6
- - rbx-19mode
7
- # - jruby-19mode
6
+ - jruby-head
7
+ # - rbx-19mode # rubinius still incorrectly defaults to 'US-ASCII'
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 *or* Rubinius in 1.9 mode (JRuby in 1.9 is broken due to some
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
- result[(key.to_sym rescue key)] = value.is_a?(Hash) ?
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
@@ -18,7 +18,15 @@ module Grocer
18
18
  validate_payload
19
19
  payload = encoded_payload
20
20
 
21
- [1, identifier, expiry_epoch_time, device_token_length, sanitized_device_token, payload.length].pack('CNNnH64n') << payload
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
- cert_data = File.read(certificate)
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 = TCPSocket.new(gateway, port)
32
- @ssl = OpenSSL::SSL::SSLSocket.new(@sock, context)
33
- @ssl.sync = true
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
 
@@ -1,3 +1,3 @@
1
1
  module Grocer
2
- VERSION = '0.0.13'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -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
- subject[0].should == "\x01"
16
+ bytes[0].should == "\x01"
15
17
  end
16
18
 
17
19
  it 'defaults the identifer to 0' do
18
- subject[1...5].should == "\x00\x00\x00\x00"
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
- subject[1...5].should == [1234].pack('N')
25
+ bytes[1...5].should == [1234].pack('N')
24
26
  end
25
27
 
26
28
  it 'defaults expiry to zero' do
27
- subject[5...9].should == "\x00\x00\x00\x00"
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
- subject[5...9].should == [expiry.to_i].pack('N')
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
- subject[9...11].should == "\x00\x20"
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
- subject[11...43].should == ['fe15a27d5df3c34778defb1f4f3880265cc52c0c047682223be59fb68500a9a2'].pack('H*')
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
- subject[11...43].should == ['fe15a27d5df3c34778defb1f4f3880265cc52c0c047682223be59fb68500a9a2'].pack('H*')
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
- subject[43...45].should == [payload_from_bytes.length].pack('n')
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.13
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-06-20 00:00:00.000000000 Z
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: -2773558114449338672
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: -2773558114449338672
181
+ hash: 1241833556512094426
181
182
  requirements: []
182
183
  rubyforge_project:
183
184
  rubygems_version: 1.8.24