pling 0.2.0 → 0.3.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.
@@ -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