grocer 0.4.1 → 0.5.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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +36 -3
- data/lib/grocer.rb +1 -0
- data/lib/grocer/notification.rb +5 -1
- data/lib/grocer/notification_reader.rb +4 -8
- data/lib/grocer/safari_notification.rb +85 -0
- data/lib/grocer/version.rb +1 -1
- data/spec/grocer/notification_spec.rb +12 -0
- data/spec/grocer/safari_notification_spec.rb +111 -0
- metadata +7 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 22fd48d5c9119fdaf4ebe30482265fd29b42dcda
|
4
|
+
data.tar.gz: e4d34ea7dd2625b96e17a1e373b6d7172b8752c6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 378b66ae89f8a80446f4b5c36a10894a0f4674e69f8ca041608a162e0030d9d44ec8daecd636119774150391e2c63c17f2dc08c91167899095e2aca5fcecf363
|
7
|
+
data.tar.gz: f2498ef365ec5e89fad9d57f79e1dd173fea1742bd6e3b106177383f03d221a3d43d0c915ada43cc11d1f42c67b3112628f60b584627e0c0becade524a57c606
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,12 @@
|
|
2
2
|
|
3
3
|
## Unreleased
|
4
4
|
|
5
|
+
## 0.5.0
|
6
|
+
|
7
|
+
* Add `Grocer::SafariNotification` for sending Safari notifications in OS X
|
8
|
+
Mavericks. ([Ben Ubois](https://github.com/benubois) and [Adam
|
9
|
+
Duke](adamvduke))
|
10
|
+
|
5
11
|
## 0.4.1
|
6
12
|
|
7
13
|
* Fix `Grocer::NotificationReader`, ensuring it sanitizes incoming `aps`
|
data/README.md
CHANGED
@@ -125,9 +125,10 @@ notification = Grocer::PassbookNotification.new(device_token: "...")
|
|
125
125
|
|
126
126
|
#### Newsstand Notifications
|
127
127
|
|
128
|
-
Grocer also supports the special Newsstand 'content-available' notification.
|
129
|
-
|
130
|
-
|
128
|
+
Grocer also supports the special Newsstand 'content-available' notification.
|
129
|
+
`Grocer::NewsstandNotification` can be used for this. Like
|
130
|
+
`Grocer::PassbookNotification`, it is a specialized kind of notification which
|
131
|
+
does not require any payload. Likewise, anything you add to it will be ignored.
|
131
132
|
|
132
133
|
```ruby
|
133
134
|
notification = Grocer::NewsstandNotification.new(device_token: "...")
|
@@ -135,6 +136,38 @@ notification = Grocer::NewsstandNotification.new(device_token: "...")
|
|
135
136
|
# {"aps": {"content-available":1}}
|
136
137
|
````
|
137
138
|
|
139
|
+
#### Safari Notifications
|
140
|
+
|
141
|
+
Grocer can be used for [Safari Push
|
142
|
+
Notifications](https://developer.apple.com/notifications/safari-push-notifications/)
|
143
|
+
introduced in Mavericks.
|
144
|
+
|
145
|
+
```ruby
|
146
|
+
notification = Grocer::SafariNotification.new(
|
147
|
+
device_token: '...', # required
|
148
|
+
title: 'Hello from Grocer', # required
|
149
|
+
body: 'Hi', # required
|
150
|
+
action: 'Read', # optional; the label of the action button
|
151
|
+
url_args: ['arg1'] # required (array); values that are paired with the placeholders inside the urlFormatString.
|
152
|
+
# Apple's documention lists url-args as optional, but in testing it was found to be required
|
153
|
+
)
|
154
|
+
```
|
155
|
+
|
156
|
+
Generates a JSON payload like:
|
157
|
+
|
158
|
+
```json
|
159
|
+
{
|
160
|
+
"aps": {
|
161
|
+
"alert": {
|
162
|
+
"title": "Hello from Grocer",
|
163
|
+
"body": "Hi",
|
164
|
+
"action": "Read"
|
165
|
+
},
|
166
|
+
"url-args": [ "arg1" ]
|
167
|
+
}
|
168
|
+
}
|
169
|
+
```
|
170
|
+
|
138
171
|
### Feedback
|
139
172
|
|
140
173
|
```ruby
|
data/lib/grocer.rb
CHANGED
@@ -5,6 +5,7 @@ require 'grocer/mobile_device_management_notification'
|
|
5
5
|
require 'grocer/newsstand_notification'
|
6
6
|
require 'grocer/notification'
|
7
7
|
require 'grocer/passbook_notification'
|
8
|
+
require 'grocer/safari_notification'
|
8
9
|
require 'grocer/push_connection'
|
9
10
|
require 'grocer/pusher'
|
10
11
|
require 'grocer/server'
|
data/lib/grocer/notification.rb
CHANGED
@@ -69,8 +69,12 @@ module Grocer
|
|
69
69
|
def validate_payload
|
70
70
|
fail NoPayloadError unless alert || badge || custom
|
71
71
|
fail PayloadTooLargeError if payload_too_large?
|
72
|
+
true
|
73
|
+
end
|
74
|
+
|
75
|
+
def valid?
|
76
|
+
validate_payload rescue false
|
72
77
|
end
|
73
|
-
alias_method :valid?, :validate_payload
|
74
78
|
|
75
79
|
private
|
76
80
|
|
@@ -39,14 +39,10 @@ module Grocer
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def sanitize_keys(hash)
|
42
|
-
hash
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
[new_key, v]
|
47
|
-
}
|
48
|
-
|
49
|
-
Hash[clean_hash]
|
42
|
+
hash.each_with_object({}) do |(k,v), h|
|
43
|
+
new_key = k.to_s.gsub(/-/,'_').to_sym
|
44
|
+
h[new_key] = v
|
45
|
+
end
|
50
46
|
end
|
51
47
|
end
|
52
48
|
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'grocer/notification'
|
2
|
+
|
3
|
+
module Grocer
|
4
|
+
# Public: A specialized form of a Grocer::Notification for sending
|
5
|
+
# Safari push notifications
|
6
|
+
#
|
7
|
+
# Examples
|
8
|
+
#
|
9
|
+
# Grocer::SafariNotification.new(
|
10
|
+
# device_token: '...',
|
11
|
+
# title: '...',
|
12
|
+
# body: '...',
|
13
|
+
# action: '...',
|
14
|
+
# url_args: ['...']
|
15
|
+
# )
|
16
|
+
#
|
17
|
+
# #=>
|
18
|
+
# {
|
19
|
+
# "aps": {
|
20
|
+
# "alert": {
|
21
|
+
# "title": "...",
|
22
|
+
# "body": "...",
|
23
|
+
# "action": "..."
|
24
|
+
# },
|
25
|
+
# "url-args": ["..."]
|
26
|
+
# }
|
27
|
+
# }
|
28
|
+
class SafariNotification < Notification
|
29
|
+
|
30
|
+
def initialize(payload = {})
|
31
|
+
self.alert = {}
|
32
|
+
super(payload)
|
33
|
+
end
|
34
|
+
|
35
|
+
def title
|
36
|
+
alert[:title]
|
37
|
+
end
|
38
|
+
|
39
|
+
def title=(title)
|
40
|
+
alert[:title] = title
|
41
|
+
@encoded_payload = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
def body
|
45
|
+
alert[:body]
|
46
|
+
end
|
47
|
+
|
48
|
+
def body=(body)
|
49
|
+
alert[:body] = body
|
50
|
+
@encoded_payload = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def action
|
54
|
+
alert[:action]
|
55
|
+
end
|
56
|
+
|
57
|
+
def action=(action)
|
58
|
+
alert[:action] = action
|
59
|
+
@encoded_payload = nil
|
60
|
+
end
|
61
|
+
|
62
|
+
def url_args
|
63
|
+
Array(@url_args)
|
64
|
+
end
|
65
|
+
|
66
|
+
def url_args=(args)
|
67
|
+
@url_args = args.dup
|
68
|
+
@encoded_payload = nil
|
69
|
+
end
|
70
|
+
|
71
|
+
private
|
72
|
+
|
73
|
+
def validate_payload
|
74
|
+
fail ArgumentError.new('missing title') unless title
|
75
|
+
fail ArgumentError.new('missing body') unless body
|
76
|
+
super
|
77
|
+
end
|
78
|
+
|
79
|
+
def payload_hash
|
80
|
+
aps_hash = { alert: alert }
|
81
|
+
aps_hash[:'url-args'] = url_args
|
82
|
+
{ aps: aps_hash }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/grocer/version.rb
CHANGED
@@ -55,6 +55,10 @@ describe Grocer::Notification do
|
|
55
55
|
expect(payload[:aps]).to_not have_key(:'content-available')
|
56
56
|
end
|
57
57
|
|
58
|
+
it "is valid" do
|
59
|
+
expect(notification.valid?).to be_true
|
60
|
+
end
|
61
|
+
|
58
62
|
context 'missing payload' do
|
59
63
|
let(:payload_options) { Hash.new }
|
60
64
|
|
@@ -62,6 +66,10 @@ describe Grocer::Notification do
|
|
62
66
|
-> { notification.to_bytes }.should raise_error(Grocer::NoPayloadError)
|
63
67
|
end
|
64
68
|
|
69
|
+
it 'is not valid' do
|
70
|
+
expect(notification.valid?).to be_false
|
71
|
+
end
|
72
|
+
|
65
73
|
[{alert: 'hi'}, {badge: 1}, {custom: {a: 'b'}}].each do |payload|
|
66
74
|
context "when #{payload.keys.first} exists, but not any other payload keys" do
|
67
75
|
let(:payload_options) { payload }
|
@@ -79,6 +87,10 @@ describe Grocer::Notification do
|
|
79
87
|
it 'raises an error when the size of the payload in bytes is too large' do
|
80
88
|
-> { notification.to_bytes }.should raise_error(Grocer::PayloadTooLargeError)
|
81
89
|
end
|
90
|
+
|
91
|
+
it 'is not valid' do
|
92
|
+
expect(notification.valid?).to be_false
|
93
|
+
end
|
82
94
|
end
|
83
95
|
end
|
84
96
|
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'grocer/safari_notification'
|
4
|
+
require 'grocer/shared_examples_for_notifications'
|
5
|
+
|
6
|
+
describe Grocer::SafariNotification do
|
7
|
+
describe 'binary format' do
|
8
|
+
let(:payload_options) {
|
9
|
+
{
|
10
|
+
title: 'Grocer Update!',
|
11
|
+
body: 'Grocer now supports Safari Notifications',
|
12
|
+
action: 'View',
|
13
|
+
url_args: ['message_id', '124234']
|
14
|
+
}
|
15
|
+
}
|
16
|
+
let(:payload) { payload_hash(notification) }
|
17
|
+
|
18
|
+
include_examples 'a notification'
|
19
|
+
|
20
|
+
it 'encodes title as part of the payload' do
|
21
|
+
notification.title = 'Hello World!'
|
22
|
+
expect(payload[:aps][:alert][:title]).to eq('Hello World!')
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'encodes body as part of the payload' do
|
26
|
+
notification.body = 'In the body'
|
27
|
+
expect(payload[:aps][:alert][:body]).to eq('In the body')
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'encodes action as part of the payload' do
|
31
|
+
notification.action = 'Launch'
|
32
|
+
expect(payload[:aps][:alert][:action]).to eq('Launch')
|
33
|
+
end
|
34
|
+
|
35
|
+
it 'encodes url-args payload attributes' do
|
36
|
+
notification.url_args = ['hello', 'world']
|
37
|
+
expect(payload[:aps][:'url-args']).to eq(['hello','world'])
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'is valid' do
|
41
|
+
expect(notification.valid?).to be_true
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'missing parameters' do
|
45
|
+
context 'title' do
|
46
|
+
let(:payload_options) { { alert: { body: 'This is a body' } } }
|
47
|
+
|
48
|
+
it 'raises an error when title is missing' do
|
49
|
+
-> { notification.to_bytes }.should raise_error(ArgumentError)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'is not valid' do
|
53
|
+
expect(notification.valid?).to be_false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'body' do
|
58
|
+
let(:payload_options) { { alert: { title: 'This is a title' } } }
|
59
|
+
|
60
|
+
it 'raises an error when body is missing' do
|
61
|
+
-> { notification.to_bytes }.should raise_error(ArgumentError)
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'is not valid' do
|
65
|
+
expect(notification.valid?).to be_false
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'oversized payload' do
|
71
|
+
let(:payload_options) { { alert: { title: 'Test', body: 'a' * (Grocer::Notification::MAX_PAYLOAD_SIZE + 1) } } }
|
72
|
+
|
73
|
+
it 'raises an error when the size of the payload in bytes is too large' do
|
74
|
+
-> { notification.to_bytes }.should raise_error(Grocer::PayloadTooLargeError)
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'is not valid' do
|
78
|
+
expect(notification.valid?).to be_false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
context 'with a complete alert hash passed to the initializer' do
|
83
|
+
let(:payload_options) {
|
84
|
+
{
|
85
|
+
alert: {
|
86
|
+
title: 'Title',
|
87
|
+
body: 'Body',
|
88
|
+
action: 'View'
|
89
|
+
},
|
90
|
+
url_args: ['hello', 'world']
|
91
|
+
}
|
92
|
+
}
|
93
|
+
|
94
|
+
it 'has the correct title' do
|
95
|
+
expect(notification.title).to eq('Title')
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'has the correct body' do
|
99
|
+
expect(notification.body).to eq('Body')
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'has the correct action' do
|
103
|
+
expect(notification.action).to eq('View')
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'has the correct url-args' do
|
107
|
+
expect(notification.url_args).to eq(['hello','world'])
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
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.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Andy Lindeman
|
@@ -10,7 +10,7 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2013-
|
13
|
+
date: 2013-11-08 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rspec
|
@@ -123,6 +123,7 @@ files:
|
|
123
123
|
- lib/grocer/passbook_notification.rb
|
124
124
|
- lib/grocer/push_connection.rb
|
125
125
|
- lib/grocer/pusher.rb
|
126
|
+
- lib/grocer/safari_notification.rb
|
126
127
|
- lib/grocer/server.rb
|
127
128
|
- lib/grocer/ssl_connection.rb
|
128
129
|
- lib/grocer/ssl_server.rb
|
@@ -146,6 +147,7 @@ files:
|
|
146
147
|
- spec/grocer/passbook_notification_spec.rb
|
147
148
|
- spec/grocer/push_connection_spec.rb
|
148
149
|
- spec/grocer/pusher_spec.rb
|
150
|
+
- spec/grocer/safari_notification_spec.rb
|
149
151
|
- spec/grocer/server_spec.rb
|
150
152
|
- spec/grocer/shared_examples_for_notifications.rb
|
151
153
|
- spec/grocer/ssl_connection_spec.rb
|
@@ -173,7 +175,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
173
175
|
version: '0'
|
174
176
|
requirements: []
|
175
177
|
rubyforge_project:
|
176
|
-
rubygems_version: 2.
|
178
|
+
rubygems_version: 2.1.10
|
177
179
|
signing_key:
|
178
180
|
specification_version: 4
|
179
181
|
summary: Pushing Apple notifications since 2012.
|
@@ -194,6 +196,7 @@ test_files:
|
|
194
196
|
- spec/grocer/passbook_notification_spec.rb
|
195
197
|
- spec/grocer/push_connection_spec.rb
|
196
198
|
- spec/grocer/pusher_spec.rb
|
199
|
+
- spec/grocer/safari_notification_spec.rb
|
197
200
|
- spec/grocer/server_spec.rb
|
198
201
|
- spec/grocer/shared_examples_for_notifications.rb
|
199
202
|
- spec/grocer/ssl_connection_spec.rb
|
@@ -201,3 +204,4 @@ test_files:
|
|
201
204
|
- spec/grocer_spec.rb
|
202
205
|
- spec/spec_helper.rb
|
203
206
|
- spec/support/notification_helpers.rb
|
207
|
+
has_rdoc:
|