grocer 0.3.4 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +1 -0
- data/CHANGELOG.md +7 -0
- data/README.md +16 -7
- data/lib/grocer/error_response.rb +33 -0
- data/lib/grocer/newsstand_notification.rb +4 -4
- data/lib/grocer/notification.rb +43 -15
- data/lib/grocer/version.rb +1 -1
- data/lib/grocer.rb +6 -4
- data/spec/grocer/error_response_spec.rb +26 -0
- data/spec/grocer/notification_spec.rb +10 -0
- metadata +26 -37
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d758bbd299d221fe50d6a3b9d736dce0ae5016fb
|
4
|
+
data.tar.gz: f29ab31ea4fd92da51b624c84172578354e96246
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8fea57524b210a9c547f6efcb17f775210cc591e1d04a904828fa9ac4d388aa0d4b31bc2a74f8b025b9d48f29618ec59f6b0deb5ee3ee2a789d1b4e47c68dece
|
7
|
+
data.tar.gz: 642f3a3612148d4a721f4bcc554aa9168e89eb0adb5ffbd4a6b9fc35e14187c1143306577a49b223e642caa6be9d1833bbc1bfd355463d97dbc06cbf22ca06ee
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,12 @@
|
|
1
1
|
# CHANGELOG
|
2
2
|
|
3
|
+
## Unreleased
|
4
|
+
|
5
|
+
## 0.4.0
|
6
|
+
|
7
|
+
* Add support for `content-available` for background fetches (iOS 7) for all
|
8
|
+
types of Notifications ([Zack Parker](https://github.com/somezack))
|
9
|
+
|
3
10
|
## 0.3.4
|
4
11
|
|
5
12
|
* Add `Grocer::MobileDeviceManagementNotification` to send PushMagic tokens.
|
data/README.md
CHANGED
@@ -69,12 +69,13 @@ pusher = Grocer.pusher(
|
|
69
69
|
#
|
70
70
|
# Information on obtaining `device_token` is shown later.
|
71
71
|
notification = Grocer::Notification.new(
|
72
|
-
device_token:
|
73
|
-
alert:
|
74
|
-
badge:
|
75
|
-
sound:
|
76
|
-
expiry:
|
77
|
-
identifier:
|
72
|
+
device_token: "fe15a27d5df3c34778defb1f4f3880265cc52c0c047682223be59fb68500a9a2",
|
73
|
+
alert: "Hello from Grocer!",
|
74
|
+
badge: 42,
|
75
|
+
sound: "siren.aiff", # optional
|
76
|
+
expiry: Time.now + 60*60, # optional; 0 is default, meaning the message is not stored
|
77
|
+
identifier: 1234, # optional
|
78
|
+
content_available: true # optional; any truthy value will set 'content-available' to 1
|
78
79
|
)
|
79
80
|
|
80
81
|
pusher.push(notification)
|
@@ -143,7 +144,7 @@ feedback = Grocer.feedback(
|
|
143
144
|
certificate: "/path/to/cert.pem", # required
|
144
145
|
passphrase: "", # optional
|
145
146
|
gateway: "feedback.push.apple.com", # optional; See note below.
|
146
|
-
port: 2196
|
147
|
+
port: 2196, # optional
|
147
148
|
retries: 3 # optional
|
148
149
|
)
|
149
150
|
|
@@ -252,3 +253,11 @@ openssl pkcs12 -in exported_certificate.p12 -out certificate.pem -nodes -clcerts
|
|
252
253
|
```
|
253
254
|
|
254
255
|
The `certificate.pem` file that is generated can be used with **grocer**.
|
256
|
+
|
257
|
+
## Support Channels
|
258
|
+
|
259
|
+
[GitHub Issues](https://github.com/grocer/grocer/issues) and [Pull
|
260
|
+
Requests](https://github.com/grocer/grocer/pulls) are the primary venues for
|
261
|
+
communicating issues and discussing possible features. Several of us also
|
262
|
+
regularly hang out in the `#grocer` channel on Freenode; feel free to pop in
|
263
|
+
and ask questions there as well. Thanks! :heart:
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Grocer
|
2
|
+
class ErrorResponse
|
3
|
+
STATUS_CODE_DESCRIPTIONS = {
|
4
|
+
0 => 'No errors encountered',
|
5
|
+
1 => 'Processing error',
|
6
|
+
2 => 'Missing device token',
|
7
|
+
3 => 'Missing topic',
|
8
|
+
4 => 'Missing payload',
|
9
|
+
5 => 'Invalid token size',
|
10
|
+
6 => 'Invalid topic size',
|
11
|
+
7 => 'Invalid payload size',
|
12
|
+
8 => 'Invalid token',
|
13
|
+
256 => 'None (unknown)',
|
14
|
+
}
|
15
|
+
|
16
|
+
COMMAND = 8
|
17
|
+
|
18
|
+
attr_accessor :status_code, :identifier
|
19
|
+
|
20
|
+
def initialize(binary_tuple)
|
21
|
+
# C => 1 byte command
|
22
|
+
# C => 1 byte status
|
23
|
+
# N => 4 byte identifier
|
24
|
+
command, @status_code, @identifier = binary_tuple.unpack('CCN')
|
25
|
+
raise InvalidFormatError unless @status_code && @identifier
|
26
|
+
raise InvalidCommandError unless command == COMMAND
|
27
|
+
end
|
28
|
+
|
29
|
+
def status
|
30
|
+
STATUS_CODE_DESCRIPTIONS[status_code]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -11,15 +11,15 @@ module Grocer
|
|
11
11
|
# #=> { aps: { 'content-available' => 1 } }
|
12
12
|
class NewsstandNotification < Notification
|
13
13
|
|
14
|
+
def initialize(payload = {})
|
15
|
+
super(payload.merge(content_available: true))
|
16
|
+
end
|
17
|
+
|
14
18
|
private
|
15
19
|
|
16
20
|
def validate_payload
|
17
21
|
true
|
18
22
|
end
|
19
23
|
|
20
|
-
def payload_hash
|
21
|
-
{ aps: {'content-available' => 1} }
|
22
|
-
end
|
23
|
-
|
24
24
|
end
|
25
25
|
end
|
data/lib/grocer/notification.rb
CHANGED
@@ -4,19 +4,21 @@ module Grocer
|
|
4
4
|
# Public: An object used to send notifications to APNS.
|
5
5
|
class Notification
|
6
6
|
MAX_PAYLOAD_SIZE = 256
|
7
|
+
CONTENT_AVAILABLE_INDICATOR = 1
|
7
8
|
|
8
|
-
attr_accessor :identifier, :expiry, :device_token
|
9
|
-
|
9
|
+
attr_accessor :identifier, :expiry, :device_token
|
10
|
+
attr_reader :alert, :badge, :custom, :sound, :content_available
|
10
11
|
|
11
12
|
# Public: Initialize a new Grocer::Notification. You must specify at least an `alert` or `badge`.
|
12
13
|
#
|
13
14
|
# payload - The Hash of notification parameters and payload to be sent to APNS.:
|
14
|
-
# :device_token
|
15
|
-
# :alert
|
16
|
-
# :badge
|
17
|
-
# :sound
|
18
|
-
# :expiry
|
19
|
-
# :identifier
|
15
|
+
# :device_token - The String representing to device token sent to APNS.
|
16
|
+
# :alert - The String or Hash to be sent as the alert portion of the payload. (optional)
|
17
|
+
# :badge - The Integer to be sent as the badge portion of the payload. (optional)
|
18
|
+
# :sound - The String representing the sound portion of the payload. (optional)
|
19
|
+
# :expiry - The Integer representing UNIX epoch date sent to APNS as the notification expiry. (default: 0)
|
20
|
+
# :identifier - The arbitrary Integer sent to APNS to uniquely this notification. (default: 0)
|
21
|
+
# :content_available - The truthy or falsy value indicating the availability of new content for background fetch. (optional)
|
20
22
|
def initialize(payload = {})
|
21
23
|
@identifier = 0
|
22
24
|
|
@@ -27,7 +29,6 @@ module Grocer
|
|
27
29
|
|
28
30
|
def to_bytes
|
29
31
|
validate_payload
|
30
|
-
payload = encoded_payload
|
31
32
|
|
32
33
|
[
|
33
34
|
1,
|
@@ -35,24 +36,46 @@ module Grocer
|
|
35
36
|
expiry_epoch_time,
|
36
37
|
device_token_length,
|
37
38
|
sanitized_device_token,
|
38
|
-
|
39
|
-
|
39
|
+
encoded_payload.bytesize,
|
40
|
+
encoded_payload
|
40
41
|
].pack('CNNnH64nA*')
|
41
42
|
end
|
42
43
|
|
43
|
-
|
44
|
+
def alert=(alert)
|
45
|
+
@alert = alert
|
46
|
+
@encoded_payload = nil
|
47
|
+
end
|
44
48
|
|
45
|
-
def
|
46
|
-
|
49
|
+
def badge=(badge)
|
50
|
+
@badge = badge
|
51
|
+
@encoded_payload = nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def custom=(custom)
|
55
|
+
@custom = custom
|
56
|
+
@encoded_payload = nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def sound=(sound)
|
60
|
+
@sound = sound
|
61
|
+
@encoded_payload = nil
|
62
|
+
end
|
63
|
+
|
64
|
+
def content_available=(content_available)
|
65
|
+
@content_available = CONTENT_AVAILABLE_INDICATOR if content_available
|
66
|
+
@encoded_payload = nil
|
47
67
|
end
|
48
68
|
|
49
69
|
def validate_payload
|
50
70
|
fail NoPayloadError unless alert || badge || custom
|
51
71
|
fail PayloadTooLargeError if payload_too_large?
|
52
72
|
end
|
73
|
+
alias_method :valid?, :validate_payload
|
74
|
+
|
75
|
+
private
|
53
76
|
|
54
77
|
def encoded_payload
|
55
|
-
JSON.dump(payload_hash)
|
78
|
+
@encoded_payload ||= JSON.dump(payload_hash)
|
56
79
|
end
|
57
80
|
|
58
81
|
def payload_hash
|
@@ -60,10 +83,15 @@ module Grocer
|
|
60
83
|
aps_hash[:alert] = alert if alert
|
61
84
|
aps_hash[:badge] = badge if badge
|
62
85
|
aps_hash[:sound] = sound if sound
|
86
|
+
aps_hash[:'content-available'] = content_available if content_available
|
63
87
|
|
64
88
|
{ aps: aps_hash }.merge(custom || { })
|
65
89
|
end
|
66
90
|
|
91
|
+
def payload_too_large?
|
92
|
+
encoded_payload.bytesize > MAX_PAYLOAD_SIZE
|
93
|
+
end
|
94
|
+
|
67
95
|
def expiry_epoch_time
|
68
96
|
expiry.to_i
|
69
97
|
end
|
data/lib/grocer/version.rb
CHANGED
data/lib/grocer.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
|
+
require 'grocer/error_response'
|
1
2
|
require 'grocer/feedback'
|
3
|
+
require 'grocer/feedback_connection'
|
4
|
+
require 'grocer/mobile_device_management_notification'
|
5
|
+
require 'grocer/newsstand_notification'
|
2
6
|
require 'grocer/notification'
|
3
7
|
require 'grocer/passbook_notification'
|
4
|
-
require 'grocer/newsstand_notification'
|
5
|
-
require 'grocer/feedback_connection'
|
6
8
|
require 'grocer/push_connection'
|
7
9
|
require 'grocer/pusher'
|
8
|
-
require 'grocer/version'
|
9
10
|
require 'grocer/server'
|
11
|
+
require 'grocer/version'
|
10
12
|
|
11
13
|
module Grocer
|
12
14
|
Error = Class.new(::StandardError)
|
@@ -16,6 +18,7 @@ module Grocer
|
|
16
18
|
NoPortError = Class.new(Error)
|
17
19
|
PayloadTooLargeError = Class.new(Error)
|
18
20
|
CertificateExpiredError = Module.new
|
21
|
+
InvalidCommandError = Class.new(Error)
|
19
22
|
|
20
23
|
def self.env
|
21
24
|
ENV['RAILS_ENV'] || ENV['RACK_ENV'] || 'development'
|
@@ -35,5 +38,4 @@ module Grocer
|
|
35
38
|
ssl_server = SSLServer.new(options)
|
36
39
|
Server.new(ssl_server)
|
37
40
|
end
|
38
|
-
|
39
41
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require_relative '../../lib/grocer/error_response'
|
2
|
+
|
3
|
+
describe Grocer::ErrorResponse do
|
4
|
+
let(:status_code) { 1 }
|
5
|
+
let(:identifier) { 8342 }
|
6
|
+
let(:binary_tuple) { [described_class::COMMAND, status_code, identifier].pack('CCN') }
|
7
|
+
let(:invalid_binary_tuple) { 'totally not the right format' }
|
8
|
+
|
9
|
+
subject(:error_response) { described_class.new(binary_tuple) }
|
10
|
+
|
11
|
+
describe 'decoding' do
|
12
|
+
it 'accepts a binary tuple and sets each attribute' do
|
13
|
+
expect(error_response.status_code).to eq(status_code)
|
14
|
+
expect(error_response.identifier).to eq(identifier)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'raises an exception when there are problems decoding' do
|
18
|
+
-> { described_class.new(invalid_binary_tuple) }.should
|
19
|
+
raise_error(Grocer::InvalidFormatError)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'finds the status from the status code' do
|
24
|
+
expect(error_response.status).to eq('Processing error')
|
25
|
+
end
|
26
|
+
end
|
@@ -45,6 +45,16 @@ describe Grocer::Notification do
|
|
45
45
|
expect(bytes[43...45]).to eq([payload_bytes(notification).bytesize].pack('n'))
|
46
46
|
end
|
47
47
|
|
48
|
+
it 'encodes content-available as part of the payload if a truthy value is passed' do
|
49
|
+
notification.content_available = :foo
|
50
|
+
expect(payload[:aps][:'content-available']).to eq(1)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'does not encode content-available as part of the payload if a falsy value is passed' do
|
54
|
+
notification.content_available = false
|
55
|
+
expect(payload[:aps]).to_not have_key(:'content-available')
|
56
|
+
end
|
57
|
+
|
48
58
|
context 'missing payload' do
|
49
59
|
let(:payload_options) { Hash.new }
|
50
60
|
|
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: grocer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
5
|
-
prerelease:
|
4
|
+
version: 0.4.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Andy Lindeman
|
@@ -11,12 +10,11 @@ authors:
|
|
11
10
|
autorequire:
|
12
11
|
bindir: bin
|
13
12
|
cert_chain: []
|
14
|
-
date: 2013-
|
13
|
+
date: 2013-06-18 00:00:00.000000000 Z
|
15
14
|
dependencies:
|
16
15
|
- !ruby/object:Gem::Dependency
|
17
16
|
name: rspec
|
18
17
|
requirement: !ruby/object:Gem::Requirement
|
19
|
-
none: false
|
20
18
|
requirements:
|
21
19
|
- - ~>
|
22
20
|
- !ruby/object:Gem::Version
|
@@ -24,7 +22,6 @@ dependencies:
|
|
24
22
|
type: :development
|
25
23
|
prerelease: false
|
26
24
|
version_requirements: !ruby/object:Gem::Requirement
|
27
|
-
none: false
|
28
25
|
requirements:
|
29
26
|
- - ~>
|
30
27
|
- !ruby/object:Gem::Version
|
@@ -32,7 +29,6 @@ dependencies:
|
|
32
29
|
- !ruby/object:Gem::Dependency
|
33
30
|
name: pry
|
34
31
|
requirement: !ruby/object:Gem::Requirement
|
35
|
-
none: false
|
36
32
|
requirements:
|
37
33
|
- - ~>
|
38
34
|
- !ruby/object:Gem::Version
|
@@ -40,7 +36,6 @@ dependencies:
|
|
40
36
|
type: :development
|
41
37
|
prerelease: false
|
42
38
|
version_requirements: !ruby/object:Gem::Requirement
|
43
|
-
none: false
|
44
39
|
requirements:
|
45
40
|
- - ~>
|
46
41
|
- !ruby/object:Gem::Version
|
@@ -48,57 +43,55 @@ dependencies:
|
|
48
43
|
- !ruby/object:Gem::Dependency
|
49
44
|
name: mocha
|
50
45
|
requirement: !ruby/object:Gem::Requirement
|
51
|
-
none: false
|
52
46
|
requirements:
|
53
|
-
- -
|
47
|
+
- - '>='
|
54
48
|
- !ruby/object:Gem::Version
|
55
49
|
version: '0'
|
56
50
|
type: :development
|
57
51
|
prerelease: false
|
58
52
|
version_requirements: !ruby/object:Gem::Requirement
|
59
|
-
none: false
|
60
53
|
requirements:
|
61
|
-
- -
|
54
|
+
- - '>='
|
62
55
|
- !ruby/object:Gem::Version
|
63
56
|
version: '0'
|
64
57
|
- !ruby/object:Gem::Dependency
|
65
58
|
name: bourne
|
66
59
|
requirement: !ruby/object:Gem::Requirement
|
67
|
-
none: false
|
68
60
|
requirements:
|
69
|
-
- -
|
61
|
+
- - '>='
|
70
62
|
- !ruby/object:Gem::Version
|
71
63
|
version: '0'
|
72
64
|
type: :development
|
73
65
|
prerelease: false
|
74
66
|
version_requirements: !ruby/object:Gem::Requirement
|
75
|
-
none: false
|
76
67
|
requirements:
|
77
|
-
- -
|
68
|
+
- - '>='
|
78
69
|
- !ruby/object:Gem::Version
|
79
70
|
version: '0'
|
80
71
|
- !ruby/object:Gem::Dependency
|
81
72
|
name: rake
|
82
73
|
requirement: !ruby/object:Gem::Requirement
|
83
|
-
none: false
|
84
74
|
requirements:
|
85
|
-
- -
|
75
|
+
- - '>='
|
86
76
|
- !ruby/object:Gem::Version
|
87
77
|
version: '0'
|
88
78
|
type: :development
|
89
79
|
prerelease: false
|
90
80
|
version_requirements: !ruby/object:Gem::Requirement
|
91
|
-
none: false
|
92
81
|
requirements:
|
93
|
-
- -
|
82
|
+
- - '>='
|
94
83
|
- !ruby/object:Gem::Version
|
95
84
|
version: '0'
|
96
|
-
description:
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
85
|
+
description: |2
|
86
|
+
Grocer interfaces with the Apple Push
|
87
|
+
Notification Service to send push
|
88
|
+
notifications to iOS devices and collect
|
89
|
+
notification feedback via the Feedback
|
90
|
+
Service.
|
91
|
+
|
92
|
+
There are other gems out there to do this,
|
93
|
+
but Grocer plans to be the cleanest, most
|
94
|
+
extensible, and friendliest.
|
102
95
|
email:
|
103
96
|
- alindeman@gmail.com
|
104
97
|
- steveharman@gmail.com
|
@@ -118,6 +111,7 @@ files:
|
|
118
111
|
- grocer.gemspec
|
119
112
|
- lib/grocer.rb
|
120
113
|
- lib/grocer/connection.rb
|
114
|
+
- lib/grocer/error_response.rb
|
121
115
|
- lib/grocer/extensions/deep_symbolize_keys.rb
|
122
116
|
- lib/grocer/failed_delivery_attempt.rb
|
123
117
|
- lib/grocer/feedback.rb
|
@@ -140,6 +134,7 @@ files:
|
|
140
134
|
- spec/fixtures/example.key
|
141
135
|
- spec/fixtures/example.pem
|
142
136
|
- spec/grocer/connection_spec.rb
|
137
|
+
- spec/grocer/error_response_spec.rb
|
143
138
|
- spec/grocer/extensions/deep_symbolize_keys_spec.rb
|
144
139
|
- spec/grocer/failed_delivery_attempt_spec.rb
|
145
140
|
- spec/grocer/feedback_connection_spec.rb
|
@@ -161,39 +156,33 @@ files:
|
|
161
156
|
homepage: https://github.com/grocer/grocer
|
162
157
|
licenses:
|
163
158
|
- MIT
|
159
|
+
metadata: {}
|
164
160
|
post_install_message:
|
165
161
|
rdoc_options: []
|
166
162
|
require_paths:
|
167
163
|
- lib
|
168
164
|
required_ruby_version: !ruby/object:Gem::Requirement
|
169
|
-
none: false
|
170
165
|
requirements:
|
171
|
-
- -
|
166
|
+
- - '>='
|
172
167
|
- !ruby/object:Gem::Version
|
173
168
|
version: '0'
|
174
|
-
segments:
|
175
|
-
- 0
|
176
|
-
hash: -4490716643818473019
|
177
169
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
178
|
-
none: false
|
179
170
|
requirements:
|
180
|
-
- -
|
171
|
+
- - '>='
|
181
172
|
- !ruby/object:Gem::Version
|
182
173
|
version: '0'
|
183
|
-
segments:
|
184
|
-
- 0
|
185
|
-
hash: -4490716643818473019
|
186
174
|
requirements: []
|
187
175
|
rubyforge_project:
|
188
|
-
rubygems_version:
|
176
|
+
rubygems_version: 2.0.2
|
189
177
|
signing_key:
|
190
|
-
specification_version:
|
178
|
+
specification_version: 4
|
191
179
|
summary: Pushing Apple notifications since 2012.
|
192
180
|
test_files:
|
193
181
|
- spec/fixtures/example.cer
|
194
182
|
- spec/fixtures/example.key
|
195
183
|
- spec/fixtures/example.pem
|
196
184
|
- spec/grocer/connection_spec.rb
|
185
|
+
- spec/grocer/error_response_spec.rb
|
197
186
|
- spec/grocer/extensions/deep_symbolize_keys_spec.rb
|
198
187
|
- spec/grocer/failed_delivery_attempt_spec.rb
|
199
188
|
- spec/grocer/feedback_connection_spec.rb
|