pling 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Pling
2
- VERSION = "0.2.0"
2
+ VERSION = "0.3.0"
3
3
  end
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.2.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: 2011-11-11 00:00:00.000000000Z
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: &70316014249500 !ruby/object:Gem::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: *70316014249500
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: &70316014248240 !ruby/object:Gem::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: *70316014248240
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: &70316014247280 !ruby/object:Gem::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: *70316014247280
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: &70316014246260 !ruby/object:Gem::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: *70316014246260
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: &70316014245540 !ruby/object:Gem::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: *70316014245540
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.10
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