Skip to content

Commit 9f06349

Browse files
committed
Send 1 message to many devices + Allow overriding general configuration credentials + Internal refactoring.
1 parent e7c0f62 commit 9f06349

25 files changed

+554
-312
lines changed

Gemfile.lock

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
PATH
2+
remote: .
3+
specs:
4+
pntfr (0.2.0)
5+
apns (~> 1.0, >= 1.0.0)
6+
gcm (~> 0.0, >= 0.0.9)
7+
json (~> 1.8, >= 1.8.1)
8+
9+
GEM
10+
remote: https://rubygems.org/
11+
specs:
12+
apns (1.0.0)
13+
gcm (0.1.0)
14+
httparty
15+
json
16+
httparty (0.13.4)
17+
json (~> 1.8)
18+
multi_xml (>= 0.5.2)
19+
json (1.8.2)
20+
minitest (5.7.0)
21+
multi_xml (0.5.5)
22+
rake (10.4.2)
23+
24+
PLATFORMS
25+
ruby
26+
27+
DEPENDENCIES
28+
bundler (~> 1.6)
29+
minitest (~> 5.7.0)
30+
pntfr!
31+
rake

lib/pntfr/notification.rb

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Encapsulates the content of a notification in a neutral maneer.
2+
# For each platform it can return the notification in the corresponding format
3+
# using #to_platform like methods (i.e. #to_ios)
4+
#
5+
6+
module Pntfr
7+
class Notification
8+
FILE_EXTENSION_REGEXP= /(\.[^\.]+)\z/
9+
10+
def initialize content, custom=nil
11+
@content= content
12+
@custom= custom
13+
end
14+
15+
def to_ios
16+
alert= @content[:title]
17+
alert+= "\n#{@content[:description]}" if @content.key?(:description)
18+
sound= @content[:sound]
19+
badge= @content[:badge]
20+
badge= @content[:badge].to_i if badge
21+
n= {:alert => alert, :sound => 'default'}
22+
n[:sound]= sound if sound
23+
n[:badge]= badge if badge
24+
n.merge!({other: {custom: @custom}}) if @custom
25+
n
26+
end
27+
28+
def to_android
29+
data= {
30+
:title => @content[:title],
31+
:description => @content[:description],
32+
}
33+
data[:sound]= remove_extension(@content[:sound]) unless @content[:sound].nil?
34+
data[:custom]= @custom if @custom
35+
data
36+
end
37+
def remove_extension filename
38+
filename.gsub(FILE_EXTENSION_REGEXP, '')
39+
end
40+
41+
end
42+
end

lib/pntfr/notifier.rb

Lines changed: 85 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,94 @@
1-
require File.dirname(__FILE__) + '/virtual_session/base'
1+
require File.dirname(__FILE__) + '/session/base'
2+
require File.dirname(__FILE__) + '/notification'
23

34
module Pntfr
45

56
class Notifier
6-
def self.to session
7-
if Platforms.android? session.platform
8-
Pntfr::VirtualSession::Android.new(session)
9-
elsif Platforms.ios? session.platform
10-
Pntfr::VirtualSession::Ios.new(session)
7+
def self.to devices, credentials=nil
8+
notif= Notifier.new(credentials)
9+
notif.update_devices(devices)
10+
notif
11+
end
12+
13+
attr_reader :andr_responses
14+
attr_reader :ios_responses
15+
16+
#
17+
# +credentials+ is a hash with 2 keys :andr and :ios, each one with its
18+
# given credentials:
19+
# - :andr => a string with the notification_key
20+
# - :ios => a hash of the form {
21+
# host: 'test-host',
22+
# pem: 'test-pem',
23+
# port: 'test-port',
24+
# pass: 'test-password',
25+
# }
26+
#
27+
def initialize credentials=nil
28+
validate_credentials(credentials)
29+
@credentials= credentials || {}
30+
31+
@andr_responses=[]
32+
@ios_responses= []
33+
end
34+
35+
def update_devices(devices)
36+
devices= [devices] unless devices.kind_of?(Array)
37+
38+
# the list of ANDROID push_id to send the notification to
39+
@andr_ids= []
40+
@ios_devices= []
41+
devices.each do |device|
42+
if Platforms.android? device.platform
43+
@andr_ids << device.push_id
44+
elsif Platforms.ios? device.platform
45+
@ios_devices << device
46+
end
47+
end
48+
self
49+
end
50+
51+
def msg content, custom=nil
52+
@msg= Notification.new(content, custom)
53+
self
54+
end
55+
56+
def notify
57+
if any_android_device?
58+
@andr_responses << andr_session.notify(@andr_ids, @msg.to_android)
59+
end
60+
if any_ios_device?
61+
@ios_responses << ios_session.notify(@ios_devices, @msg.to_ios)
62+
end
63+
end
64+
65+
def any_android_device?
66+
@andr_ids.any?
67+
end
68+
69+
def any_ios_device?
70+
@ios_devices.any?
71+
end
72+
73+
#--------------------------------------------
74+
private
75+
#--------------------------------------------
76+
def andr_session
77+
@andr_session||= Pntfr::Session::Android.new(@credentials[:andr])
78+
end
79+
def ios_session
80+
@ios_session||= Pntfr::Session::Ios.new(@credentials[:ios])
81+
end
82+
def validate_credentials(credentials)
83+
return if credentials.nil?
84+
if !credentials.is_a?(Hash)
85+
raise ArgumentError.new('Credentials should be a Hash with either :andr or :ios keys!')
86+
end
87+
if !(credentials.has_key?(:andr) or credentials.has_key?(:ios))
88+
raise ArgumentError.new('Either :andr or :ios service credentials should have been provided!')
1189
end
1290
end
91+
1392
end
1493

1594
end

lib/pntfr/session/android.rb

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#
2+
# Session implementation for Android
3+
#
4+
require 'gcm'
5+
module Pntfr
6+
module Session
7+
class Android < Pntfr::Session::Base
8+
9+
attr_reader :gcm
10+
11+
def initialize notification_key=nil
12+
if notification_key.nil?
13+
notification_key= Pntfr.config.gcm[:notification_key]
14+
end
15+
@gcm= ::GCM.new(notification_key)
16+
end
17+
18+
def notify(push_ids, data)
19+
options = {data: data}
20+
if Pntfr.test_env?
21+
push_ids.each { |push_id| Pntfr.add_delivery(push_id, options) }
22+
Session::SuccessResponse.new
23+
else
24+
rs= @gcm.send_notification(push_ids, options)
25+
parse_response rs
26+
end
27+
end
28+
29+
#---------------------------------------------------------
30+
private
31+
#---------------------------------------------------------
32+
def parse_response rs
33+
Session::GcmResponse.new(rs)
34+
end
35+
36+
end
37+
end
38+
end

lib/pntfr/session/base.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
module Pntfr
2+
module Session
3+
class Base
4+
def initialize devices
5+
@devices= devices
6+
end
7+
def msg content
8+
raise 'not implemented!'
9+
end
10+
def notify(push_ids, data)
11+
raise 'not implemented!'
12+
end
13+
end
14+
end
15+
end
16+
require 'pntfr/session/success_response'
17+
require 'pntfr/session/android'
18+
require 'pntfr/session/gcm_response'
19+
require 'pntfr/session/ios'

lib/pntfr/virtual_session/base_response.rb renamed to lib/pntfr/session/base_response.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
module Pntfr
2-
module VirtualSession
2+
module Session
33
class BaseResponse
44
# communication with notification service was OK?
55
# implies no +failure?+
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
#
22
# Google Cloud Messaging json response wrapper.
33
#
4-
require 'pntfr/virtual_session/base_response'
4+
require 'pntfr/session/base_response'
55
require 'json'
66

77
module Pntfr
8-
module VirtualSession
8+
module Session
99
class GcmResponse < BaseResponse
1010
def initialize json
1111
@raw= json

lib/pntfr/session/ios.rb

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
#
2+
# Session implementation for iOS
3+
#
4+
require 'apns'
5+
module Pntfr
6+
module Session
7+
class Ios < Pntfr::Session::Base
8+
9+
attr_reader :apns
10+
11+
def initialize apns_config=nil
12+
if apns_config.nil?
13+
configure_apns(Pntfr.config.apns)
14+
else
15+
configure_apns(apns_config)
16+
end
17+
end
18+
19+
def notify devices, notification
20+
apns_notifications= []
21+
devices.each do |device|
22+
if device_controls_badge(device)
23+
if !notification[:badge].nil?
24+
device.num_notifs= notification[:badge]
25+
else
26+
device.num_notifs+= 1
27+
notification[:badge]= device.num_notifs
28+
end
29+
end
30+
if Pntfr.test_env?
31+
Pntfr.add_delivery(device.push_id, notification)
32+
else
33+
apns_notif= APNS::Notification.new(device.push_id, notification)
34+
apns_notifications << apns_notif
35+
end
36+
end
37+
::APNS.send_notifications(apns_notifications) if apns_notifications.any?
38+
SuccessResponse.new
39+
end
40+
41+
#-------------------------------------------------
42+
private
43+
#-------------------------------------------------
44+
45+
def device_controls_badge(device)
46+
device.methods.include?(:num_notifs)
47+
end
48+
49+
def configure_apns config
50+
APNS.host = config[:host]
51+
APNS.pem = config[:pem]
52+
APNS.port = config[:port]
53+
APNS.pass = config[:pass]
54+
@apns= APNS
55+
end
56+
end
57+
end
58+
end

lib/pntfr/virtual_session/success_response.rb renamed to lib/pntfr/session/success_response.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
# Always success.
33
# To be used on cases where no response to be parsed is received.
44
#
5-
require 'pntfr/virtual_session/base_response'
5+
require 'pntfr/session/base_response'
66

77
module Pntfr
8-
module VirtualSession
8+
module Session
99
class SuccessResponse < BaseResponse
1010
# communication with notification service was OK?
1111
# implies no +failure?+

lib/pntfr/version.rb

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
module Pntfr
2-
# For IOS do not prefix custom fields with 'acme-' in the notification,
3-
# instead group them all into a :custom key, like in Android.
42
#
5-
# As this change doesn't break the gems API but changes device's management of
6-
# received notifications, lets change only the minor version. This will also
7-
# reflect the adding of the custom keys feature.
8-
VERSION = '0.2.0'
3+
# - Performance improvement: Allow sending one message to many devices in one
4+
# single call (on both platforms).
5+
# - Allow overriding general configuration credentials when instantiating each
6+
# Notifier (on both platforms).
7+
# - Internal refactoring.
8+
#
9+
# As this change don't break the gem's API (it extends it), lets change only
10+
# minor version.
11+
VERSION = '0.3.0'
912
end

0 commit comments

Comments
 (0)