ruby-push-notifications 0.0.1 → 0.1.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 +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
|
+
[](https://travis-ci.org/calonso/ruby-push-notifications) [](https://gemnasium.com/calonso/ruby-push-notifications) [](https://codeclimate.com/github/calonso/ruby-push-notifications) [](https://codeclimate.com/github/calonso/ruby-push-notifications) [](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
|