Skip to content

Commit

Permalink
Merge branch 'ruby-client'
Browse files Browse the repository at this point in the history
  • Loading branch information
Samuel Sutch committed Sep 24, 2010
2 parents f26b2c6 + 3174c56 commit e6230c2
Show file tree
Hide file tree
Showing 13 changed files with 689 additions and 6 deletions.
161 changes: 161 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Features:
<ul>
<li>XML-RPC Based, works with any client in any language</li>
<li>Native Python API with Django and Pylons support</li>
<li>Native Ruby API with Rails/Rack support</li>
<li>Scalable, fast and easy to distribute behind a proxy</li>
<li>Based on Twisted</li>
<li>Multi-application and dual environment support</li>
Expand Down Expand Up @@ -176,3 +177,163 @@ Each of these functions can be called synchronously and asynchronously. To make
app_id the app_id to query
Returns:
Feedback tuples like [(datetime_expired, token_str), ...]


## The Ruby API

###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.

PYAPNS is a multi-application APS provider, meaning it is possible to send
notifications to any number of different applications from the same application
and same server. It is also possible to scale the client to any number
of processes and servers, simply balanced behind a simple web proxy.

It may seem like overkill for such a bare interface - after all, the
APS service is rather simplistic. However, PYAPNS takes no shortcuts when it
comes to completeness/compliance with the APNS protocol and allows the
user many optimization and scaling vectors not possible with other libraries.
No bandwidth is wasted, connections are persistent and the server is
asynchronous therefore notifications are delivered immediately.

PYAPNS takes after the design of 3rd party push notification service that
charge a fee each time you push a notification, and charge extra for so-called
'premium' service which supposedly gives you quicker access to the APS servers.
However, PYAPNS is free, as in beer and offers more scaling opportunities without
the financial draw.

###Provisioning

To add your app to the PYAPNS server, it must be `provisioned` at least once.
Normally this is done once upon the start-up of your application, be it a web
service, desktop application or whatever... It must be done at least once
to the server you're connecting to. Multiple instances of PYAPNS will have
to have their applications provisioned individually. To provision an application
manually use the `PYAPNS::Client#provision` method.

require 'pyapns'
client = PYAPNS::Client.configure
client.provision :app_id => 'cf', :cert => '/home/ss/cert.pem', :env => 'sandbox', :timeout => 15

This basically says "add an app reference named 'cf' to the server and start
a connection using the certification, and if it can't within 15 seconds,
raise a `PYAPNS::TimeoutException`

That's all it takes to get started. Of course, this can be done automatically
by using PYAPNS::ClientConfiguration middleware. `PYAPNS::Client` is a singleton
class that is configured using the class method `PYAPNS::Client#configure`. It
is sensibly configured by default, but can be customized by specifying a hash
See the docs on `PYAPNS::ClientConfiguration` for a list of available configuration
parameters (some of these are important, and you can specify initial applications)
to be configured by default.

###Sending Notifications

Once your client is configured, and application provisioned (again, these
should be taken care of before you write notification code) you can begin
sending notifications to users. If you're wondering how to acquire a notification
token, you've come to the wrong place... I recommend using google. However,
if you want to send hundreds of millions of notifications to users, here's how
it's done, one at a time...

The `PYAPNS::Client#notify` is a sort of polymorphic method which can notify
any number of devices at a time. It's basic form is as follows:

client.notify 'cf', 'long ass app token', {:aps=> {:alert => 'hello?'}}

However, as stated before, it is sort of polymorphic:

client.notify 'cf', ['token', 'token2', 'token3'], [alert, alert2, alert3]

client.notify :app_id => 'cf', :tokens => 'mah token', :notifications => alertHash

client.notify 'cf', 'token', PYAPNS::Notification('hello tits!')

As you can see, the method accepts paralell arrays of tokens and notifications
meaning any number of notifications can be sent at once. Hashes will be automatically
converted to `PYAPNS::Notification` objects so they can be optimized for the wire
(nil values removed, etc...), and you can pass `PYAPNS::Notification` objects
directly if you wish.

###Retrieving Feedback

The APS service offers a feedback functionality that allows application servers
to retrieve a list of device tokens it deems to be no longer in use, and the
time it thinks they stopped being useful (the user uninstalled your app, better
luck next time...) Sounds pretty straight forward, and it is. Apple recommends
you do this at least once an hour. PYAPNS will return a list of 2-element lists
with the date and the token:

feedbacks = client.feedback 'cf'

###Asynchronous Calls

PYAPNS::Client will, by default, perform no funny stuff and operate entirely
within the calling thread. This means that certain applications may hang when,
say, sending a notification, if only for a fraction of a second. Obviously
not a desirable trait, all `provision`, `feedback` and `notify`
methods also take a block, which indicates to the method you want to call
PYAPNS asynchronously, and it will be done so handily in another thread, calling
back your block with a single argument when finished. Note that `notify` and `provision`
return absolutely nothing (nil, for you rub--wait you are ruby developers!).
It is probably wise to always use this form of operation so your calling thread
is never blocked (especially important in UI-driven apps and asynchronous servers)
Just pass a block to provision/notify/feedback like so:

PYAPNS::Client.instance.feedback do |feedbacks|
feedbacks.each { |f| trim_token f }
end

###PYAPNS::ClientConfiguration
A middleware class to make `PYAPNS::Client` easy to use in web contexts

Automates configuration of the client in Rack environments
using a simple confiuration middleware. To use `PYAPNS::Client` in
Rack environments with the least code possible `use PYAPNS::ClientConfiguration`
(no, really, in some cases, that's all you need!) middleware with an optional
hash specifying the client variables. Options are as follows:

use PYAPNS::ClientConfiguration(
:host => 'http://localhost/'
:port => 7077,
:initial => [{
:app_id => 'myapp',
:cert => '/home/myuser/apps/myapp/cert.pem',
:env => 'sandbox',
:timeout => 15
}])

Where the configuration variables are defined:

:host String the host where the server can be found
:port Number the port to which the client should connect
:initial Array OPTIONAL - an array of INITIAL hashes

INITIAL HASHES:

:app_id String the id used to send messages with this certification
can be a totally arbitrary value
:cert String a path to the certification or the certification file
as a string
:env String the environment to connect to apple with, always
either 'sandbox' or 'production'
:timoeut Number The timeout for the server to use when connecting
to the apple servers

###PYAPNS::Notification
An APNS Notification

You can construct notification objects ahead of time by using this class.
However unnecessary, it allows you to programmatically generate a Notification
like so:

note = PYAPNS::Notification.new 'alert text', 9, 'flynn.caf', {:extra => 'guid'}

-- or --
note = PYAPNS::Notification.new 'alert text'

These can be passed to `PYAPNS::Client#notify` the same as hashes

13 changes: 7 additions & 6 deletions extra/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ <h3>Recent Commits</h3>
<li>Simplified feedback interface</li>
</ul>

<h2>Usage</h2>
<p>pyapns is an APNS provider that you install on your server and access through XML-RPC. To install you will need Python, <a href="http://pypi.python.org/pypi/Twisted">Twisted</a> and <a href="http://pypi.python.org/pypi/pyOpenSSL">pyOpenSSL</a>. It's also recommended to install <a href="http://pypi.python.org/pypi/python-epoll/">python-epoll</a> for best performance (if epoll is not available, like on Mac OS X, you may want to use another library, like <a href="http://pypi.python.org/pypi/py-kqueue/2.0.1">py-kqueue</a>). If you like easy_install try (it should take care of the dependancies for you):</p>

<pre class="prettyprint lang-bsh"><code>$ sudo easy_install pyapns
Expand Down Expand Up @@ -127,19 +128,21 @@ <h3>Retrieving Inactive Tokens</h3>

<p>Call <code>feedback</code> with the <code>app_id</code>. A list of tuples will be retrieved from the APNS server that it deems inactive. These are returned as a list of 2-element lists with a <code>Datetime</code> object and the token string.</p>

<h3>XML-RPC Methods</h3>
<h2>XML-RPC Methods</h2>

<p>These methods can be called on the server you started the server on. Be sure you are not including <code>/RPC2</code> in the URL.</p>

<h3><code>provision</code></h3>
<h2><code>provision</code></h2>

<pre class="ignore"><code> Arguments
app_id String the application id for the provided
certification
cert String a path to a .pem file or the a
string with the entie file
environment String the APNS server to use - either
'production' or 'sandbox'
'production' or 'sandbox
timeout Integer timeout for connection attempts to
the APS servers'
Returns
None
</code></pre>
Expand All @@ -154,8 +157,6 @@ <h3><code>notify</code></h3>
notifications Str/Arr an Array of notification
dictionaries or a single
notification dictionary
timeout Integer timeout for connection attempts to
the APS servers

Returns
None
Expand All @@ -171,7 +172,7 @@ <h3><code>feedback</code></h3>
Array(Array(Datetime(time_expired), String(token)), ...)
</code></pre>

<h3>The Python API</h3>
<h2>The Python API</h2>

<p>pyapns also provides a Python API that makes the use of pyapns even simpler. The Python API must be configured before use but configuration files make it easier. The pyapns <code>client</code> module currently supports configuration from Django settings and Pylons config. To configure using Django, the following must be present in your settings file:</p>

Expand Down
4 changes: 4 additions & 0 deletions ruby-client/pyapns/History.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
=== 0.3.0 2010-01-22

* 1 major enhancement:
* Initial release
11 changes: 11 additions & 0 deletions ruby-client/pyapns/Manifest.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
History.txt
Manifest.txt
PostInstall.txt
README.rdoc
Rakefile
lib/pyapns.rb
script/console
script/destroy
script/generate
test/test_helper.rb
test/test_pyapns.rb
7 changes: 7 additions & 0 deletions ruby-client/pyapns/PostInstall.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@

For more information on pyapns, see http://pyapns.rubyforge.org

NOTE: Change this information in PostInstall.txt
You can also delete it if you don't want it.


48 changes: 48 additions & 0 deletions ruby-client/pyapns/README.rdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
= pyapns

* http://pyapns.org

== DESCRIPTION:

FIX (describe your package)

== FEATURES/PROBLEMS:

* FIX (list of features or problems)

== SYNOPSIS:

FIX (code sample of usage)

== REQUIREMENTS:

* FIX (list of requirements)

== INSTALL:

* FIX (sudo gem install, anything else)

== LICENSE:

(The MIT License)

Copyright (c) 2010 Samuel Webster Sutch

Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
'Software'), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
23 changes: 23 additions & 0 deletions ruby-client/pyapns/Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require 'rubygems'
gem 'hoe', '>= 2.1.0'
require 'hoe'
require 'fileutils'
require './lib/pyapns'

Hoe.plugin :newgem
# Hoe.plugin :website
# Hoe.plugin :cucumberfeatures

# Generate all the Rake tasks
# Run 'rake -T' to see list of generated tasks (from gem root directory)
$hoe = Hoe.spec 'pyapns' do
self.developer 'Samuel Webster Sutch', '[email protected]'
self.rubyforge_name = self.name
end

require 'newgem/tasks'
Dir['tasks/**/*.rake'].each { |t| load t }

# TODO - want other tests/tasks run by default? Add them to the list
# remove_task :default
# task :default => [:spec, :features]
Loading

0 comments on commit e6230c2

Please sign in to comment.