pushpad 1.0.0 → 1.2.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/.github/workflows/ci.yml +1 -1
- data/README.md +85 -18
- data/lib/pushpad/notification.rb +11 -0
- data/lib/pushpad/request.rb +4 -0
- data/lib/pushpad/subscription.rb +47 -31
- data/pushpad.gemspec +1 -1
- data/spec/pushpad/notification_spec.rb +32 -1
- data/spec/pushpad/subscription_spec.rb +116 -0
- metadata +7 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6ca523c6dd48111c5a369889092c80690401158c3f0a14142eb3aee236848062
|
4
|
+
data.tar.gz: 637c8461863686d6c3c1f9f666a7df74b2053aed5017318f360054f908b83fbe
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7aaed7d9218f698803bf1adda9ba1459365b49eaba897903b1cab109aeb9bb825ab40b25119a52cc620d515dd7cce36ad8509d34baacb7b74d58028c26327207
|
7
|
+
data.tar.gz: 0e89ea2619f3c6f39c4a073c95d56faa451e672b86ad3d898e681b53c7ecef59af7e2b7b5fd22b3be957ab02cb7fcc98b6c7e4a84b5a328e853f6c5abc8905ac
|
data/.github/workflows/ci.yml
CHANGED
data/README.md
CHANGED
@@ -3,13 +3,9 @@
|
|
3
3
|
[](https://badge.fury.io/rb/pushpad)
|
4
4
|

|
5
5
|
|
6
|
-
[Pushpad](https://pushpad.xyz) is a service for sending push notifications from
|
6
|
+
[Pushpad](https://pushpad.xyz) is a service for sending push notifications from websites and web apps. It uses the **Push API**, which is a standard supported by all major browsers (Chrome, Firefox, Opera, Edge, Safari).
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
- notifications are delivered even when the user is not on your website
|
11
|
-
- users don't need to install any app or plugin
|
12
|
-
- you can target specific users or send bulk notifications
|
8
|
+
The notifications are delivered in real time even when the users are not on your website and you can target specific users or send bulk notifications.
|
13
9
|
|
14
10
|
## Installation
|
15
11
|
|
@@ -55,17 +51,40 @@ Pushpad.signature_for current_user.id
|
|
55
51
|
|
56
52
|
```ruby
|
57
53
|
notification = Pushpad::Notification.new({
|
54
|
+
# required, the main content of the notification
|
58
55
|
body: "Hello world!",
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
56
|
+
|
57
|
+
# optional, the title of the notification (defaults to your project name)
|
58
|
+
title: "Website Name",
|
59
|
+
|
60
|
+
# optional, open this link on notification click (defaults to your project website)
|
61
|
+
target_url: "https://example.com",
|
62
|
+
|
63
|
+
# optional, the icon of the notification (defaults to the project icon)
|
64
|
+
icon_url: "https://example.com/assets/icon.png",
|
65
|
+
|
66
|
+
# optional, the small icon displayed in the status bar (defaults to the project badge)
|
67
|
+
badge_url: "https://example.com/assets/badge.png",
|
68
|
+
|
69
|
+
# optional, an image to display in the notification content
|
70
|
+
# see https://pushpad.xyz/docs/sending_images
|
71
|
+
image_url: "https://example.com/assets/image.png",
|
72
|
+
|
73
|
+
# optional, drop the notification after this number of seconds if a device is offline
|
74
|
+
ttl: 604800,
|
75
|
+
|
76
|
+
# optional, prevent Chrome on desktop from automatically closing the notification after a few seconds
|
77
|
+
require_interaction: true,
|
78
|
+
|
79
|
+
# optional, enable this option if you want a mute notification without any sound
|
80
|
+
silent: false,
|
81
|
+
|
82
|
+
# optional, enable this option only for time-sensitive alerts (e.g. incoming phone call)
|
83
|
+
urgent: false,
|
84
|
+
|
85
|
+
# optional, a string that is passed as an argument to action button callbacks
|
86
|
+
custom_data: "123",
|
87
|
+
|
69
88
|
# optional, add some action buttons to the notification
|
70
89
|
# see https://pushpad.xyz/docs/action_buttons
|
71
90
|
actions: [
|
@@ -76,10 +95,14 @@ notification = Pushpad::Notification.new({
|
|
76
95
|
action: "myActionName" # optional
|
77
96
|
}
|
78
97
|
],
|
79
|
-
|
98
|
+
|
99
|
+
# optional, bookmark the notification in the Pushpad dashboard (e.g. to highlight manual notifications)
|
100
|
+
starred: true,
|
101
|
+
|
80
102
|
# optional, use this option only if you need to create scheduled notifications (max 5 days)
|
81
103
|
# see https://pushpad.xyz/docs/schedule_notifications
|
82
104
|
send_at: Time.utc(2016, 7, 25, 10, 9),
|
105
|
+
|
83
106
|
# optional, add the notification to custom categories for stats aggregation
|
84
107
|
# see https://pushpad.xyz/docs/monitoring
|
85
108
|
custom_metrics: ['examples', 'another_metric'] # up to 3 metrics per notification
|
@@ -100,7 +123,8 @@ notification.deliver_to users, tags: ['events']
|
|
100
123
|
notification.broadcast tags: ['segment1', 'segment2']
|
101
124
|
|
102
125
|
# you can use boolean expressions
|
103
|
-
# they
|
126
|
+
# they can include parentheses and the operators !, &&, || (from highest to lowest precedence)
|
127
|
+
# https://pushpad.xyz/docs/tags
|
104
128
|
notification.broadcast tags: ['zip_code:28865 && !optout:local_events || friend_of:Organizer123']
|
105
129
|
notification.deliver_to users, tags: ['tag1 && tag2', 'tag3'] # equal to 'tag1 && tag2 || tag3'
|
106
130
|
|
@@ -177,6 +201,26 @@ to get the full list in multiple requests.
|
|
177
201
|
notifications = Pushpad::Notification.find_all(project_id: 5, page: 2)
|
178
202
|
```
|
179
203
|
|
204
|
+
## Scheduled notifications
|
205
|
+
|
206
|
+
You can create scheduled notifications that will be sent in the future:
|
207
|
+
|
208
|
+
```ruby
|
209
|
+
notification = Pushpad::Notification.new({
|
210
|
+
body: "This notification will be sent after 60 seconds",
|
211
|
+
send_at: Time.now.utc + 60
|
212
|
+
})
|
213
|
+
|
214
|
+
notification.broadcast
|
215
|
+
```
|
216
|
+
|
217
|
+
You can also cancel a scheduled notification:
|
218
|
+
|
219
|
+
```ruby
|
220
|
+
notification = Pushpad::Notification.find(5)
|
221
|
+
notification.cancel
|
222
|
+
```
|
223
|
+
|
180
224
|
## Getting subscription count
|
181
225
|
|
182
226
|
You can retrieve the number of subscriptions for a given project,
|
@@ -193,6 +237,29 @@ Pushpad::Subscription.count(project_id: 5, uids: ['user1'], tags: 'sports && tra
|
|
193
237
|
If `Pushpad.project_id` is defined, the `project_id` option can be
|
194
238
|
omitted.
|
195
239
|
|
240
|
+
## Getting push subscription data
|
241
|
+
|
242
|
+
You can retrieve the subscriptions for a given project,
|
243
|
+
optionally filtered by `tags` or `uids`:
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
Pushpad::Subscription.find_all(project_id: 5)
|
247
|
+
Pushpad::Subscription.find_all(project_id: 5, uids: ['user1'])
|
248
|
+
Pushpad::Subscription.find_all(project_id: 5, tags: ['sports'])
|
249
|
+
Pushpad::Subscription.find_all(project_id: 5, tags: 'sports && travel')
|
250
|
+
Pushpad::Subscription.find_all(project_id: 5, uids: ['user1'], tags: 'sports && travel')
|
251
|
+
```
|
252
|
+
|
253
|
+
If `Pushpad.project_id` is defined, the `project_id` option can be
|
254
|
+
omitted.
|
255
|
+
|
256
|
+
The REST API paginates the result set. You can pass a `page` parameter
|
257
|
+
to get the full list in multiple requests.
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
subscriptions = Pushpad::Subscription.find_all(project_id: 5, page: 2)
|
261
|
+
```
|
262
|
+
|
196
263
|
## License
|
197
264
|
|
198
265
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/lib/pushpad/notification.rb
CHANGED
@@ -8,6 +8,9 @@ module Pushpad
|
|
8
8
|
|
9
9
|
class FindError < RuntimeError
|
10
10
|
end
|
11
|
+
|
12
|
+
class CancelError < RuntimeError
|
13
|
+
end
|
11
14
|
|
12
15
|
class ReadonlyError < RuntimeError
|
13
16
|
end
|
@@ -87,6 +90,14 @@ module Pushpad
|
|
87
90
|
end
|
88
91
|
deliver req_body(uids, options[:tags]), options
|
89
92
|
end
|
93
|
+
|
94
|
+
def cancel
|
95
|
+
response = Request.delete("https://pushpad.xyz/api/v1/notifications/#{id}/cancel")
|
96
|
+
|
97
|
+
unless response.code == "204"
|
98
|
+
raise CancelError, "Response #{response.code} #{response.message}: #{response.body}"
|
99
|
+
end
|
100
|
+
end
|
90
101
|
|
91
102
|
private
|
92
103
|
|
data/lib/pushpad/request.rb
CHANGED
data/lib/pushpad/subscription.rb
CHANGED
@@ -2,51 +2,67 @@ module Pushpad
|
|
2
2
|
class Subscription
|
3
3
|
class CountError < RuntimeError
|
4
4
|
end
|
5
|
-
|
6
|
-
|
7
|
-
CountQuery.new(options).perform
|
5
|
+
|
6
|
+
class FindError < RuntimeError
|
8
7
|
end
|
8
|
+
|
9
|
+
attr_reader :id, :endpoint, :p256dh, :auth, :uid, :tags, :last_click_at, :created_at
|
9
10
|
|
10
|
-
|
11
|
-
|
11
|
+
def initialize(options)
|
12
|
+
@id = options[:id]
|
13
|
+
@endpoint = options[:endpoint]
|
14
|
+
@p256dh = options[:p256dh]
|
15
|
+
@auth = options[:auth]
|
16
|
+
@uid = options[:uid]
|
17
|
+
@tags = options[:tags]
|
18
|
+
@last_click_at = options[:last_click_at] && Time.parse(options[:last_click_at])
|
19
|
+
@created_at = options[:created_at] && Time.parse(options[:created_at])
|
20
|
+
end
|
12
21
|
|
13
|
-
|
14
|
-
|
15
|
-
|
22
|
+
def self.count(options = {})
|
23
|
+
project_id = options[:project_id] || Pushpad.project_id
|
24
|
+
raise "You must set project_id" unless project_id
|
16
25
|
|
17
|
-
|
18
|
-
|
19
|
-
raise "You must set project_id" unless project_id
|
26
|
+
endpoint = "https://pushpad.xyz/api/v1/projects/#{project_id}/subscriptions"
|
27
|
+
response = Request.head(endpoint, query_parameters: query_parameters(options))
|
20
28
|
|
21
|
-
|
22
|
-
|
29
|
+
unless response.code == "200"
|
30
|
+
raise CountError, "Response #{response.code} #{response.message}: #{response.body}"
|
31
|
+
end
|
23
32
|
|
24
|
-
|
25
|
-
|
26
|
-
|
33
|
+
response["X-Total-Count"].to_i
|
34
|
+
end
|
35
|
+
|
36
|
+
def self.find_all(options = {})
|
37
|
+
project_id = options[:project_id] || Pushpad.project_id
|
38
|
+
raise "You must set project_id" unless project_id
|
27
39
|
|
28
|
-
|
29
|
-
|
40
|
+
query_parameters_with_pagination = query_parameters(options)
|
41
|
+
query_parameters_with_pagination << ["page", options[:page]] if options.key?(:page)
|
30
42
|
|
31
|
-
|
43
|
+
response = Request.get("https://pushpad.xyz/api/v1/projects/#{project_id}/subscriptions",
|
44
|
+
query_parameters: query_parameters_with_pagination)
|
32
45
|
|
33
|
-
|
34
|
-
|
46
|
+
unless response.code == "200"
|
47
|
+
raise FindError, "Response #{response.code} #{response.message}: #{response.body}"
|
35
48
|
end
|
36
49
|
|
37
|
-
|
38
|
-
|
50
|
+
JSON.parse(response.body, symbolize_names: true).map do |attributes|
|
51
|
+
new(attributes)
|
39
52
|
end
|
53
|
+
end
|
40
54
|
|
41
|
-
|
42
|
-
tags = options.fetch(:tags, [])
|
55
|
+
private
|
43
56
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
57
|
+
def self.query_parameters(options)
|
58
|
+
uids = options.fetch(:uids, [])
|
59
|
+
uids_query = uids.map { |uid| ["uids[]", uid] }
|
60
|
+
|
61
|
+
tags = options.fetch(:tags, [])
|
62
|
+
tags_query = tags.is_a?(String) ? [["tags", tags]] : tags.map { |tag| ["tags[]", tag] }
|
63
|
+
|
64
|
+
[uids_query, tags_query].flatten(1)
|
50
65
|
end
|
66
|
+
|
51
67
|
end
|
52
68
|
end
|
data/pushpad.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |spec|
|
2
2
|
spec.name = "pushpad"
|
3
|
-
spec.version = '1.
|
3
|
+
spec.version = '1.2.0'
|
4
4
|
spec.authors = ["Pushpad"]
|
5
5
|
spec.email = ["support@pushpad.xyz"]
|
6
6
|
spec.summary = "Web push notifications for Chrome, Firefox, Opera, Edge and Safari using Pushpad."
|
@@ -28,7 +28,6 @@ module Pushpad
|
|
28
28
|
end
|
29
29
|
|
30
30
|
def stub_notification_post(project_id, params = {}, response_body = "{}")
|
31
|
-
|
32
31
|
stub_request(:post, "https://pushpad.xyz/api/v1/projects/#{project_id}/notifications").
|
33
32
|
with(body: hash_including(params)).
|
34
33
|
to_return(status: 201, body: response_body)
|
@@ -38,6 +37,16 @@ module Pushpad
|
|
38
37
|
stub_request(:post, "https://pushpad.xyz/api/v1/projects/#{project_id}/notifications").
|
39
38
|
to_return(status: 403)
|
40
39
|
end
|
40
|
+
|
41
|
+
def stub_notification_delete(notification_id)
|
42
|
+
stub_request(:delete, "https://pushpad.xyz/api/v1/notifications/#{notification_id}/cancel").
|
43
|
+
to_return(status: 204)
|
44
|
+
end
|
45
|
+
|
46
|
+
def stub_failing_notification_delete(notification_id)
|
47
|
+
stub_request(:delete, "https://pushpad.xyz/api/v1/notifications/#{notification_id}/cancel").
|
48
|
+
to_return(status: 404)
|
49
|
+
end
|
41
50
|
|
42
51
|
describe ".new" do
|
43
52
|
it "allows delivering notifications even if an id attribute is supplied" do
|
@@ -409,5 +418,27 @@ module Pushpad
|
|
409
418
|
end
|
410
419
|
end
|
411
420
|
end
|
421
|
+
|
422
|
+
describe "#cancel" do
|
423
|
+
it "cancels a scheduled notification" do
|
424
|
+
stub_notification_delete(5)
|
425
|
+
|
426
|
+
notification = Notification.new(id: 5)
|
427
|
+
|
428
|
+
res = notification.cancel
|
429
|
+
expect(res).to be_nil
|
430
|
+
end
|
431
|
+
|
432
|
+
it "fails with CancelError if response status code is not 204" do
|
433
|
+
stub_failing_notification_delete(5)
|
434
|
+
|
435
|
+
notification = Notification.new(id: 5)
|
436
|
+
|
437
|
+
expect {
|
438
|
+
notification.cancel
|
439
|
+
}.to raise_error(Notification::CancelError)
|
440
|
+
end
|
441
|
+
end
|
442
|
+
|
412
443
|
end
|
413
444
|
end
|
@@ -13,6 +13,17 @@ module Pushpad
|
|
13
13
|
stub_request(:head, "https://pushpad.xyz/api/v1/projects/#{options[:project_id]}/subscriptions").
|
14
14
|
to_return(status: 503)
|
15
15
|
end
|
16
|
+
|
17
|
+
def stub_subscriptions_get(options)
|
18
|
+
stub_request(:get, "https://pushpad.xyz/api/v1/projects/#{options[:project_id]}/subscriptions").
|
19
|
+
with(query: hash_including(options.fetch(:query, {}))).
|
20
|
+
to_return(status: 200, body: options[:list].to_json)
|
21
|
+
end
|
22
|
+
|
23
|
+
def stub_failing_subscriptions_get(options)
|
24
|
+
stub_request(:get, "https://pushpad.xyz/api/v1/projects/#{options[:project_id]}/subscriptions").
|
25
|
+
to_return(status: 403)
|
26
|
+
end
|
16
27
|
|
17
28
|
describe ".count" do
|
18
29
|
it "returns value from X-Total-Count header" do
|
@@ -81,5 +92,110 @@ module Pushpad
|
|
81
92
|
}.to raise_error(Subscription::CountError)
|
82
93
|
end
|
83
94
|
end
|
95
|
+
|
96
|
+
describe ".find_all" do
|
97
|
+
it "returns subscriptions of project with attributes from json response" do
|
98
|
+
attributes = {
|
99
|
+
id: 1169,
|
100
|
+
endpoint: "https://example.com/push/f7Q1Eyf7EyfAb1",
|
101
|
+
p256dh: "BCQVDTlYWdl05lal3lG5SKr3VxTrEWpZErbkxWrzknHrIKFwihDoZpc_2sH6Sh08h-CacUYI-H8gW4jH-uMYZQ4=",
|
102
|
+
auth: "cdKMlhgVeSPzCXZ3V7FtgQ==",
|
103
|
+
uid: "exampleUid",
|
104
|
+
tags: ["exampleTag1", "exampleTag2"],
|
105
|
+
last_click_at: "2023-11-03T10:30:00.000Z",
|
106
|
+
created_at: "2016-09-06T10:47:05.494Z"
|
107
|
+
}
|
108
|
+
stub_subscriptions_get(project_id: 10, list: [attributes])
|
109
|
+
|
110
|
+
subscriptions = Subscription.find_all(project_id: 10)
|
111
|
+
|
112
|
+
attributes.delete(:last_click_at)
|
113
|
+
attributes.delete(:created_at)
|
114
|
+
expect(subscriptions[0]).to have_attributes(attributes)
|
115
|
+
expect(subscriptions[0].last_click_at.utc.to_s).to eq(Time.utc(2023, 11, 3, 10, 30, 0.0).to_s)
|
116
|
+
expect(subscriptions[0].created_at.utc.to_s).to eq(Time.utc(2016, 9, 6, 10, 47, 5.494).to_s)
|
117
|
+
end
|
118
|
+
|
119
|
+
it "falls back to global project id" do
|
120
|
+
attributes = { id: 5 }
|
121
|
+
stub_subscriptions_get(project_id: 10, list: [attributes])
|
122
|
+
|
123
|
+
Pushpad.project_id = 10
|
124
|
+
subscriptions = Subscription.find_all
|
125
|
+
|
126
|
+
expect(subscriptions[0]).to have_attributes(attributes)
|
127
|
+
end
|
128
|
+
|
129
|
+
it "fails with helpful error message when project_id is missing" do
|
130
|
+
Pushpad.project_id = nil
|
131
|
+
|
132
|
+
expect {
|
133
|
+
Subscription.find_all
|
134
|
+
}.to raise_error(/must set project_id/)
|
135
|
+
end
|
136
|
+
|
137
|
+
it "allows passing page parameter for pagination" do
|
138
|
+
attributes = { id: 5 }
|
139
|
+
stub_subscriptions_get(project_id: 10, list: [attributes], query: { page: "3" })
|
140
|
+
|
141
|
+
subscriptions = Subscription.find_all(project_id: 10, page: 3)
|
142
|
+
|
143
|
+
expect(subscriptions[0]).to have_attributes(attributes)
|
144
|
+
end
|
145
|
+
|
146
|
+
it "fails with FindError if response status code is not 200" do
|
147
|
+
stub_failing_subscriptions_get(project_id: 10)
|
148
|
+
|
149
|
+
expect {
|
150
|
+
Subscription.find_all(project_id: 10)
|
151
|
+
}.to raise_error(Subscription::FindError)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "allows passing uids" do
|
155
|
+
attributes = { id: 5 }
|
156
|
+
request = stub_subscriptions_get(project_id: 5, list: [attributes], query: { uids: ["uid0", "uid1"] })
|
157
|
+
|
158
|
+
Subscription.find_all(project_id: 5, uids: ["uid0", "uid1"])
|
159
|
+
|
160
|
+
expect(request).to have_been_made.once
|
161
|
+
end
|
162
|
+
|
163
|
+
it "allows passing tags" do
|
164
|
+
attributes = { id: 5 }
|
165
|
+
request = stub_subscriptions_get(project_id: 5, list: [attributes], query: { tags: ["sports", "travel"] })
|
166
|
+
|
167
|
+
Subscription.find_all(project_id: 5, tags: ["sports", "travel"])
|
168
|
+
|
169
|
+
expect(request).to have_been_made.once
|
170
|
+
end
|
171
|
+
|
172
|
+
it "allows passing tags as boolean expression" do
|
173
|
+
attributes = { id: 5 }
|
174
|
+
request = stub_subscriptions_get(project_id: 5, list: [attributes], query: { tags: "sports || travel" })
|
175
|
+
|
176
|
+
Subscription.find_all(project_id: 5, tags: "sports || travel")
|
177
|
+
|
178
|
+
expect(request).to have_been_made.once
|
179
|
+
end
|
180
|
+
|
181
|
+
it "allows passing tags and uids" do
|
182
|
+
attributes = { id: 5 }
|
183
|
+
request = stub_subscriptions_get(project_id: 5, list: [attributes],
|
184
|
+
query: { tags: ["sports", "travel"], uids: ["uid0"] })
|
185
|
+
|
186
|
+
Subscription.find_all(project_id: 5, tags: ["sports", "travel"], uids: ["uid0"])
|
187
|
+
|
188
|
+
expect(request).to have_been_made.once
|
189
|
+
end
|
190
|
+
|
191
|
+
it "works properly when there are no results" do
|
192
|
+
stub_subscriptions_get(project_id: 5, list: [])
|
193
|
+
|
194
|
+
subscriptions = Subscription.find_all(project_id: 5)
|
195
|
+
|
196
|
+
expect(subscriptions).to eq([])
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
84
200
|
end
|
85
201
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pushpad
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pushpad
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2024-09-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -38,7 +38,7 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
-
description:
|
41
|
+
description:
|
42
42
|
email:
|
43
43
|
- support@pushpad.xyz
|
44
44
|
executables: []
|
@@ -66,7 +66,7 @@ homepage: https://pushpad.xyz
|
|
66
66
|
licenses:
|
67
67
|
- MIT
|
68
68
|
metadata: {}
|
69
|
-
post_install_message:
|
69
|
+
post_install_message:
|
70
70
|
rdoc_options: []
|
71
71
|
require_paths:
|
72
72
|
- lib
|
@@ -81,8 +81,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
81
|
- !ruby/object:Gem::Version
|
82
82
|
version: '0'
|
83
83
|
requirements: []
|
84
|
-
rubygems_version: 3.3.
|
85
|
-
signing_key:
|
84
|
+
rubygems_version: 3.0.3.1
|
85
|
+
signing_key:
|
86
86
|
specification_version: 4
|
87
87
|
summary: Web push notifications for Chrome, Firefox, Opera, Edge and Safari using
|
88
88
|
Pushpad.
|