diff --git a/Gemfile.lock b/Gemfile.lock
index 6752759..d939bc4 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -6,19 +6,28 @@ PATH
GEM
remote: http://rubygems.org/
specs:
- diff-lcs (1.1.3)
- rspec (2.10.0)
- rspec-core (~> 2.10.0)
- rspec-expectations (~> 2.10.0)
- rspec-mocks (~> 2.10.0)
- rspec-core (2.10.1)
- rspec-expectations (2.10.0)
- diff-lcs (~> 1.1.3)
- rspec-mocks (2.10.1)
+ diff-lcs (1.3)
+ rspec (3.4.0)
+ rspec-core (~> 3.4.0)
+ rspec-expectations (~> 3.4.0)
+ rspec-mocks (~> 3.4.0)
+ rspec-core (3.4.3)
+ rspec-support (~> 3.4.0)
+ rspec-expectations (3.4.0)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.4.0)
+ rspec-mocks (3.4.1)
+ diff-lcs (>= 1.2.0, < 2.0)
+ rspec-support (~> 3.4.0)
+ rspec-support (3.4.1)
PLATFORMS
ruby
DEPENDENCIES
apns!
- rspec
+ rspec (~> 3.4.0)
+ rspec-mocks (~> 3.4.0)
+
+BUNDLED WITH
+ 1.13.6
diff --git a/README.textile b/README.textile
index b7f5d7f..e7a3900 100644
--- a/README.textile
+++ b/README.textile
@@ -25,9 +25,14 @@ After you have your .pem file. Set what host, port, certificate file location on
APNS.host = 'gateway.push.apple.com'
# gateway.sandbox.push.apple.com is default
- APNS.pem = '/path/to/pem/file'
- # this is the file you just created
-
+ APNS.pem = '/path/to/pem/file'
+ # these the file you just created
+
+ # or if you have more than one app
+ APNS.pems = { app1: '/path/to/pem/file/of/app1', app2: '/path/to/pem/file/of/app2' }
+ # actually it's identical to
+ APNS.pems = { default: '/path/to/pem/file' }
+
APNS.port = 2195
# this is also the default. Shouldn't ever have to set this, but just in case Apple goes crazy, you can.
@@ -41,9 +46,12 @@ Then to send a push notification you can either just send a string as the alert
device_token = '123abc456def'
- APNS.send_notification(device_token, 'Hello iPhone!' )
+ APNS.send_notification(:app1, device_token, 'Hello iPhone!')
- APNS.send_notification(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
+ APNS.send_notification(:app2, device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
+
+ # if you only have one app and uses APNS.pem = '/path/to/pem/file' to setup the pem, just do
+ APNS.send_notification(device_token, 'Hello iPhone!')
@@ -59,6 +67,9 @@ You can also send multiple notifications using the same connection to Apple:
n2 = APNS::Notification.new(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
+ APNS.send_notifications(:app1, [n1, n2])
+
+ # if you only have one app and uses APNS.pem = '/path/to/pem/file' to setup the pem, just do
APNS.send_notifications([n1, n2])
@@ -70,7 +81,7 @@ You can send other application specific information as well.
- APNS.send_notification(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default',
+ APNS.send_notification(:app1, device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default',
:other => {:sent => 'with apns gem'})
diff --git a/apns.gemspec b/apns.gemspec
index ec17887..579051c 100644
--- a/apns.gemspec
+++ b/apns.gemspec
@@ -21,6 +21,7 @@ DESC
s.rubygems_version = %q{1.3.5}
s.summary = %q{Simple Apple push notification service gem}
- s.add_development_dependency 'rspec'
+ s.add_development_dependency 'rspec', '~>3.4.0'
+ s.add_development_dependency 'rspec-mocks', '~>3.4.0'
end
diff --git a/lib/apns/core.rb b/lib/apns/core.rb
index c371d4e..17fe692 100644
--- a/lib/apns/core.rb
+++ b/lib/apns/core.rb
@@ -6,20 +6,44 @@ module APNS
@host = 'gateway.sandbox.push.apple.com'
@port = 2195
# openssl pkcs12 -in mycert.p12 -out client-cert.pem -nodes -clcerts
- @pem = nil # this should be the path of the pem file not the contentes
+ @pems = {}
+ # this should be the path of the pem file not the contentes
+ # Example:
+ # { some_ios_app : "file/to/pem" }
+
@pass = nil
class << self
- attr_accessor :host, :pem, :port, :pass
+ attr_accessor :host, :pems, :port, :pass
+ end
+
+ def self.pem=(default_pem_path)
+ @pems = { default: default_pem_path }
end
- def self.send_notification(device_token, message)
+ def self.send_notification(app_or_device_token, device_token_or_message, message = nil)
+ app, device_token, message = begin
+ if message.nil?
+ [:default, app_or_device_token, device_token_or_message]
+ else
+ [app_or_device_token, device_token_or_message, message]
+ end
+ end
+
n = APNS::Notification.new(device_token, message)
- self.send_notifications([n])
+ self.send_notifications(app, [n])
end
- def self.send_notifications(notifications)
- sock, ssl = self.open_connection
+ def self.send_notifications(app_or_notifications, notifications = nil)
+ app, notifications = begin
+ if notifications.nil?
+ [:default, app_or_notifications]
+ else
+ [app_or_notifications, notifications]
+ end
+ end
+
+ sock, ssl = self.open_connection(app)
packed_nofications = self.packed_nofications(notifications)
@@ -45,8 +69,8 @@ def self.packed_nofications(notifications)
bytes
end
- def self.feedback
- sock, ssl = self.feedback_connection
+ def self.feedback(app = :default)
+ sock, ssl = self.feedback_connection(app)
apns_feedback = []
@@ -63,36 +87,50 @@ def self.feedback
protected
- def self.open_connection
- raise "The path to your pem file is not set. (APNS.pem = /path/to/cert.pem)" unless self.pem
- raise "The path to your pem file does not exist!" unless File.exist?(self.pem)
+ def self.open_connection(app = :default)
+ pem = self.pems[app]
+ raise "The path to your pem file is not set. (APNS.pem = /path/to/cert.pem)" unless pem
+ raise "The path to your pem file does not exist!" unless File.exist?(pem)
- context = OpenSSL::SSL::SSLContext.new
- context.cert = OpenSSL::X509::Certificate.new(File.read(self.pem))
- context.key = OpenSSL::PKey::RSA.new(File.read(self.pem), self.pass)
+ context = build_context(pem)
- sock = TCPSocket.new(self.host, self.port)
- ssl = OpenSSL::SSL::SSLSocket.new(sock,context)
+ sock = build_socket(self.host, self.port)
+ ssl = build_ssl(context, sock)
ssl.connect
return sock, ssl
end
- def self.feedback_connection
- raise "The path to your pem file is not set. (APNS.pem = /path/to/cert.pem)" unless self.pem
- raise "The path to your pem file does not exist!" unless File.exist?(self.pem)
+ def self.feedback_connection(app = :default)
+ pem = self.pems[app]
+ raise "The path to your pem file is not set. (APNS.pem = /path/to/cert.pem)" unless pem
+ raise "The path to your pem file does not exist!" unless File.exist?(pem)
- context = OpenSSL::SSL::SSLContext.new
- context.cert = OpenSSL::X509::Certificate.new(File.read(self.pem))
- context.key = OpenSSL::PKey::RSA.new(File.read(self.pem), self.pass)
+ context = build_context(pem)
fhost = self.host.gsub('gateway','feedback')
puts fhost
- sock = TCPSocket.new(fhost, 2196)
- ssl = OpenSSL::SSL::SSLSocket.new(sock,context)
+ sock = build_socket(fhost, 2196)
+ ssl = build_ssl(context, sock)
ssl.connect
return sock, ssl
end
+
+ def self.build_context(pem)
+ context = OpenSSL::SSL::SSLContext.new
+ pem_content = File.read(pem)
+ context.cert = OpenSSL::X509::Certificate.new(pem_content)
+ context.key = OpenSSL::PKey::RSA.new(pem_content, self.pass)
+ context
+ end
+
+ def self.build_socket(host, port)
+ TCPSocket.new(host, port)
+ end
+
+ def self.build_ssl(context, socket)
+ OpenSSL::SSL::SSLSocket.new(socket,context)
+ end
end
diff --git a/spec/apns/core_spec.rb b/spec/apns/core_spec.rb
new file mode 100644
index 0000000..ce469c3
--- /dev/null
+++ b/spec/apns/core_spec.rb
@@ -0,0 +1,130 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe APNS do
+ before do
+ ::APNS.host = "gateway.abc.com"
+ ::APNS.port = 1234
+ allow(File).to receive(:exist?) { true }
+ end
+
+ describe ".send_notifications" do
+ it "accepts default app and pem" do
+ ::APNS.pem = 'default_pem'
+
+ n1 = APNS::Notification.new("token1", "message1")
+ n2 = APNS::Notification.new("token1", "message2")
+
+ context = OpenStruct.new
+ socket = OpenStruct.new
+ ssl = OpenStruct.new
+
+ expect(socket).to receive(:close)
+ expect(ssl).to receive(:close)
+ expect(ssl).to receive(:write).twice
+
+ expect(APNS).to receive(:build_context).with('default_pem') { context }
+ expect(APNS).to receive(:build_socket).with(::APNS.host, ::APNS.port) { socket }
+ expect(APNS).to receive(:build_ssl).with(context, socket) { ssl }
+
+ ::APNS.send_notifications([n1, n2])
+ end
+
+
+ it "accepts an app key" do
+ ::APNS.pems = { app1: 'cert1'}
+
+ n1 = APNS::Notification.new("token1", "message1")
+ n2 = APNS::Notification.new("token1", "message2")
+
+ context = OpenStruct.new
+ socket = OpenStruct.new
+ ssl = OpenStruct.new
+
+ expect(socket).to receive(:close)
+ expect(ssl).to receive(:close)
+ expect(ssl).to receive(:write).twice
+
+ expect(APNS).to receive(:build_context).with('cert1') { context }
+ expect(APNS).to receive(:build_socket).with(::APNS.host, ::APNS.port) { socket }
+ expect(APNS).to receive(:build_ssl).with(context, socket) { ssl }
+
+ ::APNS.send_notifications(:app1, [n1, n2])
+ end
+ end
+
+ describe ".send_notification" do
+ it "accepts default app and pem" do
+ ::APNS.pem = 'default_pem'
+
+ context = OpenStruct.new
+ socket = OpenStruct.new
+ ssl = OpenStruct.new
+
+ expect(socket).to receive(:close)
+ expect(ssl).to receive(:close)
+ expect(ssl).to receive(:write)
+
+ expect(APNS).to receive(:build_context).with('default_pem') { context }
+ expect(APNS).to receive(:build_socket).with(::APNS.host, ::APNS.port) { socket }
+ expect(APNS).to receive(:build_ssl).with(context, socket) { ssl }
+
+ ::APNS.send_notification("device_token1", message: "message", otherstuff: "others")
+ end
+
+ it "accepts an app key" do
+ ::APNS.pems = { app1: 'cert1'}
+
+ context = OpenStruct.new
+ socket = OpenStruct.new
+ ssl = OpenStruct.new
+
+ expect(socket).to receive(:close)
+ expect(ssl).to receive(:close)
+ expect(ssl).to receive(:write)
+
+ expect(APNS).to receive(:build_context).with('cert1') { context }
+ expect(APNS).to receive(:build_socket).with(::APNS.host, ::APNS.port) { socket }
+ expect(APNS).to receive(:build_ssl).with(context, socket) { ssl }
+
+ ::APNS.send_notification(:app1, "device_token1", "message1")
+ end
+ end
+
+ describe ".feedback" do
+ it "accept default app and pem" do
+ ::APNS.pem = 'default_pem'
+
+ context = OpenStruct.new
+ socket = OpenStruct.new
+ ssl = OpenStruct.new
+
+ expect(socket).to receive(:close)
+ expect(ssl).to receive(:read).with(38)
+ expect(ssl).to receive(:close)
+
+ expect(APNS).to receive(:build_context).with('default_pem') { context }
+ expect(APNS).to receive(:build_socket).with("feedback.abc.com", 2196) { socket }
+ expect(APNS).to receive(:build_ssl).with(context, socket) { ssl }
+
+ ::APNS.feedback
+ end
+
+ it "accepts an app key" do
+ ::APNS.pems = { app1: 'cert1'}
+
+ context = OpenStruct.new
+ socket = OpenStruct.new
+ ssl = OpenStruct.new
+
+ expect(socket).to receive(:close)
+ expect(ssl).to receive(:read).with(38)
+ expect(ssl).to receive(:close)
+
+ expect(APNS).to receive(:build_context).with('cert1') { context }
+ expect(APNS).to receive(:build_socket).with("feedback.abc.com", 2196) { socket }
+ expect(APNS).to receive(:build_ssl).with(context, socket) { ssl }
+
+ ::APNS.feedback(:app1)
+ end
+ end
+end
diff --git a/spec/apns/notification_spec.rb b/spec/apns/notification_spec.rb
index b9ab67e..d7c6b90 100644
--- a/spec/apns/notification_spec.rb
+++ b/spec/apns/notification_spec.rb
@@ -15,7 +15,7 @@
it "should have a priority if content_availible is set" do
n = APNS::Notification.new('device_token', {:content_available => true})
- n.content_available.should be_true
+ n.content_available.should be_truthy
n.priority.should eql(5)
end
@@ -48,7 +48,7 @@
describe '#packaged_notification' do
it "should package the token" do
n = APNS::Notification.new('device_token', {:alert => 'Hello iPhone', :badge => 3, :sound => 'awesome.caf'})
- n.stub!(:message_identifier).and_return('aaaa') # make sure the message_identifier is not random
+ allow(n).to receive(:message_identifier).and_return('aaaa') # make sure the message_identifier is not random
Base64.encode64(n.packaged_notification).should == "AQAG3vLO/YTnAgBAeyJhcHMiOnsiYWxlcnQiOiJIZWxsbyBpUGhvbmUiLCJi\nYWRnZSI6Mywic291bmQiOiJhd2Vzb21lLmNhZiJ9fQMABGFhYWEEAAQAAAAA\nBQABCg==\n"
end
end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 9edde4a..33d8c2e 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -1,5 +1,6 @@
require 'rubygems'
gem 'rspec', '>= 1.2.8'
require 'rspec'
+require 'rspec/mocks'
require File.join(File.dirname(__FILE__), '..', 'lib', 'apns')
require 'base64'