pyapns 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ === 0.3.0 2010-01-22
2
+
3
+ * 1 major enhancement:
4
+ * Initial release
@@ -0,0 +1,11 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ lib/pyapns.rb
7
+ script/console
8
+ script/destroy
9
+ script/generate
10
+ test/test_helper.rb
11
+ test/test_pyapns.rb
@@ -0,0 +1,7 @@
1
+
2
+ For more information on pyapns, see http://pyapns.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
@@ -0,0 +1,212 @@
1
+ = pyapns
2
+
3
+ * http://pyapns.org
4
+
5
+ == DESCRIPTION:
6
+ :title: The Ruby API
7
+
8
+ :section: PYAPNS::Client
9
+ There's python in my ruby!
10
+
11
+ This is a class used to send notifications, provision applications and
12
+ retrieve feedback using the Apple Push Notification Service.
13
+
14
+ PYAPNS is a multi-application APS provider, meaning it is possible to send
15
+ notifications to any number of different applications from the same application
16
+ and same server. It is also possible to scale the client to any number
17
+ of processes and servers, simply balanced behind a simple web proxy.
18
+
19
+ It may seem like overkill for such a bare interface - after all, the
20
+ APS service is rather simplistic. However, PYAPNS takes no shortcuts when it
21
+ comes to completeness/compliance with the APNS protocol and allows the
22
+ user many optimization and scaling vectors not possible with other libraries.
23
+ No bandwidth is wasted, connections are persistent and the server is
24
+ asynchronous therefore notifications are delivered immediately.
25
+
26
+ PYAPNS takes after the design of 3rd party push notification service that
27
+ charge a fee each time you push a notification, and charge extra for so-called
28
+ 'premium' service which supposedly gives you quicker access to the APS servers.
29
+ However, PYAPNS is free, as in beer and offers more scaling opportunities without
30
+ the financial draw.
31
+
32
+ :section: Provisioning
33
+
34
+ To add your app to the PYAPNS server, it must be `provisioned` at least once.
35
+ Normally this is done once upon the start-up of your application, be it a web
36
+ service, desktop application or whatever... It must be done at least once
37
+ to the server you're connecting to. Multiple instances of PYAPNS will have
38
+ to have their applications provisioned individually. To provision an application
39
+ manually use the `PYAPNS::Client#provision` method.
40
+
41
+ require 'pyapns'
42
+ client = PYAPNS::Client.configure
43
+ client.provision :app_id => 'cf', :cert => '/home/ss/cert.pem', :env => 'sandbox', :timeout => 15
44
+
45
+ This basically says "add an app reference named 'cf' to the server and start
46
+ a connection using the certification, and if it can't within 15 seconds,
47
+ raise a `PYAPNS::TimeoutException`
48
+
49
+ That's all it takes to get started. Of course, this can be done automatically
50
+ by using PYAPNS::ClientConfiguration middleware. `PYAPNS::Client` is a singleton
51
+ class that is configured using the class method `PYAPNS::Client#configure`. It
52
+ is sensibly configured by default, but can be customized by specifying a hash
53
+ See the docs on `PYAPNS::ClientConfiguration` for a list of available configuration
54
+ parameters (some of these are important, and you can specify initial applications)
55
+ to be configured by default.
56
+
57
+ :section: Sending Notifications
58
+
59
+ Once your client is configured, and application provisioned (again, these
60
+ should be taken care of before you write notification code) you can begin
61
+ sending notifications to users. If you're wondering how to acquire a notification
62
+ token, you've come to the wrong place... I recommend using google. However,
63
+ if you want to send hundreds of millions of notifications to users, here's how
64
+ it's done, one at a time...
65
+
66
+ The `PYAPNS::Client#notify` is a sort of polymorphic method which can notify
67
+ any number of devices at a time. It's basic form is as follows:
68
+
69
+ client.notify 'cf', 'long ass app token', {:aps=> {:alert => 'hello?'}}
70
+
71
+ However, as stated before, it is sort of polymorphic:
72
+
73
+ client.notify 'cf', ['token', 'token2', 'token3'], [alert, alert2, alert3]
74
+
75
+ client.notify :app_id => 'cf', :tokens => 'mah token', :notifications => alertHash
76
+
77
+ client.notify 'cf', 'token', PYAPNS::Notification('hello tits!')
78
+
79
+ As you can see, the method accepts paralell arrays of tokens and notifications
80
+ meaning any number of notifications can be sent at once. Hashes will be automatically
81
+ converted to `PYAPNS::Notification` objects so they can be optimized for the wire
82
+ (nil values removed, etc...), and you can pass `PYAPNS::Notification` objects
83
+ directly if you wish.
84
+
85
+ :section: Retrieving Feedback
86
+
87
+ The APS service offers a feedback functionality that allows application servers
88
+ to retrieve a list of device tokens it deems to be no longer in use, and the
89
+ time it thinks they stopped being useful (the user uninstalled your app, better
90
+ luck next time...) Sounds pretty straight forward, and it is. Apple recommends
91
+ you do this at least once an hour. PYAPNS will return a list of 2-element lists
92
+ with the date and the token:
93
+
94
+ feedbacks = client.feedback 'cf'
95
+
96
+ :section: Asynchronous Calls
97
+
98
+ PYAPNS::Client will, by default, perform no funny stuff and operate entirely
99
+ within the calling thread. This means that certain applications may hang when,
100
+ say, sending a notification, if only for a fraction of a second. Obviously
101
+ not a desirable trait, all `provision`, `feedback` and `notify`
102
+ methods also take a block, which indicates to the method you want to call
103
+ PYAPNS asynchronously, and it will be done so handily in another thread, calling
104
+ back your block with a single argument when finished. Note that `notify` and `provision`
105
+ return absolutely nothing (nil, for you rub--wait you are ruby developers!).
106
+ It is probably wise to always use this form of operation so your calling thread
107
+ is never blocked (especially important in UI-driven apps and asynchronous servers)
108
+ Just pass a block to provision/notify/feedback like so:
109
+
110
+ PYAPNS::Client.instance.feedback do |feedbacks|
111
+ feedbacks.each { |f| trim_token f }
112
+ end
113
+
114
+ :section: PYAPNS::ClientConfiguration
115
+ A middleware class to make `PYAPNS::Client` easy to use in web contexts
116
+
117
+ Automates configuration of the client in Rack environments
118
+ using a simple confiuration middleware. To use `PYAPNS::Client` in
119
+ Rack environments with the least code possible `use PYAPNS::ClientConfiguration`
120
+ (no, really, in some cases, that's all you need!) middleware with an optional
121
+ hash specifying the client variables. Options are as follows:
122
+
123
+ use PYAPNS::ClientConfiguration(
124
+ :host => 'http://localhost/'
125
+ :port => 7077,
126
+ :initial => [{
127
+ :app_id => 'myapp',
128
+ :cert => '/home/myuser/apps/myapp/cert.pem',
129
+ :env => 'sandbox',
130
+ :timeout => 15
131
+ }])
132
+
133
+ Where the configuration variables are defined:
134
+
135
+ :host String the host where the server can be found
136
+ :port Number the port to which the client should connect
137
+ :initial Array OPTIONAL - an array of INITIAL hashes
138
+
139
+ INITIAL HASHES:
140
+
141
+ :app_id String the id used to send messages with this certification
142
+ can be a totally arbitrary value
143
+ :cert String a path to the certification or the certification file
144
+ as a string
145
+ :env String the environment to connect to apple with, always
146
+ either 'sandbox' or 'production'
147
+ :timoeut Number The timeout for the server to use when connecting
148
+ to the apple servers
149
+
150
+ :section: PYAPNS::Notification
151
+ An APNS Notification
152
+
153
+ You can construct notification objects ahead of time by using this class.
154
+ However unnecessary, it allows you to programmatically generate a Notification
155
+ like so:
156
+
157
+ note = PYAPNS::Notification.new 'alert text', 9, 'flynn.caf', {:extra => 'guid'}
158
+
159
+ -- or --
160
+ note = PYAPNS::Notification.new 'alert text'
161
+
162
+ These can be passed to `PYAPNS::Client#notify` the same as hashes
163
+
164
+ == FEATURES/PROBLEMS:
165
+
166
+ * XML-RPC Based, works with any client in any language
167
+ * Native Python API with Django and Pylons support
168
+ * Native Ruby API with Rails/Rack support
169
+ * Scalable, fast and easy to distribute behind a proxy
170
+ * Based on Twisted
171
+ * Multi-application and dual environment support
172
+ * Simplified feedback interface
173
+
174
+ == SYNOPSIS:
175
+
176
+ require 'pyapns'
177
+ c = PYAPNS::Client.configure
178
+ c.notify('myapp', 'token', 'notification')
179
+
180
+ == REQUIREMENTS:
181
+
182
+ * pyapns >=0.3.0
183
+
184
+ == INSTALL:
185
+
186
+ sudo gem install pyapns
187
+ sudo easy_install-2.6 pyapns
188
+
189
+ == LICENSE:
190
+
191
+ (The MIT License)
192
+
193
+ Copyright (c) 2010 Samuel Webster Sutch
194
+
195
+ Permission is hereby granted, free of charge, to any person obtaining
196
+ a copy of this software and associated documentation files (the
197
+ 'Software'), to deal in the Software without restriction, including
198
+ without limitation the rights to use, copy, modify, merge, publish,
199
+ distribute, sublicense, and/or sell copies of the Software, and to
200
+ permit persons to whom the Software is furnished to do so, subject to
201
+ the following conditions:
202
+
203
+ The above copyright notice and this permission notice shall be
204
+ included in all copies or substantial portions of the Software.
205
+
206
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
207
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
208
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
209
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
210
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
211
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
212
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,23 @@
1
+ require 'rubygems'
2
+ gem 'hoe', '>= 2.1.0'
3
+ require 'hoe'
4
+ require 'fileutils'
5
+ require './lib/pyapns'
6
+
7
+ Hoe.plugin :newgem
8
+ # Hoe.plugin :website
9
+ # Hoe.plugin :cucumberfeatures
10
+
11
+ # Generate all the Rake tasks
12
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
13
+ $hoe = Hoe.spec 'pyapns' do
14
+ self.developer 'Samuel Webster Sutch', 'samuraiblog@gmail.com'
15
+ self.rubyforge_name = self.name
16
+ end
17
+
18
+ require 'newgem/tasks'
19
+ Dir['tasks/**/*.rake'].each { |t| load t }
20
+
21
+ # TODO - want other tests/tasks run by default? Add them to the list
22
+ # remove_task :default
23
+ # task :default => [:spec, :features]
@@ -0,0 +1,365 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'singleton'
5
+ require 'xmlrpc/client'
6
+
7
+
8
+ module PYAPNS
9
+ VERSION = "0.3.0"
10
+
11
+ ## PYAPNS::Client
12
+ ## There's python in my ruby!
13
+ ##
14
+ ## This is a class used to send notifications, provision applications and
15
+ ## retrieve feedback using the Apple Push Notification Service.
16
+ ##
17
+ ## PYAPNS is a multi-application APS provider, meaning it is possible to send
18
+ ## notifications to any number of different applications from the same application
19
+ ## and same server. It is also possible to scale the client to any number
20
+ ## of processes and servers, simply balanced behind a simple web proxy.
21
+ ##
22
+ ## It may seem like overkill for such a bare interface - after all, the
23
+ ## APS service is rather simplistic. However, PYAPNS takes no shortcuts when it
24
+ ## comes to compleatness/compliance with the APNS protocol and allows the
25
+ ## user many optimization and scaling vectors not possible with other libraries.
26
+ ## No bandwidth is wasted, connections are persistent and the server is
27
+ ## asynchronous therefore notifications are delivered immediately.
28
+ ##
29
+ ## PYAPNS takes after the design of 3rd party push notification service that
30
+ ## charge a fee each time you push a notification, and charge extra for so-called
31
+ ## 'premium' service which supposedly gives you quicker access to the APS servers.
32
+ ## However, PYAPNS is free, as in beer and offers more scaling oportunites without
33
+ ## the financial draw.
34
+ ##
35
+ ## Provisioning
36
+ ##
37
+ ## To add your app to the PYAPNS server, it must be `provisioned` at least once.
38
+ ## Normally this is done once upon the start-up of your application, be it a web
39
+ ## service, desktop application or whatever... It must be done at least once
40
+ ## to the server you're connecting to. Multiple instances of PYAPNS will have
41
+ ## to have their applications provisioned individually. To provision an application
42
+ ## manually use the PYAPNS::Client#provision method.
43
+ ##
44
+ ## require 'pyapns'
45
+ ## client = PYAPNS::Client.configure
46
+ ## client.provision :app_id => 'cf', :cert => '/home/ss/cert.pem', :env => 'sandbox', :timeout => 15
47
+ ##
48
+ ## This basically says "add an app reference named 'cf' to the server and start
49
+ ## a connection using the certification, and if it can't within 15 seconds,
50
+ ## raise a PYAPNS::TimeoutException
51
+ ##
52
+ ## That's all it takes to get started. Of course, this can be done automatically
53
+ ## by using PYAPNS::ClientConfiguration middleware. PYAPNS::Client is a singleton
54
+ ## class that is configured using the class method PYAPNS::Client#configure. It
55
+ ## is sensibly configured by default, but can be customized by specifying a hash
56
+ ## See the docs on PYAPNS::ClientConfiguration for a list of available configuration
57
+ ## parameters (some of these are important, and you can specify initial applications)
58
+ ## to be configured by default.
59
+ ##
60
+ ## Sending Notifications
61
+ ##
62
+ ## Once your client is configured, and application provisioned (again, these
63
+ ## should be taken care of before you write notification code) you can begin
64
+ ## sending notifications to users. If you're wondering how to acquire a notification
65
+ ## token, you've come to the wrong place... I recommend using google. However,
66
+ ## if you want to send hundreds of millions of notifications to users, here's how
67
+ ## it's done, one at a time...
68
+ ##
69
+ ## The PYAPNS::Client#notify is a sort of polymorphic method which can notify
70
+ ## any number of devices at a time. It's basic form is as follows:
71
+ ##
72
+ ## client.notify 'cf', 'long ass app token', {:aps=> {:alert => 'hello?'}}
73
+ ##
74
+ ## However, as stated before, it is sort of polymorphic:
75
+ ##
76
+ ## client.notify 'cf', ['token', 'token2', 'token3'], [alert, alert2, alert3]
77
+ ##
78
+ ## client.notify :app_id => 'cf', :tokens => 'mah token', :notifications => alertHash
79
+ ##
80
+ ## client.notify 'cf', 'token', PYAPNS::Notification('hello tits!')
81
+ ##
82
+ ## As you can see, the method accepts paralell arrays of tokens and notifications
83
+ ## meaning any number of notifications can be sent at once. Hashes will be automatically
84
+ ## converted to PYAPNS::Notification objects so they can be optimized for the wire
85
+ ## (nil values removed, etc...), and you can pass PYAPNS::Notification objects
86
+ ## directly if you wish.
87
+ ##
88
+ ## Retrieving Feedback
89
+ ##
90
+ ## The APS service offers a feedback functionality that allows application servers
91
+ ## to retrieve a list of device tokens it deems to be no longer in use, and the
92
+ ## time it thinks they stopped being useful (the user uninstalled your app, better
93
+ ## luck next time...) Sounds pretty straight forward, and it is. Apple recommends
94
+ ## you do this at least once an hour. PYAPNS will return a list of 2-element lists
95
+ ## with the date and the token:
96
+ ##
97
+ ## feedbacks = client.feedback 'cf'
98
+ ##
99
+ ## Asynchronous Calls
100
+ ##
101
+ ## PYAPNS::Client will, by default, perform no funny stuff and operate entirely
102
+ ## within the calling thread. This means that certain applications may hang when,
103
+ ## say, sending a notification, if only for a fraction of a second. Obviously
104
+ ## not a desirable trait, all `provision`, `feedback` and `notify`
105
+ ## methods also take a block, which indicates to the method you want to call
106
+ ## PYAPNS asynchronously, and it will be done so handily in another thread, calling
107
+ ## back your block with a single argument when finished. Note that `notify` and `provision`
108
+ ## return absolutely nothing (nil, for you rub--wait you are ruby developers!).
109
+ ## It is probably wise to always use this form of operation so your calling thread
110
+ ## is never blocked (especially important in UI-driven apps and asynchronous servers)
111
+ ## Just pass a block to provision/notify/feedback like so:
112
+ ##
113
+ ## PYAPNS::Client.instance.feedback do |feedbacks|
114
+ ## feedbacks.each { |f| trim_token f }
115
+ ## end
116
+ ##
117
+ class Client
118
+ include Singleton
119
+
120
+ def self.configure(hash={})
121
+ y = self.instance
122
+ y.configure(hash)
123
+ end
124
+
125
+ def initialize
126
+ @configured = false
127
+ end
128
+
129
+ def provision(*args, &block)
130
+ perform_call :provision, args, :app_id, :cert, :env, :timeout, &block
131
+ end
132
+
133
+ def notify(*args, &block)
134
+ kwargs = [:app_id, :tokens, :notifications]
135
+ get_args(args, *kwargs) do |splat|
136
+ splat[2] = (splat[2].class == Array ?
137
+ splat[2] : [splat[2]]).map do |note|
138
+ if note.class != PYAPNS::Notification
139
+ PYAPNS::Notification.encode note
140
+ else
141
+ note
142
+ end
143
+ end
144
+ perform_call :notify, splat, *kwargs, &block
145
+ end
146
+ end
147
+
148
+ def feedback(*args, &block)
149
+ perform_call :feedback, args, :app_id, &block
150
+ end
151
+
152
+ def perform_call(method, splat, *args, &block)
153
+ if !configured?
154
+ raise PYAPNS::NotConfigured.new
155
+ end
156
+ get_args(splat, *args) do |splat|
157
+ if block_given?
158
+ Thread.new do
159
+ perform_call2 {
160
+ block.call(@client.call_async(method.to_s, *splat))
161
+ }
162
+ end
163
+ nil
164
+ else
165
+ perform_call2 { @client.call_async(method.to_s, *splat) }
166
+ end
167
+ end
168
+ end
169
+
170
+ def get_args(splat, *args, &block)
171
+ if splat.length == 1 && splat[0].class == Hash
172
+ splat = args.map { |k| splat[0][k] }
173
+ end
174
+ if (splat.find_all { |l| not l.nil? }).length == args.length
175
+ block.call(splat)
176
+ else
177
+ raise PYAPNS::InvalidArguments.new "Invalid args supplied #{args}"
178
+ end
179
+ end
180
+
181
+ def perform_call2(&block)
182
+ begin
183
+ block.call()
184
+ rescue XMLRPC::FaultException => fault
185
+ case fault.faultCode
186
+ when 404
187
+ raise PYAPNS::UnknownAppID.new fault.faultString
188
+ when 401
189
+ raise PYAPNS::InvalidEnvironment.new fault.faultString
190
+ when 500
191
+ raise PYAPNS::ServerTimeout.new fault.faultString
192
+ else
193
+ raise fault
194
+ end
195
+ end
196
+ end
197
+
198
+ def configured?
199
+ return @configured
200
+ end
201
+
202
+ def configure(hash={})
203
+ if configured?
204
+ return self
205
+ end
206
+ h = {}
207
+ hash.each { |k,v| h[k.to_s.downcase] = v }
208
+ @host = h['host'] || "localhost"
209
+ @port = h['port'] || 7077
210
+ @path = h['path'] || '/'
211
+ @timeout = h['timeout'] || 15
212
+ @client = XMLRPC::Client.new3(
213
+ :host => @host,
214
+ :port => @port,
215
+ :timeout => @timeout,
216
+ :path => @path)
217
+ if not h['initial'].nil?
218
+ h['initial'].each do |initial|
219
+ provision(:app_id => initial[:app_id],
220
+ :cert => initial[:cert],
221
+ :env => initial[:env],
222
+ :timeout => initial[:timeout] || 15)
223
+ end
224
+ end
225
+ @configured = true
226
+ self
227
+ end
228
+ end
229
+
230
+ ## PYAPNS::ClientConfiguration
231
+ ## A middleware class to make PYAPNS::Client easy to use in web contexts
232
+ ##
233
+ ## Automates configuration of the client in Rack environments
234
+ ## using a simple confiuration middleware. To use PYAPNS::Client in
235
+ ## Rack environments with the least code possible use PYAPNS::ClientConfiguration
236
+ ## (no, really, in some cases, that's all you need!) middleware with an optional
237
+ ## hash specifying the client variables. Options are as follows:
238
+ ##
239
+ ## use PYAPNS::ClientConfiguration(
240
+ ## :host => 'http://localhost/'
241
+ ## :port => 7077,
242
+ ## :initial => [{
243
+ ## :app_id => 'myapp',
244
+ ## :cert => '/home/myuser/apps/myapp/cert.pem',
245
+ ## :env => 'sandbox',
246
+ ## :timeout => 15
247
+ ## }])
248
+ ##
249
+ ## Where the configuration variables are defined:
250
+ ##
251
+ ## :host String the host where the server can be found
252
+ ## :port Number the port to which the client should connect
253
+ ## :initial Array OPTIONAL - an array of INITIAL hashes
254
+ ##
255
+ ## INITIAL HASHES:
256
+ ##
257
+ ## :app_id String the id used to send messages with this certification
258
+ ## can be a totally arbitrary value
259
+ ## :cert String a path to the certification or the certification file
260
+ ## as a string
261
+ ## :env String the environment to connect to apple with, always
262
+ ## either 'sandbox' or 'production'
263
+ ## :timoeut Number The timeout for the server to use when connecting
264
+ ## to the apple servers
265
+ class ClientConfiguration
266
+ def initialize(app, hash={})
267
+ @app = app
268
+ PYAPNS::Client.configure(hash)
269
+ end
270
+
271
+ def call(env)
272
+ @app.call(env)
273
+ end
274
+ end
275
+
276
+ ## PYAPNS::Notification
277
+ ## An APNS Notification
278
+ ##
279
+ ## You can construct notification objects ahead of time by using this class.
280
+ ## However unnecessary, it allows you to programatically generate a Notification
281
+ ## like so:
282
+ ##
283
+ ## note = PYAPNS::Notification.new 'alert text', 9, 'flynn.caf', {:extra => 'guid'}
284
+ ##
285
+ ## -- or --
286
+ ## note = PYAPNS::Notification.new 'alert text'
287
+ ##
288
+ ## These can be passed to PYAPNS::Client#notify the same as hashes
289
+ ##
290
+ class Notification
291
+ def initialize(*args)
292
+ kwargs = [:alert, :badge, :sound]
293
+ extra = nil
294
+ if args.length == 1 && args[0].class == Hash
295
+ args = kwargs.map { |k| args[0][k] }
296
+ end
297
+ @note = {
298
+ :aps => {
299
+ :alert => args[0].nil? ? nil : args[0].to_s,
300
+ :badge => args[1].nil? ? nil : args[1].to_i,
301
+ :sound => args[2].nil? ? nil : args[2].to_s
302
+ }
303
+ }
304
+ if args.length == 4
305
+ @note = @note.merge(args[3] || {})
306
+ end
307
+ end
308
+
309
+ def self.aps_attr(*symbols)
310
+ symbols.each do |sy|
311
+ define_method sy do
312
+ instance_variable_get(:@note)[:aps][sy]
313
+ end
314
+ define_method "#{sy}=".to_sym do |val|
315
+ instance_variable_get(:@note)[:aps][sy] = val
316
+ end
317
+ end
318
+ end
319
+
320
+ aps_attr :alert, :badge, :sound
321
+
322
+ def extra key
323
+ @note[key]
324
+ end
325
+
326
+ def set_extra key, val
327
+ @note[key] = val
328
+ end
329
+
330
+ def encode
331
+ PYAPNS::Notification.encode(@note)
332
+ end
333
+
334
+ def self.encode note
335
+ ret = {}
336
+ if !note[:aps].nil?
337
+ ret['aps'] = {}
338
+ note[:aps].each do |k, v|
339
+ if !v.nil?
340
+ ret['aps'][k.to_s] = v
341
+ end
342
+ end
343
+ end
344
+ note.keys.find_all { |k| !note[k].nil? && k != :aps }.each do |k|
345
+ ret[k.to_s] = note[k]
346
+ end
347
+ ret
348
+ end
349
+ end
350
+
351
+ class UnknownAppID < Exception
352
+ end
353
+
354
+ class NotConfigured < Exception
355
+ end
356
+
357
+ class InvalidEnvironment < Exception
358
+ end
359
+
360
+ class ServerTimeout < Exception
361
+ end
362
+
363
+ class InvalidArguments < Exception
364
+ end
365
+ end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # File: script/console
3
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
4
+
5
+ libs = " -r irb/completion"
6
+ # Perhaps use a console_lib to store any extra methods I may want available in the cosole
7
+ # libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
8
+ libs << " -r #{File.dirname(__FILE__) + '/../lib/pyapns.rb'}"
9
+ puts "Loading pyapns gem"
10
+ exec "#{irb} #{libs} --simple-prompt"
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/destroy'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Destroy.new.run(ARGV)
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+ APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
3
+
4
+ begin
5
+ require 'rubigen'
6
+ rescue LoadError
7
+ require 'rubygems'
8
+ require 'rubigen'
9
+ end
10
+ require 'rubigen/scripts/generate'
11
+
12
+ ARGV.shift if ['--help', '-h'].include?(ARGV[0])
13
+ RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
14
+ RubiGen::Scripts::Generate.new.run(ARGV)
@@ -0,0 +1,3 @@
1
+ require 'stringio'
2
+ require 'test/unit'
3
+ require File.dirname(__FILE__) + '/../lib/pyapns'
@@ -0,0 +1,11 @@
1
+ require File.dirname(__FILE__) + '/test_helper.rb'
2
+
3
+ class TestPYAPNS < Test::Unit::TestCase
4
+
5
+ def setup
6
+ end
7
+
8
+ def test_truth
9
+ assert true
10
+ end
11
+ end
metadata ADDED
@@ -0,0 +1,256 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pyapns
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Samuel Webster Sutch
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2010-01-29 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rubyforge
17
+ type: :development
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.0.3
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: gemcutter
27
+ type: :development
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 0.3.0
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: hoe
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 2.5.0
44
+ version:
45
+ description: |-
46
+ :title: The Ruby API
47
+
48
+ :section: PYAPNS::Client
49
+ There's python in my ruby!
50
+
51
+ This is a class used to send notifications, provision applications and
52
+ retrieve feedback using the Apple Push Notification Service.
53
+
54
+ PYAPNS is a multi-application APS provider, meaning it is possible to send
55
+ notifications to any number of different applications from the same application
56
+ and same server. It is also possible to scale the client to any number
57
+ of processes and servers, simply balanced behind a simple web proxy.
58
+
59
+ It may seem like overkill for such a bare interface - after all, the
60
+ APS service is rather simplistic. However, PYAPNS takes no shortcuts when it
61
+ comes to completeness/compliance with the APNS protocol and allows the
62
+ user many optimization and scaling vectors not possible with other libraries.
63
+ No bandwidth is wasted, connections are persistent and the server is
64
+ asynchronous therefore notifications are delivered immediately.
65
+
66
+ PYAPNS takes after the design of 3rd party push notification service that
67
+ charge a fee each time you push a notification, and charge extra for so-called
68
+ 'premium' service which supposedly gives you quicker access to the APS servers.
69
+ However, PYAPNS is free, as in beer and offers more scaling opportunities without
70
+ the financial draw.
71
+
72
+ :section: Provisioning
73
+
74
+ To add your app to the PYAPNS server, it must be `provisioned` at least once.
75
+ Normally this is done once upon the start-up of your application, be it a web
76
+ service, desktop application or whatever... It must be done at least once
77
+ to the server you're connecting to. Multiple instances of PYAPNS will have
78
+ to have their applications provisioned individually. To provision an application
79
+ manually use the `PYAPNS::Client#provision` method.
80
+
81
+ require 'pyapns'
82
+ client = PYAPNS::Client.configure
83
+ client.provision :app_id => 'cf', :cert => '/home/ss/cert.pem', :env => 'sandbox', :timeout => 15
84
+
85
+ This basically says "add an app reference named 'cf' to the server and start
86
+ a connection using the certification, and if it can't within 15 seconds,
87
+ raise a `PYAPNS::TimeoutException`
88
+
89
+ That's all it takes to get started. Of course, this can be done automatically
90
+ by using PYAPNS::ClientConfiguration middleware. `PYAPNS::Client` is a singleton
91
+ class that is configured using the class method `PYAPNS::Client#configure`. It
92
+ is sensibly configured by default, but can be customized by specifying a hash
93
+ See the docs on `PYAPNS::ClientConfiguration` for a list of available configuration
94
+ parameters (some of these are important, and you can specify initial applications)
95
+ to be configured by default.
96
+
97
+ :section: Sending Notifications
98
+
99
+ Once your client is configured, and application provisioned (again, these
100
+ should be taken care of before you write notification code) you can begin
101
+ sending notifications to users. If you're wondering how to acquire a notification
102
+ token, you've come to the wrong place... I recommend using google. However,
103
+ if you want to send hundreds of millions of notifications to users, here's how
104
+ it's done, one at a time...
105
+
106
+ The `PYAPNS::Client#notify` is a sort of polymorphic method which can notify
107
+ any number of devices at a time. It's basic form is as follows:
108
+
109
+ client.notify 'cf', 'long ass app token', {:aps=> {:alert => 'hello?'}}
110
+
111
+ However, as stated before, it is sort of polymorphic:
112
+
113
+ client.notify 'cf', ['token', 'token2', 'token3'], [alert, alert2, alert3]
114
+
115
+ client.notify :app_id => 'cf', :tokens => 'mah token', :notifications => alertHash
116
+
117
+ client.notify 'cf', 'token', PYAPNS::Notification('hello tits!')
118
+
119
+ As you can see, the method accepts paralell arrays of tokens and notifications
120
+ meaning any number of notifications can be sent at once. Hashes will be automatically
121
+ converted to `PYAPNS::Notification` objects so they can be optimized for the wire
122
+ (nil values removed, etc...), and you can pass `PYAPNS::Notification` objects
123
+ directly if you wish.
124
+
125
+ :section: Retrieving Feedback
126
+
127
+ The APS service offers a feedback functionality that allows application servers
128
+ to retrieve a list of device tokens it deems to be no longer in use, and the
129
+ time it thinks they stopped being useful (the user uninstalled your app, better
130
+ luck next time...) Sounds pretty straight forward, and it is. Apple recommends
131
+ you do this at least once an hour. PYAPNS will return a list of 2-element lists
132
+ with the date and the token:
133
+
134
+ feedbacks = client.feedback 'cf'
135
+
136
+ :section: Asynchronous Calls
137
+
138
+ PYAPNS::Client will, by default, perform no funny stuff and operate entirely
139
+ within the calling thread. This means that certain applications may hang when,
140
+ say, sending a notification, if only for a fraction of a second. Obviously
141
+ not a desirable trait, all `provision`, `feedback` and `notify`
142
+ methods also take a block, which indicates to the method you want to call
143
+ PYAPNS asynchronously, and it will be done so handily in another thread, calling
144
+ back your block with a single argument when finished. Note that `notify` and `provision`
145
+ return absolutely nothing (nil, for you rub--wait you are ruby developers!).
146
+ It is probably wise to always use this form of operation so your calling thread
147
+ is never blocked (especially important in UI-driven apps and asynchronous servers)
148
+ Just pass a block to provision/notify/feedback like so:
149
+
150
+ PYAPNS::Client.instance.feedback do |feedbacks|
151
+ feedbacks.each { |f| trim_token f }
152
+ end
153
+
154
+ :section: PYAPNS::ClientConfiguration
155
+ A middleware class to make `PYAPNS::Client` easy to use in web contexts
156
+
157
+ Automates configuration of the client in Rack environments
158
+ using a simple confiuration middleware. To use `PYAPNS::Client` in
159
+ Rack environments with the least code possible `use PYAPNS::ClientConfiguration`
160
+ (no, really, in some cases, that's all you need!) middleware with an optional
161
+ hash specifying the client variables. Options are as follows:
162
+
163
+ use PYAPNS::ClientConfiguration(
164
+ :host => 'http://localhost/'
165
+ :port => 7077,
166
+ :initial => [{
167
+ :app_id => 'myapp',
168
+ :cert => '/home/myuser/apps/myapp/cert.pem',
169
+ :env => 'sandbox',
170
+ :timeout => 15
171
+ }])
172
+
173
+ Where the configuration variables are defined:
174
+
175
+ :host String the host where the server can be found
176
+ :port Number the port to which the client should connect
177
+ :initial Array OPTIONAL - an array of INITIAL hashes
178
+
179
+ INITIAL HASHES:
180
+
181
+ :app_id String the id used to send messages with this certification
182
+ can be a totally arbitrary value
183
+ :cert String a path to the certification or the certification file
184
+ as a string
185
+ :env String the environment to connect to apple with, always
186
+ either 'sandbox' or 'production'
187
+ :timoeut Number The timeout for the server to use when connecting
188
+ to the apple servers
189
+
190
+ :section: PYAPNS::Notification
191
+ An APNS Notification
192
+
193
+ You can construct notification objects ahead of time by using this class.
194
+ However unnecessary, it allows you to programmatically generate a Notification
195
+ like so:
196
+
197
+ note = PYAPNS::Notification.new 'alert text', 9, 'flynn.caf', {:extra => 'guid'}
198
+
199
+ -- or --
200
+ note = PYAPNS::Notification.new 'alert text'
201
+
202
+ These can be passed to `PYAPNS::Client#notify` the same as hashes
203
+ email:
204
+ - samuraiblog@gmail.com
205
+ executables: []
206
+
207
+ extensions: []
208
+
209
+ extra_rdoc_files:
210
+ - History.txt
211
+ - Manifest.txt
212
+ - PostInstall.txt
213
+ files:
214
+ - History.txt
215
+ - Manifest.txt
216
+ - PostInstall.txt
217
+ - README.rdoc
218
+ - Rakefile
219
+ - lib/pyapns.rb
220
+ - script/console
221
+ - script/destroy
222
+ - script/generate
223
+ - test/test_helper.rb
224
+ - test/test_pyapns.rb
225
+ has_rdoc: true
226
+ homepage: http://pyapns.org
227
+ licenses: []
228
+
229
+ post_install_message:
230
+ rdoc_options:
231
+ - --main
232
+ - README.rdoc
233
+ require_paths:
234
+ - lib
235
+ required_ruby_version: !ruby/object:Gem::Requirement
236
+ requirements:
237
+ - - ">="
238
+ - !ruby/object:Gem::Version
239
+ version: "0"
240
+ version:
241
+ required_rubygems_version: !ruby/object:Gem::Requirement
242
+ requirements:
243
+ - - ">="
244
+ - !ruby/object:Gem::Version
245
+ version: "0"
246
+ version:
247
+ requirements: []
248
+
249
+ rubyforge_project: pyapns
250
+ rubygems_version: 1.3.5
251
+ signing_key:
252
+ specification_version: 3
253
+ summary: ":title: The Ruby API :section: PYAPNS::Client There's python in my ruby! This is a class used to send notifications, provision applications and retrieve feedback using the Apple Push Notification Service"
254
+ test_files:
255
+ - test/test_helper.rb
256
+ - test/test_pyapns.rb