ruby-push-notifications 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.travis.yml +1 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +18 -8
- data/README.md +4 -1
- data/lib/ruby-push-notifications/apns/apns_connection.rb +23 -0
- data/lib/ruby-push-notifications/apns/apns_notification.rb +41 -9
- data/lib/ruby-push-notifications/apns/apns_pusher.rb +21 -0
- data/lib/ruby-push-notifications/apns.rb +28 -3
- data/lib/ruby-push-notifications/gcm/gcm_connection.rb +17 -0
- data/lib/ruby-push-notifications/gcm/gcm_error.rb +3 -0
- data/lib/ruby-push-notifications/gcm/gcm_notification.rb +12 -0
- data/lib/ruby-push-notifications/gcm/gcm_pusher.rb +13 -0
- data/lib/ruby-push-notifications/gcm/gcm_response.rb +37 -1
- data/lib/ruby-push-notifications/gcm/gcm_result.rb +14 -0
- data/lib/ruby-push-notifications/version.rb +1 -1
- data/lib/ruby-push-notifications.rb +1 -6
- data/ruby-push-notifications.gemspec +2 -0
- data/spec/spec_helper.rb +5 -0
- metadata +21 -35
- data/examples/apns copy.rb +0 -17
- data/examples/gcm copy.rb +0 -16
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 26d655780454310999a17dd8ff1665594bdcc8d9
|
4
|
+
data.tar.gz: e270d62ecb2b946f24919fefb21412f52500be57
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 20912ffb3a561c8a6bc790c3ccafde1e749e89aabbac17516e9f87cd175f5b212d4de73b301940064ff7c4bdbab82b0c1ff07fa745835df16ba496ff00df0be2
|
7
|
+
data.tar.gz: 92c44920810d644ffb9f2db27587091f437d68ccae8ee8893794f5114ba300d3a0166ae43163d1aee5116a6a39f5a5bb00094abc7439f2a7435bbe07cd98c70f
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,45 +1,54 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ruby-push-notifications (0.0
|
4
|
+
ruby-push-notifications (0.1.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
-
activesupport (4.2.
|
9
|
+
activesupport (4.2.1)
|
10
10
|
i18n (~> 0.7)
|
11
11
|
json (~> 1.7, >= 1.7.7)
|
12
12
|
minitest (~> 5.1)
|
13
13
|
thread_safe (~> 0.3, >= 0.3.4)
|
14
14
|
tzinfo (~> 1.1)
|
15
|
-
addressable (2.3.
|
15
|
+
addressable (2.3.8)
|
16
|
+
codeclimate-test-reporter (0.4.7)
|
17
|
+
simplecov (>= 0.7.1, < 1.0.0)
|
16
18
|
crack (0.4.2)
|
17
19
|
safe_yaml (~> 1.0.0)
|
18
20
|
diff-lcs (1.2.5)
|
21
|
+
docile (1.1.5)
|
19
22
|
factory_girl (4.5.0)
|
20
23
|
activesupport (>= 3.0.0)
|
21
24
|
i18n (0.7.0)
|
22
25
|
json (1.8.2)
|
23
26
|
minitest (5.5.1)
|
27
|
+
multi_json (1.11.0)
|
24
28
|
rake (10.4.2)
|
25
29
|
rspec (3.2.0)
|
26
30
|
rspec-core (~> 3.2.0)
|
27
31
|
rspec-expectations (~> 3.2.0)
|
28
32
|
rspec-mocks (~> 3.2.0)
|
29
|
-
rspec-core (3.2.
|
33
|
+
rspec-core (3.2.2)
|
30
34
|
rspec-support (~> 3.2.0)
|
31
35
|
rspec-expectations (3.2.0)
|
32
36
|
diff-lcs (>= 1.2.0, < 2.0)
|
33
37
|
rspec-support (~> 3.2.0)
|
34
|
-
rspec-mocks (3.2.
|
38
|
+
rspec-mocks (3.2.1)
|
35
39
|
diff-lcs (>= 1.2.0, < 2.0)
|
36
40
|
rspec-support (~> 3.2.0)
|
37
|
-
rspec-support (3.2.
|
41
|
+
rspec-support (3.2.2)
|
38
42
|
safe_yaml (1.0.4)
|
39
|
-
|
43
|
+
simplecov (0.9.2)
|
44
|
+
docile (~> 1.1.0)
|
45
|
+
multi_json (~> 1.0)
|
46
|
+
simplecov-html (~> 0.9.0)
|
47
|
+
simplecov-html (0.9.0)
|
48
|
+
thread_safe (0.3.5)
|
40
49
|
tzinfo (1.2.2)
|
41
50
|
thread_safe (~> 0.1)
|
42
|
-
webmock (1.
|
51
|
+
webmock (1.21.0)
|
43
52
|
addressable (>= 2.3.6)
|
44
53
|
crack (>= 0.3.2)
|
45
54
|
|
@@ -48,6 +57,7 @@ PLATFORMS
|
|
48
57
|
|
49
58
|
DEPENDENCIES
|
50
59
|
bundler (~> 1.6)
|
60
|
+
codeclimate-test-reporter
|
51
61
|
factory_girl (~> 4.0)
|
52
62
|
rake (~> 10.4)
|
53
63
|
rspec (~> 3.2)
|
data/README.md
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
# Ruby
|
1
|
+
# Ruby Push Notifications
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/calonso/ruby-push-notifications.svg)](https://travis-ci.org/calonso/ruby-push-notifications) [![Dependency Status](https://gemnasium.com/calonso/ruby-push-notifications.svg)](https://gemnasium.com/calonso/ruby-push-notifications) [![Code Climate](https://codeclimate.com/github/calonso/ruby-push-notifications/badges/gpa.svg)](https://codeclimate.com/github/calonso/ruby-push-notifications) [![Test Coverage](https://codeclimate.com/github/calonso/ruby-push-notifications/badges/coverage.svg)](https://codeclimate.com/github/calonso/ruby-push-notifications) [![Gem Version](https://badge.fury.io/rb/ruby-push-notifications.svg)](http://badge.fury.io/rb/ruby-push-notifications)
|
2
4
|
|
3
5
|
###iOS and Android Push Notifications made easy!
|
4
6
|
|
@@ -46,6 +48,7 @@ Feel free to contribute!!
|
|
46
48
|
* Validate GCM registration ids format
|
47
49
|
* Validate GCM notifications format and max size
|
48
50
|
* Split GCM notifications in parts if more than 1000 destinations are given (currently raising exception)
|
51
|
+
* Integrate with APNS Feedback service
|
49
52
|
|
50
53
|
## Contributing
|
51
54
|
|
@@ -5,15 +5,28 @@ require 'forwardable'
|
|
5
5
|
|
6
6
|
module RubyPushNotifications
|
7
7
|
module APNS
|
8
|
+
# This class encapsulates a connection with APNS.
|
9
|
+
#
|
10
|
+
# @author Carlos Alonso
|
8
11
|
class APNSConnection
|
9
12
|
extend Forwardable
|
10
13
|
|
14
|
+
# @private URL of the APNS Sandbox environment
|
11
15
|
APNS_SANDBOX_URL = 'gateway.sandbox.push.apple.com'
|
16
|
+
|
17
|
+
# @private URL of APNS production environment
|
12
18
|
APNS_PRODUCTION_URL = 'gateway.push.apple.com'
|
19
|
+
|
20
|
+
# @private Port to connect to
|
13
21
|
APNS_PORT = 2195
|
14
22
|
|
15
23
|
def_delegators :@sslsock, :write, :flush, :to_io, :read
|
16
24
|
|
25
|
+
# Opens a connection with APNS
|
26
|
+
#
|
27
|
+
# @param cert [String]. Contents of the PEM encoded certificate
|
28
|
+
# @param sandbox [Boolean]. Whether to use the sandbox environment or not.
|
29
|
+
# @return [APNSConnection]. The recently stablished connection.
|
17
30
|
def self.open(cert, sandbox)
|
18
31
|
ctx = OpenSSL::SSL::SSLContext.new
|
19
32
|
ctx.key = OpenSSL::PKey::RSA.new cert
|
@@ -27,15 +40,25 @@ module RubyPushNotifications
|
|
27
40
|
new socket, ssl
|
28
41
|
end
|
29
42
|
|
43
|
+
# Returns the URL to connect to.
|
44
|
+
#
|
45
|
+
# @param sandbox [Boolean]. Whether it is the sandbox or the production
|
46
|
+
# environment we're looking for.
|
47
|
+
# @return [String]. The URL for the APNS service.
|
30
48
|
def self.host(sandbox)
|
31
49
|
sandbox ? APNS_SANDBOX_URL : APNS_PRODUCTION_URL
|
32
50
|
end
|
33
51
|
|
52
|
+
# Initializes the APNSConnection
|
53
|
+
#
|
54
|
+
# @param tcpsock [TCPSocket]. The used TCP Socket.
|
55
|
+
# @param sslsock [SSLSocket]. The connected SSL Socket.
|
34
56
|
def initialize(tcpsock, sslsock)
|
35
57
|
@tcpsock = tcpsock
|
36
58
|
@sslsock = sslsock
|
37
59
|
end
|
38
60
|
|
61
|
+
# Closes the APNSConnection
|
39
62
|
def close
|
40
63
|
@sslsock.close
|
41
64
|
@tcpsock.close
|
@@ -3,42 +3,70 @@ require 'json'
|
|
3
3
|
|
4
4
|
module RubyPushNotifications
|
5
5
|
module APNS
|
6
|
+
# Represents a APNS Notification.
|
7
|
+
# Manages the conversion of the notification to APNS binary format for
|
8
|
+
# each of the destinations.
|
9
|
+
# By default sets maximum expiration date (4 weeks).
|
10
|
+
#
|
11
|
+
# @author Carlos Alonso
|
6
12
|
class APNSNotification
|
7
13
|
|
8
|
-
|
14
|
+
# @private. 4 weeks in seconds
|
15
|
+
WEEKS_4 = 2419200
|
9
16
|
|
17
|
+
# @return [Array]. Array with the results from sending this notification
|
10
18
|
attr_accessor :results
|
11
19
|
|
20
|
+
# Initializes the APNS Notification
|
21
|
+
#
|
22
|
+
# @param [Array]. Array containing all destinations for the notification
|
23
|
+
# @param [Hash]. Hash with the data to use as payload.
|
12
24
|
def initialize(tokens, data)
|
13
25
|
@tokens = tokens
|
14
26
|
@data = data
|
15
27
|
end
|
16
28
|
|
29
|
+
# Method that yields the notification's binary for each of the receivers.
|
30
|
+
#
|
31
|
+
# @param starting_id [Integer]. Every notification encodes a unique ID for
|
32
|
+
# further reference. This parameter represents the first id the first
|
33
|
+
# notification of this group should use.
|
34
|
+
# @yieldparam [String]. APNS binary's representation of this notification.
|
35
|
+
# Consisting of:
|
36
|
+
# Notification = 2(1), FrameLength(4), items(FrameLength)
|
37
|
+
# Item = ItemID(1), ItemLength(2), data(ItemLength)
|
38
|
+
# Items:
|
39
|
+
# Device Token => Id: 1, length: 32, data: binary device token
|
40
|
+
# Payload => Id: 2, length: ??, data: json formatted payload
|
41
|
+
# Notification ID => Id: 3, length: 4, data: notif id as int
|
42
|
+
# Expiration Date => Id: 4, length: 4, data: Unix timestamp as int
|
43
|
+
# Priority => Id: 5, length: 1, data: 10 as 1 byte int
|
44
|
+
# (https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/CommunicatingWIthAPS.html#//apple_ref/doc/uid/TP40008194-CH101-SW4)
|
17
45
|
def each_message(starting_id)
|
18
46
|
@tokens.each_with_index do |token, i|
|
19
|
-
# Notification = 2(1), FrameLength(4), items(FrameLength)
|
20
|
-
# Item = ItemID(1), ItemLength(2), data(ItemLength)
|
21
|
-
# Items:
|
22
|
-
# Device Token => Id: 1, length: 32, data: binary device token
|
23
|
-
# Payload => Id: 2, length: ??, data: json formatted payload
|
24
|
-
# Notification ID => Id: 3, length: 4, data: notif id as int
|
25
|
-
# Expiration Date => Id: 4, length: 4, data: Unix timestamp as int
|
26
|
-
# Priority => Id: 5, length: 1, data: 10 as 1 byte int
|
27
47
|
bytes = device_token(token) + payload + notification_id(starting_id + i) + expiration_date + priority
|
28
48
|
yield [2, bytes.bytesize, bytes].pack 'cNa*'
|
29
49
|
end
|
30
50
|
end
|
31
51
|
|
52
|
+
# @return [Integer]. The number of binaries this notification will send.
|
53
|
+
# One for each receiver.
|
32
54
|
def count
|
33
55
|
@tokens.count
|
34
56
|
end
|
35
57
|
|
36
58
|
private
|
37
59
|
|
60
|
+
# @param [String]. The device token to encode.
|
61
|
+
# @return [String]. Binary representation of the device token field.
|
38
62
|
def device_token(token)
|
39
63
|
[1, 32, token].pack 'cnH64'
|
40
64
|
end
|
41
65
|
|
66
|
+
# Generates the APNS's binary representation of the notification's payload.
|
67
|
+
# Caches the value in an instance variable.
|
68
|
+
#
|
69
|
+
# @return [String]. Binary representation of the notification's payload.
|
42
70
|
def payload
|
43
71
|
@encoded_payload ||= -> {
|
44
72
|
json = JSON.dump(@data).force_encoding 'ascii-8bit'
|
@@ -46,14 +74,18 @@ module RubyPushNotifications
|
|
46
74
|
}.call
|
47
75
|
end
|
48
76
|
|
77
|
+
# @param [Integer]. The unique ID for this notification.
|
78
|
+
# @return [String]. Binary representation of the notification id field.
|
49
79
|
def notification_id(id)
|
50
80
|
[3, 4, id].pack 'cnN'
|
51
81
|
end
|
52
82
|
|
83
|
+
# @return [String]. Binary representation of the expiration date field.
|
53
84
|
def expiration_date
|
54
85
|
[4, 4, (Time.now + WEEKS_4).to_i].pack 'cnN'
|
55
86
|
end
|
56
87
|
|
88
|
+
# @return [String]. Binary representation of the priority field.
|
57
89
|
def priority
|
58
90
|
[5, 1, 10].pack 'cnc'
|
59
91
|
end
|
@@ -1,13 +1,34 @@
|
|
1
1
|
|
2
2
|
module RubyPushNotifications
|
3
3
|
module APNS
|
4
|
+
# This class coordinates the process of sending notifications.
|
5
|
+
# It takes care of reopening closed APNSConnections and seeking back to
|
6
|
+
# the failed notification to keep writing.
|
7
|
+
#
|
8
|
+
# Remember that APNS doesn't confirm successful notification, it just
|
9
|
+
# notifies when one went wrong and closes the connection. Therefore, this
|
10
|
+
# APNSPusher reconnects and rewinds the array until the notification that
|
11
|
+
# Apple rejected.
|
12
|
+
#
|
13
|
+
# @author Carlos Alonso
|
4
14
|
class APNSPusher
|
5
15
|
|
16
|
+
# @param certificate [String]. The PEM encoded APNS certificate.
|
17
|
+
# @param sandbox [Boolean]. Whether the certificate is an APNS sandbox or not.
|
6
18
|
def initialize(certificate, sandbox)
|
7
19
|
@certificate = certificate
|
8
20
|
@sandbox = sandbox
|
9
21
|
end
|
10
22
|
|
23
|
+
# Pushes the notifications.
|
24
|
+
# Builds an array with all the binaries (one for each notification and receiver)
|
25
|
+
# and pushes them sequentially to APNS monitoring the response.
|
26
|
+
# If an error is received, the connection is reopened and the process
|
27
|
+
# continues at the next notification after the failed one (pointed by the response error)
|
28
|
+
#
|
29
|
+
# For each notification assigns an array with the results of each submission.
|
30
|
+
#
|
31
|
+
# @param notifications [Array]. All the APNSNotifications to be sent.
|
11
32
|
def push(notifications)
|
12
33
|
conn = APNSConnection.open @certificate, @sandbox
|
13
34
|
|
@@ -1,15 +1,40 @@
|
|
1
|
-
|
1
|
+
#
|
2
|
+
# @author Carlos Alonso
|
3
|
+
#
|
2
4
|
module RubyPushNotifications::APNS
|
5
|
+
|
6
|
+
# No Status Error Code. Represents a successfully sent notification
|
3
7
|
NO_ERROR_STATUS_CODE = 0
|
8
|
+
|
9
|
+
# An error occurred while processing the notification
|
4
10
|
PROCESSING_ERROR_STATUS_CODE = 1
|
11
|
+
|
12
|
+
# The notification contains no device token
|
5
13
|
MISSING_DEVICE_TOKEN_STATUS_CODE = 2
|
6
|
-
|
14
|
+
|
15
|
+
# The notification is being sent through the plain TCPSocket,
|
16
|
+
# rather than the SSL one
|
17
|
+
MISSING_TOPIC_STATUS_CODE = 3
|
18
|
+
|
19
|
+
# The notification contains no payload
|
7
20
|
MISSING_PAYLOAD_STATUS_CODE = 4
|
21
|
+
|
22
|
+
# The given token sice is invalid (!= 32)
|
8
23
|
INVALID_TOKEN_SIZE_STATUS_CODE = 5
|
24
|
+
|
25
|
+
# The given topic size is invalid
|
9
26
|
INVALID_TOPIC_SIZE_STATUS_CODE = 6
|
27
|
+
|
28
|
+
# Payload size is invalid (256 bytes if iOS < 8, 2Kb otherwise)
|
10
29
|
INVALID_PAYLOAD_SIZE_STATUS_CODE = 7
|
11
|
-
|
30
|
+
|
31
|
+
# The token is for dev and the env is prod or viceversa, or simply wrong
|
32
|
+
INVALID_TOKEN_STATUS_CODE = 8
|
33
|
+
|
34
|
+
# Connection closed at Apple's end
|
12
35
|
SHUTDOWN_STATUS_CODE = 10
|
36
|
+
|
37
|
+
# Unknown error
|
13
38
|
UNKNOWN_ERROR_STATUS_CODE = 255
|
14
39
|
end
|
15
40
|
|
@@ -3,14 +3,31 @@ require 'net/https'
|
|
3
3
|
|
4
4
|
module RubyPushNotifications
|
5
5
|
module GCM
|
6
|
+
# Encapsulates a connection to the GCM service
|
7
|
+
# Responsible for final connection with the service.
|
8
|
+
#
|
9
|
+
# @author Carlos Alonso
|
6
10
|
class GCMConnection
|
7
11
|
|
12
|
+
# @private The URL of the Android GCM endpoint
|
8
13
|
GCM_URL = 'https://android.googleapis.com/gcm/send'
|
9
14
|
|
15
|
+
# @private Content-Type HTTP Header string
|
10
16
|
CONTENT_TYPE_HEADER = 'Content-Type'
|
17
|
+
|
18
|
+
# @private Application/JSON content type
|
11
19
|
JSON_CONTENT_TYPE = 'application/json'
|
20
|
+
|
21
|
+
# @private Authorization HTTP Header String
|
12
22
|
AUTHORIZATION_HEADER = 'Authorization'
|
13
23
|
|
24
|
+
# Issues a POST request to the GCM send endpoint to
|
25
|
+
# submit the given notifications.
|
26
|
+
#
|
27
|
+
# @param notification [String]. The text to POST
|
28
|
+
# @param key [String]. The GCM sender id to use
|
29
|
+
# (https://developer.android.com/google/gcm/gcm.html#senderid)
|
30
|
+
# @return [GCMResponse]. The GCMResponse that encapsulates the received response
|
14
31
|
def self.post(notification, key)
|
15
32
|
headers = {
|
16
33
|
CONTENT_TYPE_HEADER => JSON_CONTENT_TYPE,
|
@@ -1,15 +1,27 @@
|
|
1
1
|
|
2
2
|
module RubyPushNotifications
|
3
3
|
module GCM
|
4
|
+
# Encapsulates a GCM Notification.
|
5
|
+
# By default only Required fields are set.
|
6
|
+
# (https://developer.android.com/google/gcm/server-ref.html#send-downstream)
|
7
|
+
#
|
8
|
+
# @author Carlos Alonso
|
4
9
|
class GCMNotification
|
5
10
|
|
11
|
+
# @return [Array]. Array with the results from sending this notification
|
6
12
|
attr_accessor :results
|
7
13
|
|
14
|
+
# Initializes the notification
|
15
|
+
#
|
16
|
+
# @param [Array]. Array with the receiver's GCM registration ids.
|
17
|
+
# @param [Hash]. Payload to send.
|
8
18
|
def initialize(registration_ids, data)
|
9
19
|
@registration_ids = registration_ids
|
10
20
|
@data = data
|
11
21
|
end
|
12
22
|
|
23
|
+
# @return [String]. The GCM's JSON format for the payload to send.
|
24
|
+
# (https://developer.android.com/google/gcm/server-ref.html#send-downstream)
|
13
25
|
def as_gcm_json
|
14
26
|
JSON.dump(
|
15
27
|
registration_ids: @registration_ids,
|
@@ -1,12 +1,25 @@
|
|
1
1
|
|
2
2
|
module RubyPushNotifications
|
3
3
|
module GCM
|
4
|
+
|
5
|
+
# This class is responsible for sending notifications to the GCM service.
|
6
|
+
#
|
7
|
+
# @author Carlos Alonso
|
4
8
|
class GCMPusher
|
5
9
|
|
10
|
+
# Initializes the GCMPusher
|
11
|
+
#
|
12
|
+
# @param key [String]. GCM sender id to use
|
13
|
+
# ((https://developer.android.com/google/gcm/gcm.html#senderid))
|
6
14
|
def initialize(key)
|
7
15
|
@key = key
|
8
16
|
end
|
9
17
|
|
18
|
+
# Actually pushes the given notifications.
|
19
|
+
# Assigns every notification an array with the result of each
|
20
|
+
# individual notification.
|
21
|
+
#
|
22
|
+
# @param notifications [Array]. Array of GCMNotification to send.
|
10
23
|
def push(notifications)
|
11
24
|
notifications.each do |notif|
|
12
25
|
notif.results = GCMConnection.post notif.as_gcm_json, @key
|
@@ -1,10 +1,35 @@
|
|
1
1
|
|
2
2
|
module RubyPushNotifications
|
3
3
|
module GCM
|
4
|
+
|
5
|
+
# This class encapsulates a response received from the GCM service
|
6
|
+
# and helps parsing and understanding the received meesages/codes.
|
7
|
+
#
|
8
|
+
# @author Carlos Alonso
|
4
9
|
class GCMResponse
|
5
10
|
|
6
|
-
|
11
|
+
# @return [Integer] the number of successfully sent notifications
|
12
|
+
attr_reader :success
|
13
|
+
|
14
|
+
# @return [Integer] the number of failed notifications
|
15
|
+
attr_reader :failed
|
16
|
+
|
17
|
+
# @return [Integer] the number of received canonical IDS
|
18
|
+
# (https://developer.android.com/google/gcm/server-ref.html#table4)
|
19
|
+
attr_reader :canonical_ids
|
20
|
+
|
21
|
+
# @return [Array] Array of a GCMResult for every receiver of the notification
|
22
|
+
# sent indicating the result of the operation.
|
23
|
+
attr_reader :results
|
7
24
|
|
25
|
+
# Initializes the GCMResponse and runs response parsing
|
26
|
+
#
|
27
|
+
# @param code [Integer]. The HTTP status code received
|
28
|
+
# @param body [String]. The response body received.
|
29
|
+
# @raise MalformedGCMJsonError if code == 400 Bad Request
|
30
|
+
# @raise GCMAuthError if code == 401 Unauthorized
|
31
|
+
# @raise GCMInternalError if code == 5xx
|
32
|
+
# @raise UnexpectedGCMResponseError if code != 200
|
8
33
|
def initialize(code, body)
|
9
34
|
case code
|
10
35
|
when 200
|
@@ -30,6 +55,12 @@ module RubyPushNotifications
|
|
30
55
|
|
31
56
|
private
|
32
57
|
|
58
|
+
# Parses the response extracting counts for successful, failed and
|
59
|
+
# containing canonical ID messages.
|
60
|
+
# Also creates the results array assigning a GCMResult subclass for each
|
61
|
+
# registration ID the notification was sent to.
|
62
|
+
#
|
63
|
+
# @param body [String]. The response body
|
33
64
|
def parse_response(body)
|
34
65
|
json = JSON.parse body, symbolize_names: true
|
35
66
|
@success = json[:success]
|
@@ -38,6 +69,11 @@ module RubyPushNotifications
|
|
38
69
|
@results = (json[:results] || []).map { |result| gcm_result_for result }
|
39
70
|
end
|
40
71
|
|
72
|
+
# Factory method that, for each GCM result object assigns a GCMResult
|
73
|
+
# subclass.
|
74
|
+
#
|
75
|
+
# @param result [Hash]. GCM Result parsed hash
|
76
|
+
# @return [GCMResult]. Corresponding GCMResult subclass
|
41
77
|
def gcm_result_for(result)
|
42
78
|
if canonical_id = result[:registration_id]
|
43
79
|
GCMCanonicalIDResult.new canonical_id
|
@@ -1,8 +1,14 @@
|
|
1
1
|
|
2
2
|
module RubyPushNotifications
|
3
3
|
module GCM
|
4
|
+
# Class that encapsulates the result of a single sent notification to a single
|
5
|
+
# Registration ID
|
6
|
+
# (https://developer.android.com/google/gcm/server-ref.html#table4)
|
7
|
+
# @author Carlos Alonso
|
4
8
|
class GCMResult ; end
|
5
9
|
|
10
|
+
# Indicates that the notification was successfully sent to the corresponding
|
11
|
+
# registration ID
|
6
12
|
class GCMResultOK < GCMResult
|
7
13
|
|
8
14
|
def ==(other)
|
@@ -10,7 +16,12 @@ module RubyPushNotifications
|
|
10
16
|
end
|
11
17
|
end
|
12
18
|
|
19
|
+
# Indicates that the notification was successfully sent to the corresponding
|
20
|
+
# registration ID but GCM sent a canonical ID for it, so the received canonical
|
21
|
+
# ID should be used as registration ID ASAP.
|
13
22
|
class GCMCanonicalIDResult < GCMResult
|
23
|
+
|
24
|
+
# @return [String]. The suggested canonical ID from GCM
|
14
25
|
attr_reader :canonical_id
|
15
26
|
|
16
27
|
def initialize(canonical_id)
|
@@ -23,7 +34,10 @@ module RubyPushNotifications
|
|
23
34
|
end
|
24
35
|
end
|
25
36
|
|
37
|
+
# An error occurred sending the notification to the registration ID.
|
26
38
|
class GCMResultError < GCMResult
|
39
|
+
|
40
|
+
# @return [String]. The error sent by GCM
|
27
41
|
attr_accessor :error
|
28
42
|
|
29
43
|
def initialize(error)
|
@@ -13,6 +13,8 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.homepage = 'https://github.com/calonso/ruby-push-notifications'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
|
16
|
+
spec.required_ruby_version = '>= 1.9.3'
|
17
|
+
|
16
18
|
spec.files = `git ls-files -z`.split("\x0")
|
17
19
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
20
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
|
2
|
+
require 'codeclimate-test-reporter'
|
3
|
+
CodeClimate::TestReporter.start
|
4
|
+
|
2
5
|
Bundler.setup
|
3
6
|
Bundler.require :defaults, :development
|
4
7
|
require 'webmock/rspec'
|
@@ -108,3 +111,5 @@ def apns_binary(json, token, id)
|
|
108
111
|
3, 4, id, 4, 4, (Time.now + RubyPushNotifications::APNS::APNSNotification::WEEKS_4).to_i, 5, 1, 10
|
109
112
|
].pack 'cNcnH64cna*cnNcnNcnc'
|
110
113
|
end
|
114
|
+
|
115
|
+
WebMock.disable_net_connect!(:allow => "codeclimate.com")
|
metadata
CHANGED
@@ -1,94 +1,83 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruby-push-notifications
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
5
|
-
prerelease:
|
4
|
+
version: 0.1.0
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Carlos Alonso
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2015-03-
|
11
|
+
date: 2015-03-30 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
14
|
name: bundler
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- - ~>
|
17
|
+
- - "~>"
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '1.6'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- - ~>
|
24
|
+
- - "~>"
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '1.6'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: rake
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- - ~>
|
31
|
+
- - "~>"
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '10.4'
|
38
34
|
type: :development
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- - ~>
|
38
|
+
- - "~>"
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '10.4'
|
46
41
|
- !ruby/object:Gem::Dependency
|
47
42
|
name: rspec
|
48
43
|
requirement: !ruby/object:Gem::Requirement
|
49
|
-
none: false
|
50
44
|
requirements:
|
51
|
-
- - ~>
|
45
|
+
- - "~>"
|
52
46
|
- !ruby/object:Gem::Version
|
53
47
|
version: '3.2'
|
54
48
|
type: :development
|
55
49
|
prerelease: false
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
57
|
-
none: false
|
58
51
|
requirements:
|
59
|
-
- - ~>
|
52
|
+
- - "~>"
|
60
53
|
- !ruby/object:Gem::Version
|
61
54
|
version: '3.2'
|
62
55
|
- !ruby/object:Gem::Dependency
|
63
56
|
name: factory_girl
|
64
57
|
requirement: !ruby/object:Gem::Requirement
|
65
|
-
none: false
|
66
58
|
requirements:
|
67
|
-
- - ~>
|
59
|
+
- - "~>"
|
68
60
|
- !ruby/object:Gem::Version
|
69
61
|
version: '4.0'
|
70
62
|
type: :development
|
71
63
|
prerelease: false
|
72
64
|
version_requirements: !ruby/object:Gem::Requirement
|
73
|
-
none: false
|
74
65
|
requirements:
|
75
|
-
- - ~>
|
66
|
+
- - "~>"
|
76
67
|
- !ruby/object:Gem::Version
|
77
68
|
version: '4.0'
|
78
69
|
- !ruby/object:Gem::Dependency
|
79
70
|
name: webmock
|
80
71
|
requirement: !ruby/object:Gem::Requirement
|
81
|
-
none: false
|
82
72
|
requirements:
|
83
|
-
- - ~>
|
73
|
+
- - "~>"
|
84
74
|
- !ruby/object:Gem::Version
|
85
75
|
version: '1.20'
|
86
76
|
type: :development
|
87
77
|
prerelease: false
|
88
78
|
version_requirements: !ruby/object:Gem::Requirement
|
89
|
-
none: false
|
90
79
|
requirements:
|
91
|
-
- - ~>
|
80
|
+
- - "~>"
|
92
81
|
- !ruby/object:Gem::Version
|
93
82
|
version: '1.20'
|
94
83
|
description: Easy to use gem to send iOS and Android Push notifications
|
@@ -98,17 +87,15 @@ executables: []
|
|
98
87
|
extensions: []
|
99
88
|
extra_rdoc_files: []
|
100
89
|
files:
|
101
|
-
- .gitignore
|
102
|
-
- .rspec
|
103
|
-
- .travis.yml
|
90
|
+
- ".gitignore"
|
91
|
+
- ".rspec"
|
92
|
+
- ".travis.yml"
|
104
93
|
- Gemfile
|
105
94
|
- Gemfile.lock
|
106
95
|
- LICENSE
|
107
96
|
- README.md
|
108
97
|
- Rakefile
|
109
|
-
- examples/apns copy.rb
|
110
98
|
- examples/apns.rb
|
111
|
-
- examples/gcm copy.rb
|
112
99
|
- examples/gcm.rb
|
113
100
|
- lib/ruby-push-notifications.rb
|
114
101
|
- lib/ruby-push-notifications/apns.rb
|
@@ -139,27 +126,26 @@ files:
|
|
139
126
|
homepage: https://github.com/calonso/ruby-push-notifications
|
140
127
|
licenses:
|
141
128
|
- MIT
|
129
|
+
metadata: {}
|
142
130
|
post_install_message:
|
143
131
|
rdoc_options: []
|
144
132
|
require_paths:
|
145
133
|
- lib
|
146
134
|
required_ruby_version: !ruby/object:Gem::Requirement
|
147
|
-
none: false
|
148
135
|
requirements:
|
149
|
-
- -
|
136
|
+
- - ">="
|
150
137
|
- !ruby/object:Gem::Version
|
151
|
-
version:
|
138
|
+
version: 1.9.3
|
152
139
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
|
-
none: false
|
154
140
|
requirements:
|
155
|
-
- -
|
141
|
+
- - ">="
|
156
142
|
- !ruby/object:Gem::Version
|
157
143
|
version: '0'
|
158
144
|
requirements: []
|
159
145
|
rubyforge_project:
|
160
|
-
rubygems_version:
|
146
|
+
rubygems_version: 2.2.2
|
161
147
|
signing_key:
|
162
|
-
specification_version:
|
148
|
+
specification_version: 4
|
163
149
|
summary: iOS and Android Push Notifications made easy!
|
164
150
|
test_files:
|
165
151
|
- spec/factories.rb
|
data/examples/apns copy.rb
DELETED
@@ -1,17 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
-
|
5
|
-
require 'ruby-push-notifications'
|
6
|
-
|
7
|
-
tokens = [
|
8
|
-
'6400b2a275eacf035d22d285d0ffb58fde5b647e54c7da882703136ba4ecd7d6',
|
9
|
-
'16400b2a275eacf035d22d285d0ffb58fde5b647e54c7da882703136ba4ecd7d6',
|
10
|
-
'6400b2a275eacf035d22d285d0ffb58fde5b647e54c7da882703136ba4ecd7d6'
|
11
|
-
]
|
12
|
-
|
13
|
-
notification = RubyPushNotifications::APNS::APNSNotification.new tokens, { aps: { alert: 'Hello APNS World!', sound: 'true', badge: 1 } }
|
14
|
-
|
15
|
-
pusher = RubyPushNotifications::APNS::APNSPusher.new(File.read('/Users/calonso/Desktop/apns.pem'), true)
|
16
|
-
pusher.push [notification]
|
17
|
-
p notification.results
|
data/examples/gcm copy.rb
DELETED
@@ -1,16 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
4
|
-
|
5
|
-
require 'ruby-push-notifications'
|
6
|
-
|
7
|
-
registration_ids = [
|
8
|
-
'APA91bFrtpS1bEo4BtTjE3V-GvRTX6KATZmh3ZGZ-wrQVY5UvsuQ4F-UmShwwjiG4uY0qtXG0RS4Tq2ir6t7gN6ziU7fpb1HtuiUKpdkY6WpE38mCxTa7cNeotIGgaKXOdNTEV10GU6Txp-Oxakqavuga6SrYNoVyA',
|
9
|
-
'APA91bFwufZkwhPhhLlQyIbM3MUksfxSvXXmXRP9L8LrJ8RMvUbRExERKHAzDR_pXZryKYICuqdS18fiytmks0WmKTZFd9_5AR8nK_m-5djqzM7AfBOyyv7Hy1uWCunJ2FcAbapGfaFYOTaW3MQGxjUIav_8Wj1R0OYANVMvZNGSDcu_j5wA80Y'
|
10
|
-
]
|
11
|
-
|
12
|
-
notification = RubyPushNotifications::GCM::GCMNotification.new registration_ids, { text: 'Hello GCM World!' }
|
13
|
-
|
14
|
-
pusher = RubyPushNotifications::GCM::GCMPusher.new 'AIzaSyAEO2CE_ipX217WwWsbHvGr8fiVHDdKUIc'
|
15
|
-
pusher.push [notification]
|
16
|
-
p notification.results
|