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