pushing 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 562381d6f82d103cc6e5db2b3408ea36d28fdb7a
4
- data.tar.gz: 4df883a714c37d2a87f7555b75eb1cce48d5f370
2
+ SHA256:
3
+ metadata.gz: e6d63cb3c86db8e3be6c3335fb26e7e028ba5b25eb9ad13aa4b8febbf82d432c
4
+ data.tar.gz: 82d5cf8214b81355a1dbc1db23843525e492889af24f0750874d1ec626830767
5
5
  SHA512:
6
- metadata.gz: be1bde88cd14ce57d6196cffb630bcf228d89e47040c7cce38e3887b6291bc32ff0edf83ed93ce3532deb1b9f477ab9990f0fae094df5a9f1566ed4816524e42
7
- data.tar.gz: 060ca860f1f7960dfa264c4d5fd3ee6aa21fa11546cd6dcf6ce6cc87e6d879a16f0ab1211b9715925938fc64e38251cc14771767c5437a59d355f226ad44cce9
6
+ metadata.gz: 2f6c64a1574f61b456656018eb94fe6c73f3638745c8c26420f22098ef0de9188511e317a7f070ad2b683c58785650634fb03e2db9ccc6278f4e1280d7729e46
7
+ data.tar.gz: 5c31ab15a5ec3f68622167ea05fe89eaf9b43c563d65f02d903f3793e3a5c22674d90af97d631dfe79df086524dd2f85f873298bff5136443358dc6944687e14
data/.gitignore CHANGED
@@ -10,3 +10,4 @@
10
10
  gemfiles/*.lock
11
11
  certs/apns_example_production.pem
12
12
  gemfiles/.bundle/
13
+ certs/apns_auth_key_for_jwt_auth.p8
@@ -4,25 +4,35 @@ cache: bundler
4
4
  sudo: false
5
5
 
6
6
  before_install:
7
- 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
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.7
11
- - 2.3.4
12
- - 2.4.1
13
+ - 2.2.9
14
+ - 2.3.6
15
+ - 2.4.3
16
+ - 2.5.0
13
17
  - ruby-head
14
- - jruby-9.1.10.0
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.10.0
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 "rails_50" do
11
- gem "railties", '~> 5.0.0'
12
- gem "actionpack", '~> 5.0.0'
13
- gem "actionview", '~> 5.0.0'
14
- gem "activejob", '~> 5.0.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
@@ -5,7 +5,7 @@ gemspec
5
5
 
6
6
  # APNs
7
7
  gem 'houston', require: false
8
- gem 'apnotic', require: false
8
+ gem 'apnotic', '>= 1.2.0', require: false
9
9
  gem 'lowdown', require: false
10
10
 
11
11
  # FCM
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
- # Pushing [![Build Status](https://travis-ci.org/yuki24/pushing.svg?branch=master)](https://travis-ci.org/yuki24/pushing)
1
+ # Pushing: ActionMailer for Push Notifications [![Build Status](https://travis-ci.org/yuki24/pushing.svg?branch=master)](https://travis-ci.org/yuki24/pushing)
2
2
 
3
- Pushing is a push notification framework that implements interfaces similar to ActionMailer's APIs.
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's push notification implementation.
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', github: 'yuki24/pushing'
15
- gem 'jbuilder'
14
+ gem 'pushing'
15
+ gem 'jbuilder' # if you don't have it in your Gemfile
16
16
  ```
17
17
 
18
- As 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).
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 Client Gems
20
+ ### Supported Platforms
21
21
 
22
- Pushing itself doesn't make HTTP requests. Instead, it uses an adapter and let an underlaying gem do it. Currently, Pushing has support for the following client gems:
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 integration
37
- gem 'andpush' # FCM integration
36
+ gem 'apnotic' # APNs
37
+ gem 'andpush' # FCM
38
38
  ```
39
39
 
40
40
  ### Walkthrough to Writing a Notifier
41
41
 
42
- In this README, we'll use Twitter as an example. Suppose you'd like to send a push notification when a user receives a new direct message from other user. To get started, you can use Pushing's notifier generator:
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
- Notice that the `:apn` key takes a truthy string value while the `:fcm` key takes a boolean value. Also, Pushing only sends a notification for the platforms that are given a truthy value. For example, the call:
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 Push Notifications
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 the test environment, use the `:test` adapter instead of an actual adapter you'd like to use in development/production.
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 use the `#deliveries` method. Here is an example with [ActiveSupport::TestCase](http://api.rubyonrails.org/classes/ActiveSupport/TestCase.html):
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
@@ -3,7 +3,7 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "houston", require: false
6
- gem "apnotic", require: false
6
+ gem "apnotic", ">= 1.2.0", require: false
7
7
  gem "lowdown", require: false
8
8
  gem "andpush", require: false
9
9
  gem "fcm", require: false
@@ -3,7 +3,7 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "houston", require: false
6
- gem "apnotic", require: false
6
+ gem "apnotic", ">= 1.2.0", require: false
7
7
  gem "lowdown", require: false
8
8
  gem "andpush", require: false
9
9
  gem "fcm", require: false
@@ -3,7 +3,7 @@
3
3
  source "https://rubygems.org"
4
4
 
5
5
  gem "houston", require: false
6
- gem "apnotic", require: false
6
+ gem "apnotic", ">= 1.2.0", require: false
7
7
  gem "lowdown", require: false
8
8
  gem "andpush", require: false
9
9
  gem "fcm", require: false
@@ -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: "../"
@@ -10,7 +10,7 @@ git "git://github.com/rails/rails.git" do
10
10
  end
11
11
 
12
12
  gem "houston", require: false
13
- gem "apnotic", require: false
13
+ gem "apnotic", ">= 1.2.0", require: false
14
14
  gem "lowdown", require: false
15
15
  gem "andpush", require: false
16
16
  gem "fcm", require: false
@@ -1,10 +1,55 @@
1
1
  Pushing::Platforms.configure do |config|
2
- config.fcm.adapter = Rails.env.test? ? :test : :andpush
3
- config.fcm.server_key = 'YOUR_FCM_TEST_SERVER_KEY'
4
-
5
- config.apn.environment = Rails.env.production? ? :production : :development
6
- config.apn.adapter = Rails.env.test? ? :test : :apnotic
7
- config.apn.topic = 'com.awesomecompany.app'
8
- config.apn.certificate_path = '/config/your_certificate.pem'
9
- config.apn.certificate_password = 'PASSWORD_FOR_CERT'
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 :environment, :topic, :connection_pool
23
+ attr_reader :connection_pool
23
24
 
24
25
  def initialize(apn_settings)
25
- @environment = apn_settings.environment.to_sym
26
- @topic = apn_settings.topic
27
-
28
- options = {
29
- cert_path: apn_settings.certificate_path,
30
- cert_pass: apn_settings.certificate_password
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'] || 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 || environment].with {|connection| connection.push(message) }
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, :environment, :client
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 || environment].push(houston_notification)
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 :environment, :topic, :clients
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'] || topic
37
+ lowdown_notification.topic = notification.headers[:'apns-topic']
40
38
 
41
39
  response = nil
42
- clients[notification.environment || environment].group do |group|
40
+ clients[notification.environment].group do |group|
43
41
  group.send_notification(lowdown_notification) do |_response|
44
42
  response = _response
45
43
  end
@@ -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, options|
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?(options)
162
+ if payload_class.should_render?(headers[platform])
158
163
  json = render_json(platform, headers)
159
- payload[platform] = payload_class.new(json, options)
164
+ payload[platform] = payload_class.new(json, headers[platform], config)
160
165
  end
161
166
  end
162
167
 
@@ -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
- else options.is_a?(Hash)
32
- @device_token, @environment, @headers = options.values_at(:device_token, :environment, :headers)
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
- @payload = payload
36
- @headers ||= EMPTY_HASH
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
@@ -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!)
@@ -1,3 +1,3 @@
1
1
  module Pushing
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -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 = ["mail@yukinishijima.net"]
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.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuki Nishijima
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-28 00:00:00.000000000 Z
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
- - mail@yukinishijima.net
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.11
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.