pushing 0.1.0 → 0.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 +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +16 -6
- data/Appraisals +12 -5
- data/Gemfile +1 -1
- data/README.md +130 -36
- data/certs/apns_auth_key_for_jwt_auth.p8.enc +0 -0
- data/certs/apns_example_production.pem.enc +0 -0
- data/gemfiles/rails_42.gemfile +1 -1
- data/gemfiles/rails_50.gemfile +1 -1
- data/gemfiles/rails_51.gemfile +1 -1
- data/gemfiles/rails_52.gemfile +17 -0
- data/gemfiles/rails_edge.gemfile +1 -1
- data/lib/generators/pushing/templates/initializer.rb +53 -8
- data/lib/pushing/adapters/apn/apnotic_adapter.rb +22 -11
- data/lib/pushing/adapters/apn/houston_adapter.rb +3 -4
- data/lib/pushing/adapters/apn/lowdown_adapter.rb +4 -6
- data/lib/pushing/base.rb +8 -3
- data/lib/pushing/platforms.rb +26 -6
- data/lib/pushing/railtie.rb +25 -0
- data/lib/pushing/version.rb +1 -1
- data/pushing.gemspec +1 -3
- metadata +7 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: e6d63cb3c86db8e3be6c3335fb26e7e028ba5b25eb9ad13aa4b8febbf82d432c
|
4
|
+
data.tar.gz: 82d5cf8214b81355a1dbc1db23843525e492889af24f0750874d1ec626830767
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2f6c64a1574f61b456656018eb94fe6c73f3638745c8c26420f22098ef0de9188511e317a7f070ad2b683c58785650634fb03e2db9ccc6278f4e1280d7729e46
|
7
|
+
data.tar.gz: 5c31ab15a5ec3f68622167ea05fe89eaf9b43c563d65f02d903f3793e3a5c22674d90af97d631dfe79df086524dd2f85f873298bff5136443358dc6944687e14
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -4,25 +4,35 @@ cache: bundler
|
|
4
4
|
sudo: false
|
5
5
|
|
6
6
|
before_install:
|
7
|
-
|
7
|
+
- gem update --system
|
8
|
+
- gem pristine bundler
|
9
|
+
- openssl aes-256-cbc -K $encrypted_86ed69f44076_key -iv $encrypted_86ed69f44076_iv -in ./certs/apns_auth_key_for_jwt_auth.p8.enc -out certs/apns_auth_key_for_jwt_auth.p8 -d
|
10
|
+
- openssl aes-256-cbc -K $encrypted_86ed69f44076_key -iv $encrypted_86ed69f44076_iv -in ./certs/apns_example_production.pem.enc -out certs/apns_example_production.pem -d
|
8
11
|
|
9
12
|
rvm:
|
10
|
-
- 2.2.
|
11
|
-
- 2.3.
|
12
|
-
- 2.4.
|
13
|
+
- 2.2.9
|
14
|
+
- 2.3.6
|
15
|
+
- 2.4.3
|
16
|
+
- 2.5.0
|
13
17
|
- ruby-head
|
14
|
-
- jruby-9.1.
|
18
|
+
- jruby-9.1.16.0
|
15
19
|
- jruby-head
|
16
20
|
|
17
21
|
gemfile:
|
18
22
|
- gemfiles/rails_42.gemfile
|
19
23
|
- gemfiles/rails_50.gemfile
|
20
24
|
- gemfiles/rails_51.gemfile
|
25
|
+
- gemfiles/rails_52.gemfile
|
21
26
|
- gemfiles/rails_edge.gemfile
|
22
27
|
|
23
28
|
matrix:
|
24
29
|
allow_failures:
|
25
30
|
- rvm: ruby-head
|
26
|
-
- rvm: jruby-9.1.
|
31
|
+
- rvm: jruby-9.1.16.0
|
27
32
|
- rvm: jruby-head
|
28
33
|
- gemfile: gemfiles/rails_edge.gemfile
|
34
|
+
exclude:
|
35
|
+
- rvm: 2.3.6
|
36
|
+
gemfile: gemfiles/rails_edge.gemfile
|
37
|
+
- rvm: 2.2.9
|
38
|
+
gemfile: gemfiles/rails_edge.gemfile
|
data/Appraisals
CHANGED
@@ -7,11 +7,11 @@ appraise "rails_edge" do
|
|
7
7
|
end
|
8
8
|
end
|
9
9
|
|
10
|
-
appraise "
|
11
|
-
gem "railties", '~> 5.0.
|
12
|
-
gem "actionpack", '~> 5.0.
|
13
|
-
gem "actionview", '~> 5.0.
|
14
|
-
gem "activejob", '~> 5.0.
|
10
|
+
appraise "rails_52" do
|
11
|
+
gem "railties", '~> 5.2.0.rc1'
|
12
|
+
gem "actionpack", '~> 5.2.0.rc1'
|
13
|
+
gem "actionview", '~> 5.2.0.rc1'
|
14
|
+
gem "activejob", '~> 5.2.0.rc1'
|
15
15
|
end
|
16
16
|
|
17
17
|
appraise "rails_51" do
|
@@ -21,6 +21,13 @@ appraise "rails_51" do
|
|
21
21
|
gem "activejob", '~> 5.1.0'
|
22
22
|
end
|
23
23
|
|
24
|
+
appraise "rails_50" do
|
25
|
+
gem "railties", '~> 5.0.0'
|
26
|
+
gem "actionpack", '~> 5.0.0'
|
27
|
+
gem "actionview", '~> 5.0.0'
|
28
|
+
gem "activejob", '~> 5.0.0'
|
29
|
+
end
|
30
|
+
|
24
31
|
appraise "rails_42" do
|
25
32
|
gem "rails", '~> 4.2.0'
|
26
33
|
gem "actionpack", '~> 4.2.0'
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
# Pushing [](https://travis-ci.org/yuki24/pushing)
|
1
|
+
# Pushing: ActionMailer for Push Notifications [](https://travis-ci.org/yuki24/pushing)
|
2
2
|
|
3
|
-
Pushing is a push notification framework that implements interfaces similar to ActionMailer
|
3
|
+
Pushing is a push notification framework that implements interfaces similar to ActionMailer.
|
4
4
|
|
5
|
-
* **Convention over Configuration**: Pushing brings Convention over Configuration to your app
|
5
|
+
* **Convention over Configuration**: Pushing brings Convention over Configuration to your app for organizing your push notification implementations.
|
6
6
|
* **Extremely Easy to Learn**: If you know how to use ActionMailer, you already know how to use Pushing. Send notifications asynchronously with ActiveJob at no learning cost.
|
7
7
|
* **Testability**: First-class support for push notification. No more hassle writing custom code or stubs/mocks for your tests.
|
8
8
|
|
@@ -11,44 +11,40 @@ Pushing is a push notification framework that implements interfaces similar to A
|
|
11
11
|
Add this line to your application's Gemfile:
|
12
12
|
|
13
13
|
```ruby
|
14
|
-
gem 'pushing'
|
15
|
-
gem 'jbuilder'
|
14
|
+
gem 'pushing'
|
15
|
+
gem 'jbuilder' # if you don't have it in your Gemfile
|
16
16
|
```
|
17
17
|
|
18
|
-
|
18
|
+
At the time of writing, Pushing only has support for [jbuilder](https://github.com/rails/jbuilder) (Rails' default JSON constructor), but there are plans to add support for [jb](https://github.com/amatsuda/jb) and [rabl](https://github.com/nesquena/rabl).
|
19
19
|
|
20
|
-
### Supported
|
20
|
+
### Supported Platforms
|
21
21
|
|
22
|
-
Pushing itself doesn't make HTTP requests. Instead, it uses an adapter
|
22
|
+
Pushing itself doesn't make HTTP requests. Instead, it uses an adapter to make actual calls. Currently, Pushing has support for the following client gems:
|
23
23
|
|
24
|
-
* [APNs](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1):
|
24
|
+
* [APNs](https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/APNSOverview.html#//apple_ref/doc/uid/TP40008194-CH8-SW1) (iOS):
|
25
25
|
* [anpotic](https://github.com/ostinelli/apnotic) (recommended)
|
26
26
|
* [lowdown](https://github.com/alloy/lowdown)
|
27
27
|
* [houston](https://github.com/nomad/houston)
|
28
28
|
|
29
|
-
* [FCM](https://firebase.google.com/docs/cloud-messaging/):
|
29
|
+
* [FCM](https://firebase.google.com/docs/cloud-messaging/) (Android):
|
30
30
|
* [andpush](https://github.com/yuki24/andpush) (recommended)
|
31
31
|
* [fcm](https://github.com/spacialdb/fcm)
|
32
32
|
|
33
33
|
If you are starting from scratch, it is recommended using [anpotic](https://github.com/ostinelli/apnotic) for APNs and [andpush](https://github.com/yuki24/andpush) for FCM due to their reliability and performance:
|
34
34
|
|
35
35
|
```ruby
|
36
|
-
gem 'apnotic' # APNs
|
37
|
-
gem 'andpush' # FCM
|
36
|
+
gem 'apnotic' # APNs
|
37
|
+
gem 'andpush' # FCM
|
38
38
|
```
|
39
39
|
|
40
40
|
### Walkthrough to Writing a Notifier
|
41
41
|
|
42
|
-
|
42
|
+
#### Generate a new notifier:
|
43
43
|
|
44
44
|
```sh
|
45
45
|
$ rails g pushing:notifier TweetNotifier new_direct_message
|
46
46
|
```
|
47
47
|
|
48
|
-
#### Edit the Notifier
|
49
|
-
|
50
|
-
Let's say there are `direct_messages` and `device_tokens` tables where we store actual messages and device tokens (a.k.a registration ids in FCM) given by APNs or FCM.
|
51
|
-
|
52
48
|
```ruby
|
53
49
|
# app/notifiers/tweet_notifier.rb
|
54
50
|
class TweetNotifier < ApplicationNotifier
|
@@ -61,18 +57,7 @@ class TweetNotifier < ApplicationNotifier
|
|
61
57
|
end
|
62
58
|
```
|
63
59
|
|
64
|
-
|
65
|
-
|
66
|
-
```ruby
|
67
|
-
# only sends a push notification to FCM
|
68
|
-
push apn: false, fcm: @token.registration_id
|
69
|
-
```
|
70
|
-
|
71
|
-
will only send a notification to the FCM service.
|
72
|
-
|
73
|
-
#### Edit the Push Notification Payload
|
74
|
-
|
75
|
-
Next, let's modify the templates to generate JSON that contains message data. Like controllers, you can use all the instance variables initialized in the action.
|
60
|
+
#### Edit the push notification payload:
|
76
61
|
|
77
62
|
APNs:
|
78
63
|
|
@@ -104,9 +89,7 @@ json.notification do
|
|
104
89
|
end
|
105
90
|
```
|
106
91
|
|
107
|
-
### Deliver the
|
108
|
-
|
109
|
-
Finally, send a push notification to the user. You can call the `#deliver_now!` method to immediately send a notification, or the `#deliver_later!` method if you have ActiveJob set up.
|
92
|
+
### Deliver the push notifications:
|
110
93
|
|
111
94
|
```ruby
|
112
95
|
TweetNotifier.new_direct_message(message_id, device_token.id).deliver_now!
|
@@ -116,6 +99,57 @@ TweetNotifier.new_direct_message(message_id, device_token.id).deliver_later!
|
|
116
99
|
# => enqueues a job that sends a push notification later
|
117
100
|
```
|
118
101
|
|
102
|
+
## Advanced Usage
|
103
|
+
|
104
|
+
### Pushing only to one platform
|
105
|
+
|
106
|
+
Pushing only sends a notification for the platforms that are given a truthy value. For example, give the following code:
|
107
|
+
|
108
|
+
```ruby
|
109
|
+
push apn: @token.device_token, fcm: false
|
110
|
+
# => only sends a push notification to APNs
|
111
|
+
|
112
|
+
push apn: @token.device_token
|
113
|
+
# => same as above but without the `:fcm` key, only sends a push notification to APNs
|
114
|
+
```
|
115
|
+
|
116
|
+
This will only send a push notification to APNs and skip the call to FCM.
|
117
|
+
|
118
|
+
### APNs
|
119
|
+
|
120
|
+
It is often necessary to switch the environment endpoint or adjust the request headers depending on the notification you want to send. Pushing's `#push` method allows for overriding APNs request headers on a delivery-basis:
|
121
|
+
|
122
|
+
#### Overriding the default environment:
|
123
|
+
|
124
|
+
```ruby
|
125
|
+
push apn: { device_token: @token.device_token, environment: @token.apn_environment }
|
126
|
+
```
|
127
|
+
|
128
|
+
#### Overriding the default APN topic:
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
push apn: { device_token: @token.device_token, headers: { apns_topic: 'your.otherapp.ios' } }
|
132
|
+
```
|
133
|
+
|
134
|
+
#### Or all of the above:
|
135
|
+
|
136
|
+
```ruby
|
137
|
+
push fcm: @token.fcm?,
|
138
|
+
apn: {
|
139
|
+
device_token: @token.apn? && @token.device_token,
|
140
|
+
environment: @token.apn_environment,
|
141
|
+
headers: {
|
142
|
+
apns_id: uuid,
|
143
|
+
apns_expiration: 7.days.from_now,
|
144
|
+
apns_priority: 5,
|
145
|
+
apns_topic: 'your.otherapp.ios',
|
146
|
+
apns_collapse_id: 'not-so-important-notification'
|
147
|
+
}
|
148
|
+
}
|
149
|
+
```
|
150
|
+
|
151
|
+
The `:fcm` key, on the other hand, doesn't have any options as everything's configurable through the request body.
|
152
|
+
|
119
153
|
## Error Handling
|
120
154
|
|
121
155
|
Like ActionMailer, you can use the `rescue_from` hook to handle exceptions. A common use-case would be to handle a **'BadDeviceToken'** response from APNs or a response with a **'Retry-After'** header from FCM.
|
@@ -129,8 +163,9 @@ class ApplicationNotifier < Pushing::Base
|
|
129
163
|
|
130
164
|
if response.status == 410 || (response.status == 400 && response.json[:reason] == 'BadDeviceToken')
|
131
165
|
token = error.notification.device_token
|
166
|
+
Rails.logger.info("APN device token #{token} has been expired and will be removed.")
|
132
167
|
|
133
|
-
# delete device token accordingly
|
168
|
+
# delete or expire device token accordingly
|
134
169
|
else
|
135
170
|
raise # Make sure to raise any other types of error to re-enqueue the job
|
136
171
|
end
|
@@ -180,11 +215,70 @@ end
|
|
180
215
|
|
181
216
|
## Configuration
|
182
217
|
|
183
|
-
TODO
|
218
|
+
##### TODO: Make this section more helpful
|
219
|
+
|
220
|
+
```ruby
|
221
|
+
Pushing::Platforms.configure do |config|
|
222
|
+
# Adapter that is used to send push notifications through FCM
|
223
|
+
config.fcm.adapter = Rails.env.test? ? :test : :andpush
|
224
|
+
|
225
|
+
# Your FCM servery key that can be found here: https://console.firebase.google.com/project/_/settings/cloudmessaging
|
226
|
+
config.fcm.server_key = 'YOUR_FCM_SERVER_KEY'
|
227
|
+
|
228
|
+
# Adapter that is used to send push notifications through APNs
|
229
|
+
config.apn.adapter = Rails.env.test? ? :test : :apnotic
|
230
|
+
|
231
|
+
# The environment that is used by default to send push notifications through APNs
|
232
|
+
config.apn.environment = Rails.env.production? ? :production : :development
|
233
|
+
|
234
|
+
# The scheme that is used for negotiating connection trust between your provider
|
235
|
+
# servers and Apple Push Notification service. As documented in the offitial doc,
|
236
|
+
# there are two schemes available:
|
237
|
+
#
|
238
|
+
# :token - Token-based provider connection trust (default)
|
239
|
+
# :certificate - Certificate-based provider connection trust
|
240
|
+
#
|
241
|
+
# This option is only applied when using an adapter that uses the HTTP/2-based
|
242
|
+
# API.
|
243
|
+
config.apn.connection_scheme = :token
|
244
|
+
|
245
|
+
# Path to the certificate or auth key for establishing a connection to APNs.
|
246
|
+
#
|
247
|
+
# This config is always required.
|
248
|
+
config.apn.certificate_path = 'path/to/your/certificate'
|
249
|
+
|
250
|
+
# Password for the certificate specified above if there's any.
|
251
|
+
# config.apn.certificate_password = 'passphrase'
|
252
|
+
|
253
|
+
# A 10-character key identifier (kid) key, obtained from your developer account.
|
254
|
+
# If you haven't created an Auth Key for your app, create a new one at:
|
255
|
+
# https://developer.apple.com/account/ios/authkey/
|
256
|
+
#
|
257
|
+
# Required if the +connection_scheme+ is set to +:token+.
|
258
|
+
config.apn.key_id = 'DEF123GHIJ'
|
259
|
+
|
260
|
+
# The issuer (iss) registered claim key, whose value is your 10-character Team ID,
|
261
|
+
# obtained from your developer account. Your team id could be found at:
|
262
|
+
# https://developer.apple.com/account/#/membership
|
263
|
+
#
|
264
|
+
# Required if the +connection_scheme+ is set to +:token+.
|
265
|
+
config.apn.team_id = 'ABC123DEFG'
|
266
|
+
|
267
|
+
# Header values that are added to every request to APNs. documentation for the
|
268
|
+
# headers available can be found here:
|
269
|
+
# https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html#//apple_ref/doc/uid/TP40008194-CH11-SW13
|
270
|
+
config.apn.default_headers = {
|
271
|
+
apns_priority: 10,
|
272
|
+
apns_topic: 'your.awesomeapp.ios',
|
273
|
+
apns_collapse_id: 'wrong.topicname.com'
|
274
|
+
}
|
275
|
+
end
|
276
|
+
|
277
|
+
```
|
184
278
|
|
185
279
|
## Testing
|
186
280
|
|
187
|
-
Pushing provides first-class support for testing. In
|
281
|
+
Pushing provides first-class support for testing. In order to test your notifier, use the `:test` adapter in the test environment instead of an actual adapter in development/production.
|
188
282
|
|
189
283
|
```ruby
|
190
284
|
# config/initializers/pushing.rb
|
@@ -194,7 +288,7 @@ Pushing::Platforms.configure do |config|
|
|
194
288
|
end
|
195
289
|
```
|
196
290
|
|
197
|
-
Now you can
|
291
|
+
Now you can call the `#deliveries` method on the notifier. Here is an example with [ActiveSupport::TestCase](http://api.rubyonrails.org/classes/ActiveSupport/TestCase.html):
|
198
292
|
|
199
293
|
```ruby
|
200
294
|
TweetNotifier.deliveries.clear # => clears the test inbox
|
Binary file
|
Binary file
|
data/gemfiles/rails_42.gemfile
CHANGED
data/gemfiles/rails_50.gemfile
CHANGED
data/gemfiles/rails_51.gemfile
CHANGED
@@ -0,0 +1,17 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "houston", require: false
|
6
|
+
gem "apnotic", ">= 1.2.0", require: false
|
7
|
+
gem "lowdown", require: false
|
8
|
+
gem "andpush", require: false
|
9
|
+
gem "fcm", require: false
|
10
|
+
gem "pry"
|
11
|
+
gem "pry-byebug", platforms: :mri
|
12
|
+
gem "railties", "~> 5.2.0.rc1"
|
13
|
+
gem "actionpack", "~> 5.2.0.rc1"
|
14
|
+
gem "actionview", "~> 5.2.0.rc1"
|
15
|
+
gem "activejob", "~> 5.2.0.rc1"
|
16
|
+
|
17
|
+
gemspec path: "../"
|
data/gemfiles/rails_edge.gemfile
CHANGED
@@ -1,10 +1,55 @@
|
|
1
1
|
Pushing::Platforms.configure do |config|
|
2
|
-
|
3
|
-
config.fcm.
|
4
|
-
|
5
|
-
|
6
|
-
config.
|
7
|
-
|
8
|
-
|
9
|
-
config.apn.
|
2
|
+
# Adapter that is used to send push notifications through FCM
|
3
|
+
config.fcm.adapter = Rails.env.test? ? :test : :andpush
|
4
|
+
|
5
|
+
# Your FCM servery key that can be found here: https://console.firebase.google.com/project/_/settings/cloudmessaging
|
6
|
+
config.fcm.server_key = 'YOUR_FCM_SERVER_KEY'
|
7
|
+
|
8
|
+
# Adapter that is used to send push notifications through APNs
|
9
|
+
config.apn.adapter = Rails.env.test? ? :test : :apnotic
|
10
|
+
|
11
|
+
# The environment that is used by default to send push notifications through APNs
|
12
|
+
config.apn.environment = Rails.env.production? ? :production : :development
|
13
|
+
|
14
|
+
# The scheme that is used for negotiating connection trust between your provider
|
15
|
+
# servers and Apple Push Notification service. As documented in the offitial doc,
|
16
|
+
# there are two schemes available:
|
17
|
+
#
|
18
|
+
# :token - Token-based provider connection trust (default)
|
19
|
+
# :certificate - Certificate-based provider connection trust
|
20
|
+
#
|
21
|
+
# This option is only applied when using an adapter that uses the HTTP/2-based
|
22
|
+
# API.
|
23
|
+
config.apn.connection_scheme = :token
|
24
|
+
|
25
|
+
# Path to the certificate or auth key for establishing a connection to APNs.
|
26
|
+
#
|
27
|
+
# This config is always required.
|
28
|
+
config.apn.certificate_path = 'path/to/your/certificate'
|
29
|
+
|
30
|
+
# Password for the certificate specified above if there's any.
|
31
|
+
# config.apn.certificate_password = 'passphrase'
|
32
|
+
|
33
|
+
# A 10-character key identifier (kid) key, obtained from your developer account.
|
34
|
+
# If you haven't created an Auth Key for your app, create a new one at:
|
35
|
+
# https://developer.apple.com/account/ios/authkey/
|
36
|
+
#
|
37
|
+
# Required if the +connection_scheme+ is set to +:token+.
|
38
|
+
config.apn.key_id = 'DEF123GHIJ'
|
39
|
+
|
40
|
+
# The issuer (iss) registered claim key, whose value is your 10-character Team ID,
|
41
|
+
# obtained from your developer account. Your team id could be found at:
|
42
|
+
# https://developer.apple.com/account/#/membership
|
43
|
+
#
|
44
|
+
# Required if the +connection_scheme+ is set to +:token+.
|
45
|
+
config.apn.team_id = 'ABC123DEFG'
|
46
|
+
|
47
|
+
# Header values that are added to every request to APNs. documentation for the
|
48
|
+
# headers available can be found here:
|
49
|
+
# https://developer.apple.com/library/content/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/CommunicatingwithAPNs.html#//apple_ref/doc/uid/TP40008194-CH11-SW13
|
50
|
+
config.apn.default_headers = {
|
51
|
+
apns_priority: 10,
|
52
|
+
apns_topic: 'your.awesomeapp.ios',
|
53
|
+
apns_collapse_id: 'wrong.topicname.com'
|
54
|
+
}
|
10
55
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
3
|
require 'apnotic'
|
4
|
+
require 'active_support/core_ext/hash/keys'
|
4
5
|
|
5
6
|
module Pushing
|
6
7
|
module Adapters
|
@@ -19,16 +20,26 @@ module Pushing
|
|
19
20
|
size: Process.getrlimit(Process::RLIMIT_NOFILE).first / 8
|
20
21
|
}.freeze
|
21
22
|
|
22
|
-
attr_reader :
|
23
|
+
attr_reader :connection_pool
|
23
24
|
|
24
25
|
def initialize(apn_settings)
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
26
|
+
options = case apn_settings.connection_scheme.to_sym
|
27
|
+
when :token
|
28
|
+
{
|
29
|
+
auth_method: :token,
|
30
|
+
cert_path: apn_settings.certificate_path,
|
31
|
+
key_id: apn_settings.key_id,
|
32
|
+
team_id: apn_settings.team_id
|
33
|
+
}
|
34
|
+
when :certificate
|
35
|
+
{
|
36
|
+
cert_path: apn_settings.certificate_path,
|
37
|
+
cert_pass: apn_settings.certificate_password
|
38
|
+
}
|
39
|
+
else
|
40
|
+
raise "Unknown connection scheme #{apn_settings.connection_scheme.inspect}. " \
|
41
|
+
"The connection scheme should either be :token or :certificate."
|
42
|
+
end
|
32
43
|
|
33
44
|
@connection_pool = {
|
34
45
|
development: Apnotic::ConnectionPool.development(options, DEFAULT_ADAPTER_OPTIONS),
|
@@ -38,7 +49,7 @@ module Pushing
|
|
38
49
|
|
39
50
|
def push!(notification)
|
40
51
|
message = Apnotic::Notification.new(notification.device_token)
|
41
|
-
json = notification.payload
|
52
|
+
json = notification.payload.dup
|
42
53
|
|
43
54
|
if aps = json.delete(:aps)
|
44
55
|
APS_DICTIONARY_KEYS.each {|key| message.instance_variable_set(:"@#{key}", aps[key]) }
|
@@ -49,10 +60,10 @@ module Pushing
|
|
49
60
|
message.apns_id = notification.headers[:'apns-id'] || message.apns_id
|
50
61
|
message.expiration = notification.headers[:'apns-expiration'].to_i
|
51
62
|
message.priority = notification.headers[:'apns-priority']
|
52
|
-
message.topic = notification.headers[:'apns-topic']
|
63
|
+
message.topic = notification.headers[:'apns-topic']
|
53
64
|
message.apns_collapse_id = notification.headers[:'apns-collapse-id']
|
54
65
|
|
55
|
-
response = connection_pool[notification.environment
|
66
|
+
response = connection_pool[notification.environment].with {|connection| connection.push(message) }
|
56
67
|
|
57
68
|
if !response
|
58
69
|
raise "Timeout sending a push notification"
|
@@ -3,11 +3,10 @@ require 'houston'
|
|
3
3
|
module Pushing
|
4
4
|
module Adapters
|
5
5
|
class HoustonAdapter
|
6
|
-
attr_reader :certificate_path, :
|
6
|
+
attr_reader :certificate_path, :client
|
7
7
|
|
8
8
|
def initialize(apn_settings)
|
9
9
|
@certificate_path = apn_settings.certificate_path
|
10
|
-
@environment = apn_settings.environment
|
11
10
|
|
12
11
|
@client = {
|
13
12
|
production: Houston::Client.production,
|
@@ -17,12 +16,12 @@ module Pushing
|
|
17
16
|
end
|
18
17
|
|
19
18
|
def push!(notification)
|
20
|
-
payload = notification.payload
|
19
|
+
payload = notification.payload.dup
|
21
20
|
aps = payload.delete(:aps)
|
22
21
|
aps[:device] = notification.device_token
|
23
22
|
|
24
23
|
houston_notification = Houston::Notification.new(payload.merge(aps))
|
25
|
-
client[notification.environment
|
24
|
+
client[notification.environment].push(houston_notification)
|
26
25
|
rescue => cause
|
27
26
|
error = Pushing::ApnDeliveryError.new("Error while trying to send push notification: #{cause.message}", nil, notification)
|
28
27
|
|
@@ -1,16 +1,14 @@
|
|
1
1
|
# frozen-string-literal: true
|
2
2
|
|
3
3
|
require 'json'
|
4
|
+
require 'delegate'
|
4
5
|
|
5
6
|
module Pushing
|
6
7
|
module Adapters
|
7
8
|
class LowdownAdapter
|
8
|
-
attr_reader :
|
9
|
+
attr_reader :clients
|
9
10
|
|
10
11
|
def initialize(apn_settings)
|
11
|
-
@environment = apn_settings.environment.to_sym
|
12
|
-
@topic = apn_settings.topic
|
13
|
-
|
14
12
|
# Don't load lowdown earlier as it may load Celluloid (and start it)
|
15
13
|
# before daemonizing the workers spun up by a gem (e,g, delayed_job).
|
16
14
|
require 'lowdown' unless defined?(Lodwown)
|
@@ -36,10 +34,10 @@ module Pushing
|
|
36
34
|
|
37
35
|
lowdown_notification.expiration = notification.headers[:'apns-expiration'].to_i if notification.headers[:'apns-expiration']
|
38
36
|
lowdown_notification.priority = notification.headers[:'apns-priority']
|
39
|
-
lowdown_notification.topic = notification.headers[:'apns-topic']
|
37
|
+
lowdown_notification.topic = notification.headers[:'apns-topic']
|
40
38
|
|
41
39
|
response = nil
|
42
|
-
clients[notification.environment
|
40
|
+
clients[notification.environment].group do |group|
|
43
41
|
group.send_notification(lowdown_notification) do |_response|
|
44
42
|
response = _response
|
45
43
|
end
|
data/lib/pushing/base.rb
CHANGED
@@ -102,6 +102,11 @@ module Pushing
|
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
+
# Push notifications do not support relative path links.
|
106
|
+
def supports_path? # :doc:
|
107
|
+
false
|
108
|
+
end
|
109
|
+
|
105
110
|
private
|
106
111
|
|
107
112
|
def set_payload_for_notification(payload, notification)
|
@@ -151,12 +156,12 @@ module Pushing
|
|
151
156
|
return notification if notification && headers.blank?
|
152
157
|
|
153
158
|
payload = {}
|
154
|
-
headers.each do |platform,
|
159
|
+
::Pushing::Platforms.config.select {|platform, _| headers[platform] }.each do |platform, config|
|
155
160
|
payload_class = ::Pushing::Platforms.lookup(platform)
|
156
161
|
|
157
|
-
if payload_class.should_render?(
|
162
|
+
if payload_class.should_render?(headers[platform])
|
158
163
|
json = render_json(platform, headers)
|
159
|
-
payload[platform] = payload_class.new(json,
|
164
|
+
payload[platform] = payload_class.new(json, headers[platform], config)
|
160
165
|
end
|
161
166
|
end
|
162
167
|
|
data/lib/pushing/platforms.rb
CHANGED
@@ -25,19 +25,39 @@ module Pushing
|
|
25
25
|
options.is_a?(Hash) ? options[:device_token].present? : options.present?
|
26
26
|
end
|
27
27
|
|
28
|
-
def initialize(payload, options)
|
28
|
+
def initialize(payload, options, config = EMPTY_HASH)
|
29
|
+
@payload = payload
|
30
|
+
@environment = config[:environment]
|
31
|
+
@headers = config[:default_headers] || {}
|
32
|
+
|
33
|
+
if config[:topic]
|
34
|
+
ActiveSupport::Deprecation.warn "`config.apn.topic' is deprecated and will be removed in 0.3.0. " \
|
35
|
+
"Please use `config.apn.default_headers' instead:\n\n" \
|
36
|
+
" config.apn.default_headers = {\n" \
|
37
|
+
" apns_topic: '#{config[:topic]}'\n" \
|
38
|
+
" }", caller
|
39
|
+
|
40
|
+
@headers['apns-topic'] ||= config[:topic]
|
41
|
+
end
|
42
|
+
|
29
43
|
if options.is_a?(String)
|
30
44
|
@device_token = options
|
31
|
-
|
32
|
-
@device_token
|
45
|
+
elsif options.is_a?(Hash)
|
46
|
+
@device_token = options[:device_token]
|
47
|
+
@environment = options[:environment] || @environment
|
48
|
+
@headers = @headers.merge(options[:headers] || EMPTY_HASH)
|
49
|
+
else
|
50
|
+
raise TypeError, "The :apn key only takes a device token as a string or a hash that has `device_token: \"...\"'."
|
33
51
|
end
|
34
52
|
|
35
|
-
|
36
|
-
|
53
|
+
# raise("APNs environment is required.") if @environment.nil?
|
54
|
+
# raise("APNs device token is required.") if @device_token.nil?
|
55
|
+
|
56
|
+
@environment = @environment.to_sym
|
37
57
|
end
|
38
58
|
|
39
59
|
def recipients
|
40
|
-
Array(@device_token)
|
60
|
+
Array("#{@environment}/#{@device_token}")
|
41
61
|
end
|
42
62
|
|
43
63
|
def headers
|
data/lib/pushing/railtie.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require "active_job/railtie"
|
2
2
|
require "rails"
|
3
|
+
require "abstract_controller/railties/routes_helpers"
|
3
4
|
|
4
5
|
module Pushing
|
5
6
|
class Railtie < Rails::Railtie # :nodoc:
|
@@ -16,6 +17,30 @@ module Pushing
|
|
16
17
|
end
|
17
18
|
end
|
18
19
|
|
20
|
+
initializer "pushing.set_configs" do |app|
|
21
|
+
paths = app.config.paths
|
22
|
+
options = ActiveSupport::OrderedOptions.new
|
23
|
+
|
24
|
+
if app.config.force_ssl
|
25
|
+
options.default_url_options ||= {}
|
26
|
+
options.default_url_options[:protocol] ||= "https"
|
27
|
+
end
|
28
|
+
|
29
|
+
options.assets_dir ||= paths["public"].first
|
30
|
+
|
31
|
+
# make sure readers methods get compiled
|
32
|
+
options.asset_host ||= app.config.asset_host
|
33
|
+
options.relative_url_root ||= app.config.relative_url_root
|
34
|
+
|
35
|
+
ActiveSupport.on_load(:pushing) do
|
36
|
+
include AbstractController::UrlFor
|
37
|
+
extend ::AbstractController::Railties::RoutesHelpers.with(app.routes, false)
|
38
|
+
include app.routes.mounted_helpers
|
39
|
+
|
40
|
+
options.each { |k, v| send("#{k}=", v) }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
19
44
|
initializer "pushing.compile_config_methods" do
|
20
45
|
ActiveSupport.on_load(:pushing) do
|
21
46
|
config.compile_methods! if config.respond_to?(:compile_methods!)
|
data/lib/pushing/version.rb
CHANGED
data/pushing.gemspec
CHANGED
@@ -7,14 +7,12 @@ Gem::Specification.new do |spec|
|
|
7
7
|
spec.name = "pushing"
|
8
8
|
spec.version = Pushing::VERSION
|
9
9
|
spec.authors = ["Yuki Nishijima"]
|
10
|
-
spec.email = ["
|
10
|
+
spec.email = ["yk.nishijima@gmail.com"]
|
11
11
|
spec.summary = %q{Push notification framework that does not hurt. finally.}
|
12
12
|
spec.description = %q{Pushing is like ActionMailer, but for sending push notifications.}
|
13
13
|
spec.homepage = "https://github.com/yuki24/pushing"
|
14
14
|
spec.license = "MIT"
|
15
15
|
spec.files = `git ls-files -z`.split("\x0").reject {|f| f.match(%r{^(test)/}) }
|
16
|
-
spec.bindir = "exe"
|
17
|
-
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
16
|
spec.require_paths = ["lib"]
|
19
17
|
|
20
18
|
spec.add_dependency "actionpack", ">= 4.2.0"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pushing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yuki Nishijima
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-03-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: actionpack
|
@@ -138,7 +138,7 @@ dependencies:
|
|
138
138
|
version: '0'
|
139
139
|
description: Pushing is like ActionMailer, but for sending push notifications.
|
140
140
|
email:
|
141
|
-
-
|
141
|
+
- yk.nishijima@gmail.com
|
142
142
|
executables: []
|
143
143
|
extensions: []
|
144
144
|
extra_rdoc_files: []
|
@@ -153,10 +153,12 @@ files:
|
|
153
153
|
- Rakefile
|
154
154
|
- bin/console
|
155
155
|
- bin/setup
|
156
|
+
- certs/apns_auth_key_for_jwt_auth.p8.enc
|
156
157
|
- certs/apns_example_production.pem.enc
|
157
158
|
- gemfiles/rails_42.gemfile
|
158
159
|
- gemfiles/rails_50.gemfile
|
159
160
|
- gemfiles/rails_51.gemfile
|
161
|
+
- gemfiles/rails_52.gemfile
|
160
162
|
- gemfiles/rails_edge.gemfile
|
161
163
|
- lib/generators/pushing/USAGE
|
162
164
|
- lib/generators/pushing/notifier_generator.rb
|
@@ -204,7 +206,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
204
206
|
version: '0'
|
205
207
|
requirements: []
|
206
208
|
rubyforge_project:
|
207
|
-
rubygems_version: 2.6
|
209
|
+
rubygems_version: 2.7.6
|
208
210
|
signing_key:
|
209
211
|
specification_version: 4
|
210
212
|
summary: Push notification framework that does not hurt. finally.
|