pushmeup 0.1.2 → 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.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MWVmYjczNGEwYjE4ZTkzNGFhOWJlYjE5NDFhNDg2OTk1YjMyYjJhYg==
5
+ data.tar.gz: !binary |-
6
+ OGYyNzFkMzdlZTY1MTM3NWVlMjE0YTEwYThmZWU4ZTE0NTg2ZTllNw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ M2FiNjA1MWQ2YzRiNGI5OGYzNjBlODFjM2NiYzUwY2E3ZjQwYmVlYzZiYWJj
10
+ YTI0MTU0ODU0ZWU4ODBkN2U5YmY0OWU4MmIxNDU0ZjNhNjEzMmMwMzE0ZDdk
11
+ MjA4ZmE4ZWZkODc0NmI3N2M5MTIwNmVlNDM1Y2EwZDQ2ZjYwMzc=
12
+ data.tar.gz: !binary |-
13
+ NTM1YjA1NzhjZWNhMmQzMjAwNmVkM2VjZmQ5MjY1YjA0MGY4MDg5YmE1NjZi
14
+ MDk0Yzk2NTAyZTI3M2U0NTk5NmI4ZDExMzAzOTAwNmEzOGNmNTE4N2QyOTE5
15
+ NTY0ZWFkMTdlZjc0ZDVhMDUyMDg0MDYyMTExYTE2NDk5NTk5MDE=
data/.gitignore CHANGED
@@ -15,3 +15,5 @@ spec/reports
15
15
  test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
+ NEW_README.md
19
+ .idea/
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ pushmeup
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-1.9.3
data/.travis.yml CHANGED
@@ -1,11 +1,7 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 1.8.7
4
- - 1.9.2
5
3
  - 1.9.3
6
- - jruby-18mode # JRuby in 1.8 mode
4
+ - 2.0.0
7
5
  - jruby-19mode # JRuby in 1.9 mode
8
- - rbx-18mode
9
- - rbx-19mode
10
6
  # uncomment this line if your project needs to run something other than `rake`:
11
- # script: bundle exec rspec spec
7
+ # script: bundle exec rspec spec
data/README.md CHANGED
@@ -12,7 +12,7 @@ Pushmeup is an attempt to create an push notifications center that could send pu
12
12
  - Windows Phone
13
13
  - And many others
14
14
 
15
- Currently we have only support for ``iOS`` and ``Android`` but we are planning code for more plataforms.
15
+ Currently we have only support for ``iOS``, ``Android`` and ``Kindle Fire`` but we are planning code for more plataforms.
16
16
 
17
17
  ## Installation
18
18
 
@@ -41,14 +41,15 @@ and install it with
41
41
  3. After you have created your ``pem`` file. Set the host, port and certificate file location on the APNS class. You just need to set this once:
42
42
 
43
43
  APNS.host = 'gateway.push.apple.com'
44
- # gateway.sandbox.push.apple.com is default
44
+ # gateway.sandbox.push.apple.com is default and only for development
45
+ # gateway.push.apple.com is only for production
45
46
 
46
47
  APNS.port = 2195
47
48
  # this is also the default. Shouldn't ever have to set this, but just in case Apple goes crazy, you can.
48
-
49
+
49
50
  APNS.pem = '/path/to/pem/file'
50
51
  # this is the file you just created
51
-
52
+
52
53
  APNS.pass = ''
53
54
  # Just in case your pem need a password
54
55
 
@@ -56,25 +57,49 @@ and install it with
56
57
 
57
58
  #### Sending a single notification:
58
59
 
59
- device_token = '123abc456def'
60
- APNS.send_notification(device_token, 'Hello iPhone!' )
61
- APNS.send_notification(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
60
+ device_token = '123abc456def'
61
+ APNS.send_notification(device_token, 'Hello iPhone!' )
62
+ APNS.send_notification(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
62
63
 
63
64
  #### Sending multiple notifications
64
65
 
65
- device_token = '123abc456def'
66
- n1 = APNS::Notification.new(device_token, 'Hello iPhone!' )
67
- n2 = APNS::Notification.new(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
68
- APNS.send_notifications([n1, n2])
66
+ device_token = '123abc456def'
67
+ n1 = APNS::Notification.new(device_token, 'Hello iPhone!' )
68
+ n2 = APNS::Notification.new(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
69
+ APNS.send_notifications([n1, n2])
70
+
71
+ > All notifications passed as a parameter will be sent on a single connection, this is done to improve
72
+ > reliability with APNS servers.
73
+
74
+ #### Another way to send multiple notifications is to send notifications in a persistent connection (thread safe)
75
+
76
+ # Define that you want persistent connection
77
+ APNS.start_persistence
78
+
79
+ device_token = '123abc456def'
80
+
81
+ # Send single notifications
82
+ APNS.send_notification(device_token, 'Hello iPhone!' )
83
+ APNS.send_notification(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
84
+
85
+ # Send multiple notifications
86
+ n1 = APNS::Notification.new(device_token, 'Hello iPhone!' )
87
+ n2 = APNS::Notification.new(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default')
88
+ APNS.send_notifications([n1, n2])
89
+
90
+ ...
91
+
92
+ # Stop persistence, from this point each new push will open and close connections
93
+ APNS.stop_persistence
69
94
 
70
95
  #### Sending more information along
71
96
 
72
- APNS.send_notification(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default',
73
- :other => {:sent => 'with apns gem', :custom_param => "value"})
97
+ APNS.send_notification(device_token, :alert => 'Hello iPhone!', :badge => 1, :sound => 'default',
98
+ :other => {:sent => 'with apns gem', :custom_param => "value"})
74
99
 
75
100
  this will result in a payload like this:
76
101
 
77
- {"aps":{"alert":"Hello iPhone!","badge":1,"sound":"default"},"sent":"with apns gem", "custom_param":"value"}
102
+ {"aps":{"alert":"Hello iPhone!","badge":1,"sound":"default"},"sent":"with apns gem", "custom_param":"value"}
78
103
 
79
104
  ### Getting your iOS device token
80
105
 
@@ -168,29 +193,101 @@ You can use multiple keys to send notifications, to do it just do this changes i
168
193
  # For single notification
169
194
  GCM.send_notification( destination, :identity => :key1 )
170
195
  # Empty notification
171
-
196
+
172
197
  GCM.send_notification( destination, data, :identity => :key1 )
173
198
  # Notification with custom information
174
-
199
+
175
200
  GCM.send_notification( destination, data, :collapse_key => "placar_score_global", :time_to_live => 3600, :delay_while_idle => false, :identity => :key1 )
176
201
  # Notification with custom information and parameters
177
-
202
+
178
203
  # For multiple notifications
179
204
  options1 = {}
180
205
  options2 = {..., :identity => :key2}
181
206
  n1 = GCM::Notification.new(destination1, data1, options1.merge({:identity => :key2}))
182
207
  n2 = GCM::Notification.new(destination2, data2, :identity => :key1)
183
208
  n3 = GCM::Notification.new(destination3, data3, options2)
184
-
209
+
185
210
  GCM.send_notifications( [n1, n2, n3] )
186
211
  # In this case, every notification has his own parameters, options and key
187
212
 
188
- ## Build Status [![Build Status](https://secure.travis-ci.org/NicosKaralis/pushmeup.png?branch=master)](http://travis-ci.org/NicosKaralis/pushmeup) [![Code Climate](https://codeclimate.com/badge.png)](https://codeclimate.com/github/NicosKaralis/pushmeup)
213
+ ## FIRE (Amazon Messaging)
214
+
215
+ ### Configure
216
+
217
+ FIRE.client_id = "amzn1.application-oa2-client.12345678sdfgsdfg"
218
+ # this is the Client ID obtained from your Security Profile Management on amazon developers
219
+
220
+ FIRE.client_secret = "fkgjsbegksklwr863485245ojowe345"
221
+ # this is the Client Secret obtained from your Security Profile Management on amazon developers
222
+
223
+ ### Usage
224
+
225
+ #### Sending a single notification:
226
+
227
+ destination = "tydgfhewgnwe37586329586ejthe93053th346hrth3t"
228
+ # can be an string or an array of strings containing the regId of the device you want to send
229
+
230
+ data = {:key => "value", :key2 => "some value2"}
231
+ # must be an hash with all values you want inside you notification, strings only, no arrays
232
+
233
+ FIRE.send_notification( destination )
234
+ # Empty notification
235
+
236
+ FIRE.send_notification( destination, data )
237
+ # Notification with custom information
238
+
239
+ FIRE.send_notification( destination, data, :consolidationKey => "placar_score_global", :expiresAfter => 3600)
240
+ # Notification with custom information and parameters
241
+
242
+ for more information on parameters check documentation: [Amazon Messaging | Developers](https://developer.amazon.com/public/apis/engage/device-messaging/tech-docs/06-sending-a-message#Request Format)
243
+
244
+ #### Sending multiple notifications:
245
+
246
+ destination1 = "device1"
247
+ destination2 = ["device2"]
248
+ destination3 = ["device1", "device2", "device3"]
249
+ # can be an string or an array of strings containing the regIds of the devices you want to send
250
+
251
+ data1 = {:key => "value", :key2 => ["array", "value"]}
252
+ # must be an hash with all values you want inside you notification
253
+
254
+ options1 = {:consolidationKey => "placar_score_global", :expiresAfter => 3600}
255
+ # options for the notification
256
+
257
+ n1 = FIRE::Notification.new(destination1, data1, options1)
258
+ n2 = FIRE::Notification.new(destination2, data2)
259
+ n3 = FIRE::Notification.new(destination3, data3, options2)
260
+
261
+ FIRE.send_notifications( [n1, n2, n3] )
262
+ # In this case, every notification has his own parameters
263
+
264
+ for more information on parameters check documentation: [Amazon Messaging | Developers](https://developer.amazon.com/public/apis/engage/device-messaging/tech-docs/06-sending-a-message#Request Format)
265
+
266
+ #### Getting your Kindle Fire device token (regId)
267
+
268
+ Check this link [Amazon Messaging: Getting Started](https://developer.amazon.com/public/apis/engage/device-messaging)
269
+
189
270
 
190
- ## Dependency Status [![Dependency Status](https://gemnasium.com/NicosKaralis/pushmeup.png?travis)](https://gemnasium.com/NicosKaralis/pushmeup)
271
+ ## Status
272
+
273
+ #### Build Status
274
+ [![Build Status](https://travis-ci.org/NicosKaralis/pushmeup.png?branch=master)](https://travis-ci.org/NicosKaralis/pushmeup)
275
+ [![Code Climate](https://codeclimate.com/github/NicosKaralis/pushmeup.png)](https://codeclimate.com/github/NicosKaralis/pushmeup)
276
+
277
+ #### Dependency Status [![Dependency Status](https://gemnasium.com/NicosKaralis/pushmeup.png?travis)](https://gemnasium.com/NicosKaralis/pushmeup)
278
+
279
+ ## Contributing
280
+
281
+ We would be very pleased if you want to help us!
282
+
283
+ Currently we need a lot of testing so if you are good at writing tests please help us
191
284
 
192
285
  ## License
193
286
 
194
287
  Pushmeup is released under the MIT license:
195
288
 
196
289
  http://www.opensource.org/licenses/MIT
290
+
291
+
292
+ [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/NicosKaralis/pushmeup/trend.png)](https://bitdeli.com/free "Bitdeli Badge")
293
+
data/Rakefile CHANGED
@@ -5,4 +5,9 @@ require "rspec/core/rake_task"
5
5
  RSpec::Core::RakeTask.new
6
6
 
7
7
  task :default => :spec
8
- task :test => :spec
8
+ task :test => :spec
9
+
10
+ desc "Open an irb session preloaded with this library"
11
+ task :console do
12
+ sh "irb -rubygems -I lib -r pushmeup.rb"
13
+ end
@@ -0,0 +1,2 @@
1
+ require "pushmeup/fire/core"
2
+ require "pushmeup/fire/notification"
@@ -10,36 +10,52 @@ module APNS
10
10
  @pem = nil # this should be the path of the pem file not the contentes
11
11
  @pass = nil
12
12
 
13
+ @persistent = false
14
+ @mutex = Mutex.new
15
+ @retries = 3 # TODO: check if we really need this
16
+
17
+ @sock = nil
18
+ @ssl = nil
19
+
13
20
  class << self
14
21
  attr_accessor :host, :pem, :port, :pass
15
22
  end
16
23
 
24
+ def self.start_persistence
25
+ @persistent = true
26
+ end
27
+
28
+ def self.stop_persistence
29
+ @persistent = false
30
+
31
+ @ssl.close
32
+ @sock.close
33
+ end
34
+
17
35
  def self.send_notification(device_token, message)
18
36
  n = APNS::Notification.new(device_token, message)
19
37
  self.send_notifications([n])
20
38
  end
21
39
 
22
40
  def self.send_notifications(notifications)
23
- sock, ssl = self.open_connection
24
-
25
- notifications.each do |n|
26
- ssl.write(n.packaged_notification)
41
+ @mutex.synchronize do
42
+ self.with_connection do
43
+ notifications.each do |n|
44
+ @ssl.write(n.packaged_notification)
45
+ end
46
+ end
27
47
  end
28
-
29
- ssl.close
30
- sock.close
31
48
  end
32
49
 
33
50
  def self.feedback
34
- raise "Not implemented yet"
35
51
  sock, ssl = self.feedback_connection
36
52
 
37
53
  apns_feedback = []
38
54
 
39
- while line = sock.gets # Read lines from the socket
55
+ while line = ssl.read(38) # Read lines from the socket
40
56
  line.strip!
41
57
  f = line.unpack('N1n1H140')
42
- apns_feedback << [Time.at(f[0]), f[2]]
58
+ apns_feedback << { :timestamp => Time.at(f[0]), :token => f[2] }
43
59
  end
44
60
 
45
61
  ssl.close
@@ -48,8 +64,38 @@ module APNS
48
64
  return apns_feedback
49
65
  end
50
66
 
51
- protected
52
-
67
+ protected
68
+
69
+ def self.with_connection
70
+ attempts = 1
71
+
72
+ begin
73
+ # If no @ssl is created or if @ssl is closed we need to start it
74
+ if @ssl.nil? || @sock.nil? || @ssl.closed? || @sock.closed?
75
+ @sock, @ssl = self.open_connection
76
+ end
77
+
78
+ yield
79
+
80
+ rescue StandardError, Errno::EPIPE
81
+ raise unless attempts < @retries
82
+
83
+ @ssl.close
84
+ @sock.close
85
+
86
+ attempts += 1
87
+ retry
88
+ end
89
+
90
+ # Only force close if not persistent
91
+ unless @persistent
92
+ @ssl.close
93
+ @ssl = nil
94
+ @sock.close
95
+ @sock = nil
96
+ end
97
+ end
98
+
53
99
  def self.open_connection
54
100
  raise "The path to your pem file is not set. (APNS.pem = /path/to/cert.pem)" unless self.pem
55
101
  raise "The path to your pem file does not exist!" unless File.exist?(self.pem)
@@ -74,13 +120,12 @@ module APNS
74
120
  context.key = OpenSSL::PKey::RSA.new(File.read(self.pem), self.pass)
75
121
 
76
122
  fhost = self.host.gsub('gateway','feedback')
77
- puts fhost
78
123
 
79
124
  sock = TCPSocket.new(fhost, 2196)
80
- ssl = OpenSSL::SSL::SSLSocket.new(sock,context)
125
+ ssl = OpenSSL::SSL::SSLSocket.new(sock, context)
81
126
  ssl.connect
82
127
 
83
128
  return sock, ssl
84
129
  end
85
130
 
86
- end
131
+ end
@@ -1,7 +1,7 @@
1
1
  module APNS
2
2
  class Notification
3
3
  attr_accessor :device_token, :alert, :badge, :sound, :other
4
-
4
+
5
5
  def initialize(device_token, message)
6
6
  self.device_token = device_token
7
7
  if message.is_a?(Hash)
@@ -12,28 +12,36 @@ module APNS
12
12
  elsif message.is_a?(String)
13
13
  self.alert = message
14
14
  else
15
- raise "Notification needs to have either a hash or string"
15
+ raise "Notification needs to have either a Hash or String"
16
16
  end
17
17
  end
18
-
18
+
19
19
  def packaged_notification
20
20
  pt = self.packaged_token
21
21
  pm = self.packaged_message
22
22
  [0, 0, 32, pt, 0, pm.bytesize, pm].pack("ccca*cca*")
23
23
  end
24
-
24
+
25
25
  def packaged_token
26
26
  [device_token.gsub(/[\s|<|>]/,'')].pack('H*')
27
27
  end
28
-
28
+
29
29
  def packaged_message
30
30
  aps = {'aps'=> {} }
31
31
  aps['aps']['alert'] = self.alert if self.alert
32
32
  aps['aps']['badge'] = self.badge if self.badge
33
33
  aps['aps']['sound'] = self.sound if self.sound
34
34
  aps.merge!(self.other) if self.other
35
- aps.to_json
35
+ aps.to_json.gsub(/\\u([\da-fA-F]{4})/) {|m| [$1].pack("H*").unpack("n*").pack("U*")}
36
36
  end
37
-
37
+
38
+ def ==(that)
39
+ device_token == that.device_token &&
40
+ alert == that.alert &&
41
+ badge == that.badge &&
42
+ sound == that.sound &&
43
+ other == that.other
44
+ end
45
+
38
46
  end
39
- end
47
+ end
@@ -0,0 +1,103 @@
1
+ require 'httparty'
2
+ # require 'cgi'
3
+ require 'json'
4
+
5
+ module FIRE
6
+ include HTTParty
7
+
8
+ @host = 'https://api.amazon.com/messaging/registrations/%s/messages'
9
+ @client_id = nil
10
+ @client_secret = nil
11
+
12
+ @access_token_expiration = Time.new(0)
13
+ @access_token = nil
14
+
15
+ class << self
16
+ attr_accessor :host, :client_id, :client_secret, :access_token, :access_token_expiration
17
+ end
18
+
19
+ def self.send_notification(device_token, data = {}, options = {})
20
+ n = FIRE::Notification.new(device_token, data, options)
21
+ self.send_notifications([n])
22
+ end
23
+
24
+ def self.send_notifications(notifications)
25
+ self.prepare_token
26
+ responses = []
27
+ notifications.each do |n|
28
+ responses << self.prepare_and_send(n)
29
+ end
30
+ responses
31
+ end
32
+
33
+ def self.prepare_token
34
+ return if Time.now < self.access_token_expiration
35
+
36
+ token = self.get_access_token
37
+ self.access_token = token['access_token']
38
+ expires_in_sec = token['expires_in']
39
+ self.access_token_expiration = Time.now + expires_in_sec - 60
40
+ end
41
+
42
+ def self.get_access_token
43
+ headers = {'Content-Type' => 'application/x-www-form-urlencoded'}
44
+ body = {grant_type: 'client_credentials',
45
+ scope: 'messaging:push',
46
+ client_id: self.client_id,
47
+ client_secret: self.client_secret
48
+ }
49
+ params = {headers: headers, body: body}
50
+ res = self.post('https://api.amazon.com/auth/O2/token', params)
51
+ return res.parsed_response if res.response.code.to_i == 200
52
+ raise 'Error getting access token'
53
+ end
54
+
55
+ private
56
+
57
+ def self.prepare_and_send(n)
58
+ if !n.consolidationKey.nil? && n.expiresAfter.nil?
59
+ raise %q{If you are defining a "colapse key" you need a "time to live"}
60
+ end
61
+ self.send_push(n)
62
+ end
63
+
64
+ def self.send_push(n)
65
+ headers = {
66
+ 'Authorization' => "Bearer #{self.access_token}",
67
+ 'Content-Type' => 'application/json',
68
+ 'Accept' => 'application/json',
69
+ 'X-Amzn-Accept-Type' => 'com.amazon.device.messaging.ADMSendResult@1.0',
70
+ 'X-Amzn-Type-Version' => 'com.amazon.device.messaging.ADMMessage@1.0'
71
+ }
72
+
73
+ body = {
74
+ :data => n.data
75
+ }
76
+ body.merge!({consolidationKey: n.consolidationKey}) if n.consolidationKey
77
+ body.merge!({expiresAfter: n.expiresAfter}) if n.expiresAfter
78
+ return self.send_to_server(headers, body.to_json, n.device_token)
79
+ end
80
+
81
+ def self.send_to_server(headers, body, token)
82
+ params = {:headers => headers, :body => body}
83
+ device_dest = self.host % [token]
84
+ response = self.post(device_dest, params)
85
+ return build_response(response)
86
+ end
87
+
88
+ def self.build_response(response)
89
+ case response.code
90
+ when 200
91
+ {:response => 'success', :body => JSON.parse(response.body), :headers => response.headers, :status_code => response.code}
92
+ when 400
93
+ {:response => response.parsed_response, :status_code => response.code}
94
+ when 401
95
+ {:response => 'There was an error authenticating the sender account.', :status_code => response.code}
96
+ when 500
97
+ {:response => 'There was an internal error in the Amazaon server while trying to process the request.', :status_code => response.code}
98
+ when 503
99
+ {:response => 'Server is temporarily unavailable.', :status_code => response.code}
100
+ end
101
+ end
102
+
103
+ end
@@ -0,0 +1,46 @@
1
+ module FIRE
2
+ class Notification
3
+ attr_accessor :device_token, :data, :consolidationKey, :expiresAfter
4
+
5
+ def initialize(token, data, options = {})
6
+ self.device_token = token
7
+ self.data = data
8
+
9
+ @consolidationKey = options[:consolidationKey]
10
+ @expiresAfter = options[:expiresAfter]
11
+ end
12
+
13
+ def device_token=(token)
14
+
15
+ if token.is_a?(String)
16
+ @device_token = token
17
+ else
18
+ raise "device_token needs to be String"
19
+ end
20
+ end
21
+
22
+ def data=(data)
23
+ if data.is_a?(Hash)
24
+ @data = data
25
+ else
26
+ raise "data parameter must be the type of Hash"
27
+ end
28
+ end
29
+
30
+ def expiresAfter=(expiresAfter)
31
+ if expiresAfter.is_a?(Integer)
32
+ @expiresAfter = expiresAfter
33
+ else
34
+ raise %q{"expiresAfter" must be seconds as an integer value, like "100"}
35
+ end
36
+ end
37
+
38
+ def ==(that)
39
+ device_token == that.device_token &&
40
+ data == that.data &&
41
+ consolidationKey == that.consolidationKey &&
42
+ expiresAfter == that.expiresAfter
43
+ end
44
+
45
+ end
46
+ end
@@ -4,14 +4,14 @@ require 'json'
4
4
 
5
5
  module GCM
6
6
  include HTTParty
7
-
7
+
8
8
  @host = 'https://android.googleapis.com/gcm/send'
9
9
  @format = :json
10
10
  @key = nil
11
11
 
12
12
  class << self
13
13
  attr_accessor :host, :format, :key
14
-
14
+
15
15
  def key(identity = nil)
16
16
  if @key.is_a?(Hash)
17
17
  raise %{If your key is a hash of keys you'l need to pass a identifier to the notification!} if identity.nil?
@@ -20,13 +20,21 @@ module GCM
20
20
  return @key
21
21
  end
22
22
  end
23
+
24
+ def key_identities
25
+ if @key.is_a?(Hash)
26
+ return @key.keys
27
+ else
28
+ return nil
29
+ end
30
+ end
23
31
  end
24
-
32
+
25
33
  def self.send_notification(device_tokens, data = {}, options = {})
26
34
  n = GCM::Notification.new(device_tokens, data, options)
27
35
  self.send_notifications([n])
28
36
  end
29
-
37
+
30
38
  def self.send_notifications(notifications)
31
39
  responses = []
32
40
  notifications.each do |n|
@@ -36,7 +44,7 @@ module GCM
36
44
  end
37
45
 
38
46
  private
39
-
47
+
40
48
  def self.prepare_and_send(n)
41
49
  if n.device_tokens.count < 1 || n.device_tokens.count > 1000
42
50
  raise "Number of device_tokens invalid, keep it betwen 1 and 1000"
@@ -47,7 +55,7 @@ module GCM
47
55
  if @key.is_a?(Hash) && n.identity.nil?
48
56
  raise %{If your key is a hash of keys you'l need to pass a identifier to the notification!}
49
57
  end
50
-
58
+
51
59
  if self.format == :json
52
60
  self.send_push_as_json(n)
53
61
  elsif self.format == :text
@@ -56,7 +64,7 @@ module GCM
56
64
  raise "Invalid format"
57
65
  end
58
66
  end
59
-
67
+
60
68
  def self.send_push_as_json(n)
61
69
  headers = {
62
70
  'Authorization' => "key=#{ self.key(n.identity) }",
@@ -71,7 +79,7 @@ module GCM
71
79
  }
72
80
  return self.send_to_server(headers, body.to_json)
73
81
  end
74
-
82
+
75
83
  def self.send_push_as_plain_text(n)
76
84
  raise "Still has to be done: http://developer.android.com/guide/google/gcm/gcm.html"
77
85
  headers = {
@@ -81,13 +89,13 @@ module GCM
81
89
  }
82
90
  return self.send_to_server(headers, body)
83
91
  end
84
-
92
+
85
93
  def self.send_to_server(headers, body)
86
94
  params = {:headers => headers, :body => body}
87
- response = self.post('https://android.googleapis.com/gcm/send', params)
95
+ response = self.post(self.host, params)
88
96
  return build_response(response)
89
97
  end
90
-
98
+
91
99
  def self.build_response(response)
92
100
  case response.code
93
101
  when 200
@@ -102,5 +110,5 @@ module GCM
102
110
  {:response => 'Server is temporarily unavailable.', :status_code => response.code}
103
111
  end
104
112
  end
105
-
113
+
106
114
  end
@@ -1,39 +1,39 @@
1
1
  module GCM
2
2
  class Notification
3
3
  attr_accessor :device_tokens, :data, :collapse_key, :time_to_live, :delay_while_idle, :identity
4
-
4
+
5
5
  def initialize(tokens, data, options = {})
6
6
  self.device_tokens = tokens
7
7
  self.data = data
8
-
8
+
9
9
  @collapse_key = options[:collapse_key]
10
10
  @time_to_live = options[:time_to_live]
11
11
  @delay_while_idle = options[:delay_while_idle]
12
12
  @identity = options[:identity]
13
13
  end
14
-
14
+
15
15
  def device_tokens=(tokens)
16
16
  if tokens.is_a?(Array)
17
17
  @device_tokens = tokens
18
18
  elsif tokens.is_a?(String)
19
19
  @device_tokens = [tokens]
20
20
  else
21
- raise "device_tokens needs to be either a hash or string"
21
+ raise "device_tokens needs to be either a Hash or String"
22
22
  end
23
23
  end
24
-
24
+
25
25
  def data=(data)
26
26
  if data.is_a?(Hash)
27
27
  @data = data
28
28
  else
29
- raise "data parameter must be the tpe of Hash"
29
+ raise "data parameter must be the type of Hash"
30
30
  end
31
31
  end
32
-
32
+
33
33
  def delay_while_idle=(delay_while_idle)
34
34
  @delay_while_idle = (delay_while_idle == true || delay_while_idle == :true)
35
35
  end
36
-
36
+
37
37
  def time_to_live=(time_to_live)
38
38
  if time_to_live.is_a?(Integer)
39
39
  @time_to_live = time_to_live
@@ -41,6 +41,15 @@ module GCM
41
41
  raise %q{"time_to_live" must be seconds as an integer value, like "100"}
42
42
  end
43
43
  end
44
-
44
+
45
+ def ==(that)
46
+ device_tokens == that.device_tokens &&
47
+ data == that.data &&
48
+ collapse_key == that.collapse_key &&
49
+ time_to_live == that.time_to_live &&
50
+ delay_while_idle == that.delay_while_idle &&
51
+ identity == that.identity
52
+ end
53
+
45
54
  end
46
- end
55
+ end
@@ -1,3 +1,3 @@
1
1
  module Pushmeup
2
- VERSION = "0.1.2"
2
+ VERSION = "0.3.0"
3
3
  end
data/lib/pushmeup.rb CHANGED
@@ -1,3 +1,4 @@
1
1
  require "pushmeup/version"
2
2
  require "pushmeup/apple"
3
3
  require "pushmeup/android"
4
+ require "pushmeup/amazon"
@@ -5,23 +5,37 @@ describe Pushmeup do
5
5
  it "should have a APNS object" do
6
6
  defined?(APNS).should_not be_false
7
7
  end
8
-
8
+
9
9
  it "should not forget the APNS default parameters" do
10
10
  APNS.host.should == "gateway.sandbox.push.apple.com"
11
11
  APNS.port.should == 2195
12
12
  APNS.pem.should be_equal(nil)
13
13
  APNS.pass.should be_equal(nil)
14
14
  end
15
-
15
+
16
+ describe "Notifications" do
17
+
18
+ describe "#==" do
19
+
20
+ it "should properly equate objects without caring about object identity" do
21
+ a = APNS::Notification.new("123", {:alert => "hi"})
22
+ b = APNS::Notification.new("123", {:alert => "hi"})
23
+ a.should eq(b)
24
+ end
25
+
26
+ end
27
+
28
+ end
29
+
16
30
  end
17
-
31
+
18
32
  describe "GCM" do
19
33
  it "should have a GCM object" do
20
34
  defined?(GCM).should_not be_false
21
35
  end
22
-
36
+
23
37
  describe "Notifications" do
24
-
38
+
25
39
  before do
26
40
  @options = {:data => "dummy data"}
27
41
  end
@@ -38,20 +52,30 @@ describe Pushmeup do
38
52
  end
39
53
 
40
54
  it "should allow only notifications with data as hash with :data root" do
41
- n = GCM::Notification.new("id", {:data => "data"})
42
-
55
+ n = GCM::Notification.new("id", { :data => "data" })
56
+
43
57
  n.data.is_a?(Hash).should be_true
44
58
  n.data.should == {:data => "data"}
45
59
 
46
60
  n.data = {:a => ["a", "b", "c"]}
47
61
  n.data.is_a?(Hash).should be_true
48
62
  n.data.should == {:a => ["a", "b", "c"]}
49
-
63
+
50
64
  n.data = {:a => "a"}
51
65
  n.data.is_a?(Hash).should be_true
52
66
  n.data.should == {:a => "a"}
53
67
  end
54
-
68
+
69
+ describe "#==" do
70
+
71
+ it "should properly equate objects without caring about object identity" do
72
+ a = GCM::Notification.new("id", { :data => "data" })
73
+ b = GCM::Notification.new("id", { :data => "data" })
74
+ a.should eq(b)
75
+ end
76
+
77
+ end
78
+
55
79
  end
56
80
  end
57
81
  end
metadata CHANGED
@@ -1,20 +1,18 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pushmeup
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
5
- prerelease:
4
+ version: 0.3.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Nicos Karalis
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2012-11-14 00:00:00.000000000 Z
11
+ date: 2014-07-26 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: httparty
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
17
  - - ! '>='
20
18
  - !ruby/object:Gem::Version
@@ -22,7 +20,6 @@ dependencies:
22
20
  type: :runtime
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
24
  - - ! '>='
28
25
  - !ruby/object:Gem::Version
@@ -30,7 +27,6 @@ dependencies:
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: json
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
31
  - - ! '>='
36
32
  - !ruby/object:Gem::Version
@@ -38,7 +34,6 @@ dependencies:
38
34
  type: :runtime
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
38
  - - ! '>='
44
39
  - !ruby/object:Gem::Version
@@ -46,7 +41,6 @@ dependencies:
46
41
  - !ruby/object:Gem::Dependency
47
42
  name: rake
48
43
  requirement: !ruby/object:Gem::Requirement
49
- none: false
50
44
  requirements:
51
45
  - - ! '>='
52
46
  - !ruby/object:Gem::Version
@@ -54,7 +48,6 @@ dependencies:
54
48
  type: :development
55
49
  prerelease: false
56
50
  version_requirements: !ruby/object:Gem::Requirement
57
- none: false
58
51
  requirements:
59
52
  - - ! '>='
60
53
  - !ruby/object:Gem::Version
@@ -62,7 +55,6 @@ dependencies:
62
55
  - !ruby/object:Gem::Dependency
63
56
  name: rspec
64
57
  requirement: !ruby/object:Gem::Requirement
65
- none: false
66
58
  requirements:
67
59
  - - ! '>='
68
60
  - !ruby/object:Gem::Version
@@ -70,7 +62,6 @@ dependencies:
70
62
  type: :development
71
63
  prerelease: false
72
64
  version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
65
  requirements:
75
66
  - - ! '>='
76
67
  - !ruby/object:Gem::Version
@@ -88,7 +79,8 @@ extensions: []
88
79
  extra_rdoc_files: []
89
80
  files:
90
81
  - .gitignore
91
- - .rvmrc
82
+ - .ruby-gemset
83
+ - .ruby-version
92
84
  - .travis.yml
93
85
  - Gemfile
94
86
  - Keychain Access.jpg
@@ -96,10 +88,13 @@ files:
96
88
  - README.md
97
89
  - Rakefile
98
90
  - lib/pushmeup.rb
91
+ - lib/pushmeup/amazon.rb
99
92
  - lib/pushmeup/android.rb
100
93
  - lib/pushmeup/apns/core.rb
101
94
  - lib/pushmeup/apns/notification.rb
102
95
  - lib/pushmeup/apple.rb
96
+ - lib/pushmeup/fire/core.rb
97
+ - lib/pushmeup/fire/notification.rb
103
98
  - lib/pushmeup/gcm/core.rb
104
99
  - lib/pushmeup/gcm/notification.rb
105
100
  - lib/pushmeup/version.rb
@@ -108,33 +103,26 @@ files:
108
103
  - spec/spec_helper.rb
109
104
  homepage: https://github.com/NicosKaralis/pushmeup
110
105
  licenses: []
106
+ metadata: {}
111
107
  post_install_message:
112
108
  rdoc_options: []
113
109
  require_paths:
114
110
  - lib
115
111
  required_ruby_version: !ruby/object:Gem::Requirement
116
- none: false
117
112
  requirements:
118
113
  - - ! '>='
119
114
  - !ruby/object:Gem::Version
120
115
  version: '0'
121
- segments:
122
- - 0
123
- hash: -2951185572715016947
124
116
  required_rubygems_version: !ruby/object:Gem::Requirement
125
- none: false
126
117
  requirements:
127
118
  - - ! '>='
128
119
  - !ruby/object:Gem::Version
129
120
  version: '0'
130
- segments:
131
- - 0
132
- hash: -2951185572715016947
133
121
  requirements: []
134
122
  rubyforge_project: pushmeup
135
- rubygems_version: 1.8.24
123
+ rubygems_version: 2.2.2
136
124
  signing_key:
137
- specification_version: 3
125
+ specification_version: 4
138
126
  summary: Send push notifications to Apple devices through ANPS and Android devices
139
127
  through GCM
140
128
  test_files:
data/.rvmrc DELETED
@@ -1,48 +0,0 @@
1
- #!/usr/bin/env bash
2
-
3
- # This is an RVM Project .rvmrc file, used to automatically load the ruby
4
- # development environment upon cd'ing into the directory
5
-
6
- # First we specify our desired <ruby>[@<gemset>], the @gemset name is optional,
7
- # Only full ruby name is supported here, for short names use:
8
- # echo "rvm use 1.9.3" > .rvmrc
9
- environment_id="ruby-1.9.3-p125@pushmeup"
10
-
11
- # Uncomment the following lines if you want to verify rvm version per project
12
- # rvmrc_rvm_version="1.11.5 (master)" # 1.10.1 seams as a safe start
13
- # eval "$(echo ${rvm_version}.${rvmrc_rvm_version} | awk -F. '{print "[[ "$1*65536+$2*256+$3" -ge "$4*65536+$5*256+$6" ]]"}' )" || {
14
- # echo "This .rvmrc file requires at least RVM ${rvmrc_rvm_version}, aborting loading."
15
- # return 1
16
- # }
17
-
18
- # First we attempt to load the desired environment directly from the environment
19
- # file. This is very fast and efficient compared to running through the entire
20
- # CLI and selector. If you want feedback on which environment was used then
21
- # insert the word 'use' after --create as this triggers verbose mode.
22
- if [[ -d "${rvm_path:-$HOME/.rvm}/environments"
23
- && -s "${rvm_path:-$HOME/.rvm}/environments/$environment_id" ]]
24
- then
25
- \. "${rvm_path:-$HOME/.rvm}/environments/$environment_id"
26
- [[ -s "${rvm_path:-$HOME/.rvm}/hooks/after_use" ]] &&
27
- \. "${rvm_path:-$HOME/.rvm}/hooks/after_use" || true
28
- else
29
- # If the environment file has not yet been created, use the RVM CLI to select.
30
- rvm --create "$environment_id" || {
31
- echo "Failed to create RVM environment '${environment_id}'."
32
- return 1
33
- }
34
- fi
35
-
36
- # If you use bundler, this might be useful to you:
37
- if [[ -s Gemfile ]] && {
38
- ! builtin command -v bundle >/dev/null ||
39
- builtin command -v bundle | grep $rvm_path/bin/bundle >/dev/null
40
- }
41
- then
42
- printf "%b" "The rubygem 'bundler' is not installed. Installing it now.\n"
43
- gem install bundler
44
- fi
45
- if [[ -s Gemfile ]] && builtin command -v bundle >/dev/null
46
- then
47
- bundle install | grep -vE '^Using|Your bundle is complete'
48
- fi