grocer 0.3.4 → 0.4.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.
- 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
|