pling 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/pling/gcm/gateway.rb +87 -0
- data/lib/pling/gcm.rb +21 -0
- data/lib/pling/version.rb +1 -1
- data/lib/pling.rb +1 -0
- data/pling.gemspec +1 -0
- data/spec/pling/gcm/gateway_spec.rb +186 -0
- metadata +58 -13
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'faraday_middleware'
|
3
|
+
require 'json'
|
4
|
+
|
5
|
+
module Pling
|
6
|
+
module GCM
|
7
|
+
##
|
8
|
+
# Pling gateway to communicate with Google's Android GCM service.
|
9
|
+
#
|
10
|
+
# The gateway is implemented using Faraday. It defaults to Faraday's :net_http adapter.
|
11
|
+
# You can customize the adapter by passing the :adapter configuration.
|
12
|
+
#
|
13
|
+
# @example
|
14
|
+
#
|
15
|
+
# Pling::GCM::Gateway.new({
|
16
|
+
# :key => 'your-api-key', # Your google account's api key (Required)
|
17
|
+
# :push_url => 'http://...', # The push url to use (Optional, Default: GCM default authentication url)
|
18
|
+
# :adapter => :net_http, # The Faraday adapter you want to use (Optional, Default: :net_http)
|
19
|
+
# :connection => {} # Options you want to pass to Faraday (Optional, Default: {})
|
20
|
+
# })
|
21
|
+
class Gateway < Pling::Gateway
|
22
|
+
|
23
|
+
handles :android, :gcm
|
24
|
+
|
25
|
+
##
|
26
|
+
# Initializes a new gateway to Apple's Push Notification service
|
27
|
+
#
|
28
|
+
# @param [Hash] configuration
|
29
|
+
# @option configuration [String] :key Your google account's api key (Required)
|
30
|
+
# @option configuration [String] :push_url The URL to push to (Optional)
|
31
|
+
# @option configuration [Symbol] :adapter The Faraday adapter to use (Optional)
|
32
|
+
# @option configuration [String] :connection Any options for Faraday (Optional)
|
33
|
+
# @raise Pling::AuthenticationFailed
|
34
|
+
def initialize(configuration)
|
35
|
+
super
|
36
|
+
require_configuration([:key])
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Sends the given message to the given device.
|
41
|
+
#
|
42
|
+
# @param [#to_pling_message] message
|
43
|
+
# @param [#to_pling_device] device
|
44
|
+
# @raise Pling::DeliveryFailed
|
45
|
+
def deliver!(message, device)
|
46
|
+
data = {
|
47
|
+
:registration_ids => [device.identifier],
|
48
|
+
:data => {
|
49
|
+
:body => message.body,
|
50
|
+
:badge => message.badge,
|
51
|
+
:sound => message.sound,
|
52
|
+
:subject => message.subject
|
53
|
+
}.delete_if { |_, value| value.nil? },
|
54
|
+
:collapse_key => "collapse-#{message.body.hash}"
|
55
|
+
}
|
56
|
+
|
57
|
+
data[:data].merge!(message.payload) if configuration[:payload] && message.payload
|
58
|
+
|
59
|
+
response = connection.post(configuration[:push_url], data, { :Authorization => "key=#{configuration[:key]}"})
|
60
|
+
|
61
|
+
if !response.success? || response.body['failure'].to_i > 0
|
62
|
+
error_class = Pling::GCM.const_get(response.body['results'][0]['error']) rescue Pling::DeliveryFailed
|
63
|
+
raise error_class.new("GCM Delivery failed: [#{response.status}] #{response.body}", message, device)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def default_configuration
|
70
|
+
super.merge({
|
71
|
+
:push_url => 'https://android.googleapis.com/gcm/send',
|
72
|
+
:adapter => :net_http,
|
73
|
+
:connection => {}
|
74
|
+
})
|
75
|
+
end
|
76
|
+
|
77
|
+
def connection
|
78
|
+
@connection ||= Faraday.new(configuration[:connection]) do |builder|
|
79
|
+
builder.use FaradayMiddleware::EncodeJson
|
80
|
+
builder.use FaradayMiddleware::ParseJson, :content_type => /\bjson$/
|
81
|
+
builder.use Faraday::Response::Logger if configuration[:debug]
|
82
|
+
builder.adapter(configuration[:adapter])
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/lib/pling/gcm.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
module Pling
|
2
|
+
##
|
3
|
+
# This module adds support for Google Cloud Messaging for Android (GCM) to pling.
|
4
|
+
# Please refer to {Pling::GCM::Gateway} for documentation.
|
5
|
+
#
|
6
|
+
# @see Pling::GCM::Gateway
|
7
|
+
module GCM
|
8
|
+
autoload :Gateway, 'pling/gcm/gateway'
|
9
|
+
|
10
|
+
class MissingRegistration < Pling::DeliveryFailed; end
|
11
|
+
class InvalidRegistration < Pling::DeliveryFailed; end
|
12
|
+
class MismatchSenderId < Pling::DeliveryFailed; end
|
13
|
+
class NotRegistered < Pling::DeliveryFailed; end
|
14
|
+
class MessageTooBig < Pling::DeliveryFailed; end
|
15
|
+
class InvalidTtl < Pling::DeliveryFailed; end
|
16
|
+
class Unavailable < Pling::DeliveryFailed; end
|
17
|
+
class InternalServerError < Pling::DeliveryFailed; end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
|
data/lib/pling/version.rb
CHANGED
data/lib/pling.rb
CHANGED
@@ -8,6 +8,7 @@ module Pling
|
|
8
8
|
autoload :Gateway, 'pling/gateway'
|
9
9
|
autoload :APN, 'pling/apn'
|
10
10
|
autoload :C2DM, 'pling/c2dm'
|
11
|
+
autoload :GCM, 'pling/gcm'
|
11
12
|
autoload :Middleware, 'pling/middleware'
|
12
13
|
autoload :Adapter, 'pling/adapter'
|
13
14
|
autoload :Configurable, 'pling/configurable'
|
data/pling.gemspec
CHANGED
@@ -17,6 +17,7 @@ Gem::Specification.new do |s|
|
|
17
17
|
s.require_paths = ["lib"]
|
18
18
|
|
19
19
|
s.add_runtime_dependency "faraday", "~> 0.7"
|
20
|
+
s.add_runtime_dependency "faraday_middleware", "~> 0.8"
|
20
21
|
s.add_runtime_dependency "json", "~> 1.4"
|
21
22
|
s.add_runtime_dependency("jruby-openssl") if RUBY_PLATFORM == 'java'
|
22
23
|
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Pling::GCM::Gateway do
|
4
|
+
|
5
|
+
let(:valid_configuration) do
|
6
|
+
{ :key => 'some-key' }
|
7
|
+
end
|
8
|
+
|
9
|
+
let(:push_response_mock) do
|
10
|
+
mock('Faraday send response', :success? => true, :status => 200, :body => "")
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:connection_mock) do
|
14
|
+
mock('Faraday connection').tap do |mock|
|
15
|
+
mock.stub(:post).
|
16
|
+
with('https://android.googleapis.com/gcm/send', anything, anything).
|
17
|
+
and_return(push_response_mock)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:message) { Pling::Message.new('Hello from Pling') }
|
22
|
+
let(:device) { Pling::Device.new(:identifier => 'DEVICEIDENTIFIER', :type => :android) }
|
23
|
+
|
24
|
+
|
25
|
+
before { Faraday.stub(:new).and_return(connection_mock) }
|
26
|
+
|
27
|
+
it 'should handle various apn related device types' do
|
28
|
+
Pling::GCM::Gateway.handled_types.should =~ [:android, :gcm]
|
29
|
+
end
|
30
|
+
|
31
|
+
context 'when created with an invalid configuration' do
|
32
|
+
[:key].each do |attribute|
|
33
|
+
it "should raise an error when :#{attribute} is missing" do
|
34
|
+
configuration = valid_configuration
|
35
|
+
configuration.delete(attribute)
|
36
|
+
expect { Pling::GCM::Gateway.new(configuration) }.to raise_error(ArgumentError, /:#{attribute} is missing/)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
context 'when created with valid configuration' do
|
42
|
+
|
43
|
+
context 'configuration' do
|
44
|
+
it 'should allow configuring Faraday\'s :connection settings' do
|
45
|
+
Faraday.should_receive(:new).with(:ssl => { :verify => false })
|
46
|
+
Pling::GCM::Gateway.new(valid_configuration.merge(:connection => { :ssl => { :verify => false }})).deliver(message, device)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should use Faraday::Response::Logger when :debug is set to true' do
|
50
|
+
builder = mock(:use => true, :adapter => true)
|
51
|
+
builder.should_receive(:use).with(Faraday::Response::Logger)
|
52
|
+
Faraday.stub(:new).and_yield(builder).and_return(connection_mock)
|
53
|
+
|
54
|
+
Pling::GCM::Gateway.new(valid_configuration.merge(:debug => true)).deliver(message, device)
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'should use the adapter set with :adapter' do
|
58
|
+
builder = mock(:use => true, :adapter => true)
|
59
|
+
builder.should_receive(:adapter).with(:typheus)
|
60
|
+
Faraday.stub(:new).and_yield(builder).and_return(connection_mock)
|
61
|
+
|
62
|
+
Pling::GCM::Gateway.new(valid_configuration.merge(:adapter => :typheus)).deliver(message, device)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '#deliver' do
|
69
|
+
subject { Pling::GCM::Gateway.new(valid_configuration) }
|
70
|
+
|
71
|
+
let(:valid_push_params) do
|
72
|
+
{
|
73
|
+
:registration_ids => [device.identifier],
|
74
|
+
:data => { :body => message.body },
|
75
|
+
:collapse_key => "collapse-#{message.body.hash}"
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
let(:valid_push_headers) do
|
80
|
+
{
|
81
|
+
:Authorization => 'key=some-key'
|
82
|
+
}
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should raise an error if no message is given' do
|
86
|
+
expect { subject.deliver(nil, device) }.to raise_error
|
87
|
+
end
|
88
|
+
|
89
|
+
it 'should raise an error the device is given' do
|
90
|
+
expect { subject.deliver(message, nil) }.to raise_error
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should call #to_pling_message on the given message' do
|
94
|
+
message.should_receive(:to_pling_message).and_return(message)
|
95
|
+
subject.deliver(message, device)
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'should call #to_pling_device on the given device' do
|
99
|
+
device.should_receive(:to_pling_device).and_return(device)
|
100
|
+
subject.deliver(message, device)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should try to deliver the given message' do
|
104
|
+
connection_mock.should_receive(:post).
|
105
|
+
with('https://android.googleapis.com/gcm/send', valid_push_params, valid_push_headers).
|
106
|
+
and_return(push_response_mock)
|
107
|
+
|
108
|
+
subject.deliver(message, device)
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'should raise a Pling::DeliveryFailed exception if the delivery was not successful' do
|
112
|
+
connection_mock.should_receive(:post).and_return(push_response_mock)
|
113
|
+
push_response_mock.stub(:status => 401, :success? => false, :body => "Something went wrong")
|
114
|
+
|
115
|
+
expect { subject.deliver(message, device) }.to raise_error Pling::DeliveryFailed, /Something went wrong/
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'should raise a Pling::DeliveryFailed exception if the response body contained an error' do
|
119
|
+
connection_mock.should_receive(:post).and_return(push_response_mock)
|
120
|
+
push_response_mock.stub(:status => 200, :success? => true, :body => { 'failure' => 1, 'results' => ['error' => 'SomeError'] })
|
121
|
+
|
122
|
+
expect { subject.deliver(message, device) }.to raise_error Pling::DeliveryFailed, /SomeError/
|
123
|
+
end
|
124
|
+
|
125
|
+
it 'should send data.badge if the given message has a badge' do
|
126
|
+
connection_mock.should_receive(:post).
|
127
|
+
with(anything, hash_including(:data => hash_including({ :badge => '10' })), anything).
|
128
|
+
and_return(push_response_mock)
|
129
|
+
message.badge = 10
|
130
|
+
subject.deliver(message, device)
|
131
|
+
end
|
132
|
+
|
133
|
+
it 'should send data.sound if the given message has a sound' do
|
134
|
+
connection_mock.should_receive(:post).
|
135
|
+
with(anything, hash_including(:data => hash_including({ :sound => 'pling' })), anything).
|
136
|
+
and_return(push_response_mock)
|
137
|
+
message.sound = :pling
|
138
|
+
subject.deliver(message, device)
|
139
|
+
end
|
140
|
+
|
141
|
+
it 'should send data.subject if the given message has a subject' do
|
142
|
+
connection_mock.should_receive(:post).
|
143
|
+
with(anything, hash_including(:data => hash_including({ :subject => 'Important!' })), anything).
|
144
|
+
and_return(push_response_mock)
|
145
|
+
message.subject = 'Important!'
|
146
|
+
subject.deliver(message, device)
|
147
|
+
end
|
148
|
+
|
149
|
+
context 'when configured to include payload' do
|
150
|
+
before do
|
151
|
+
valid_configuration.merge!(:payload => true)
|
152
|
+
message.payload = { :data => 'available' }
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'should include the given payload' do
|
156
|
+
connection_mock.should_receive(:post).
|
157
|
+
with(anything, hash_including(:data => hash_including({ :data => 'available' })), anything).
|
158
|
+
and_return(push_response_mock)
|
159
|
+
subject.deliver(message, device)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
context 'when configured to not include payload' do
|
164
|
+
before do
|
165
|
+
valid_configuration.merge!(:payload => false)
|
166
|
+
message.payload = { :data => 'available' }
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'should include the given payload' do
|
170
|
+
connection_mock.should_receive(:post).
|
171
|
+
with(anything, hash_not_including(:'data.data' => 'available'), anything).
|
172
|
+
and_return(push_response_mock)
|
173
|
+
subject.deliver(message, device)
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
[:MissingRegistration, :InvalidRegistration, :MismatchSenderId,
|
178
|
+
:NotRegistered, :MessageTooBig, :InvalidTtl, :Unavailable,
|
179
|
+
:InternalServerError].each do |exception|
|
180
|
+
it "should raise a Pling::GCM::#{exception} when the response body is ''" do
|
181
|
+
push_response_mock.stub(:body => { 'failure' => 1, 'results' => [{ 'error' => exception }]})
|
182
|
+
expect { subject.deliver(message, device) }.to raise_error Pling::GCM.const_get(exception)
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: pling
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -11,11 +11,11 @@ authors:
|
|
11
11
|
autorequire:
|
12
12
|
bindir: bin
|
13
13
|
cert_chain: []
|
14
|
-
date:
|
14
|
+
date: 2012-09-10 00:00:00.000000000 Z
|
15
15
|
dependencies:
|
16
16
|
- !ruby/object:Gem::Dependency
|
17
17
|
name: faraday
|
18
|
-
requirement:
|
18
|
+
requirement: !ruby/object:Gem::Requirement
|
19
19
|
none: false
|
20
20
|
requirements:
|
21
21
|
- - ~>
|
@@ -23,10 +23,31 @@ dependencies:
|
|
23
23
|
version: '0.7'
|
24
24
|
type: :runtime
|
25
25
|
prerelease: false
|
26
|
-
version_requirements:
|
26
|
+
version_requirements: !ruby/object:Gem::Requirement
|
27
|
+
none: false
|
28
|
+
requirements:
|
29
|
+
- - ~>
|
30
|
+
- !ruby/object:Gem::Version
|
31
|
+
version: '0.7'
|
32
|
+
- !ruby/object:Gem::Dependency
|
33
|
+
name: faraday_middleware
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
35
|
+
none: false
|
36
|
+
requirements:
|
37
|
+
- - ~>
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0.8'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
none: false
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0.8'
|
27
48
|
- !ruby/object:Gem::Dependency
|
28
49
|
name: json
|
29
|
-
requirement:
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
30
51
|
none: false
|
31
52
|
requirements:
|
32
53
|
- - ~>
|
@@ -34,10 +55,15 @@ dependencies:
|
|
34
55
|
version: '1.4'
|
35
56
|
type: :runtime
|
36
57
|
prerelease: false
|
37
|
-
version_requirements:
|
58
|
+
version_requirements: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ~>
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '1.4'
|
38
64
|
- !ruby/object:Gem::Dependency
|
39
65
|
name: rspec
|
40
|
-
requirement:
|
66
|
+
requirement: !ruby/object:Gem::Requirement
|
41
67
|
none: false
|
42
68
|
requirements:
|
43
69
|
- - ~>
|
@@ -45,10 +71,15 @@ dependencies:
|
|
45
71
|
version: '2.7'
|
46
72
|
type: :development
|
47
73
|
prerelease: false
|
48
|
-
version_requirements:
|
74
|
+
version_requirements: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ~>
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
version: '2.7'
|
49
80
|
- !ruby/object:Gem::Dependency
|
50
81
|
name: yard
|
51
|
-
requirement:
|
82
|
+
requirement: !ruby/object:Gem::Requirement
|
52
83
|
none: false
|
53
84
|
requirements:
|
54
85
|
- - ! '>='
|
@@ -56,10 +87,15 @@ dependencies:
|
|
56
87
|
version: '0.7'
|
57
88
|
type: :development
|
58
89
|
prerelease: false
|
59
|
-
version_requirements:
|
90
|
+
version_requirements: !ruby/object:Gem::Requirement
|
91
|
+
none: false
|
92
|
+
requirements:
|
93
|
+
- - ! '>='
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0.7'
|
60
96
|
- !ruby/object:Gem::Dependency
|
61
97
|
name: rake
|
62
|
-
requirement:
|
98
|
+
requirement: !ruby/object:Gem::Requirement
|
63
99
|
none: false
|
64
100
|
requirements:
|
65
101
|
- - ! '>='
|
@@ -67,7 +103,12 @@ dependencies:
|
|
67
103
|
version: '0.9'
|
68
104
|
type: :development
|
69
105
|
prerelease: false
|
70
|
-
version_requirements:
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ! '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0.9'
|
71
112
|
description: Pling is a notification framework that supports multiple gateways. Currently
|
72
113
|
supported are Android Push and SMS.
|
73
114
|
email:
|
@@ -98,6 +139,8 @@ files:
|
|
98
139
|
- lib/pling/delayed_initializer.rb
|
99
140
|
- lib/pling/device.rb
|
100
141
|
- lib/pling/gateway.rb
|
142
|
+
- lib/pling/gcm.rb
|
143
|
+
- lib/pling/gcm/gateway.rb
|
101
144
|
- lib/pling/message.rb
|
102
145
|
- lib/pling/middleware.rb
|
103
146
|
- lib/pling/middleware/base.rb
|
@@ -113,6 +156,7 @@ files:
|
|
113
156
|
- spec/pling/delayed_initializer_spec.rb
|
114
157
|
- spec/pling/device_spec.rb
|
115
158
|
- spec/pling/gateway_spec.rb
|
159
|
+
- spec/pling/gcm/gateway_spec.rb
|
116
160
|
- spec/pling/message_spec.rb
|
117
161
|
- spec/pling_spec.rb
|
118
162
|
- spec/spec_helper.rb
|
@@ -136,7 +180,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
136
180
|
version: '0'
|
137
181
|
requirements: []
|
138
182
|
rubyforge_project:
|
139
|
-
rubygems_version: 1.8.
|
183
|
+
rubygems_version: 1.8.24
|
140
184
|
signing_key:
|
141
185
|
specification_version: 3
|
142
186
|
summary: Pling is a notification framework that supports multiple gateways
|
@@ -151,6 +195,7 @@ test_files:
|
|
151
195
|
- spec/pling/delayed_initializer_spec.rb
|
152
196
|
- spec/pling/device_spec.rb
|
153
197
|
- spec/pling/gateway_spec.rb
|
198
|
+
- spec/pling/gcm/gateway_spec.rb
|
154
199
|
- spec/pling/message_spec.rb
|
155
200
|
- spec/pling_spec.rb
|
156
201
|
- spec/spec_helper.rb
|