{ "info": { "author": "Juan Luis Boya Garc\u00eda", "author_email": "ntrrgc@gmail.com", "bugtrack_url": null, "classifiers": [ "Development Status :: 3 - Alpha", "Intended Audience :: Developers", "License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)", "Natural Language :: English", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Topic :: Software Development :: Libraries :: Python Modules" ], "description": ".. image:: https://raw.githubusercontent.com/ntrrgc/snorky/master/doc/logo.png\n\nSnorky is a framework for building WebSocket servers based on patterns.\n\nSnorky runs on top of `Tornado `_ a fast, performant, asynchronous web server. Snorky is intended to run as a separated process, therefore being able to communicate with web applications written in any programming language or web framework.\n\nYou can use Snorky DataSync service to synchronize a server-side database with a web view. You only need to add hooks somewhere (e.g. in an ORM layer) so that Snorky is notified of them. Clients need a *subscription token* in order to get data from Snorky.\n\nSnorky integrates in the server side with `Django `_ ORM and `Django REST Framework `_ in order to streamline this process, but you can use it with any server technology with a bit more coding. On the client side, Snorky provides a JavaScript library that handles connections and notifications. You can also connect it easily with client-side MVC-like frameworks like `AngularJS `_ in order to close the gap between server and client MVC.\n\nYou can use the Snorky architecture of self-contained services with an RPC over JSON interface to add new functionality other than data entities synchronization: e.g. PubSub, person to person chat or cursor synchronization.\n\nInstallation\n============\n\nTo run a Snorky server you only need a Python interpreter (both Python 2 and Python 3 are fine) and a few dependencies.\n\nYou can install Snorky from the Python package index:\n\n.. code:: bash\n\n pip install snorky\n\nDocumentation\n=============\n\nSnorky documentation is hosted in *Read the Docs*. You can check it out at http://snorky.readthedocs.org/.\n\nSimple PubSub server\n====================\n\nSnorky groups functionality in *services*, which are classes intended to attend user events in different ways. The following code shows a Snorky server with an PubSub service.\n\n.. code:: python\n\n from tornado.ioloop import IOLoop\n from tornado.web import Application\n from snorky import ServiceRegistry\n\n from snorky.request_handlers.websocket import SnorkyWebSocketHandler\n from snorky.services.pubsub import PubSubService\n\n if __name__ == \"__main__\":\n service_registry = ServiceRegistry()\n # Every service instance has a name, here: pubsub\n service_registry.register_service(PubSubService(\"pubsub\"))\n\n # Register HTTP endpoint: ws://localhost:8002/websocket\n application = Application([\n # Each endpoint connects clients with the services of a registry\n SnorkyWebSocketHandler.get_route(service_registry, \"/websocket\"),\n ])\n application.listen(8002, address=\"\") # listen on all network interfaces\n\n try:\n print(\"Snorky running...\")\n IOLoop.instance().start()\n except KeyboardInterrupt:\n pass\n\nThis is the HTML code of a minimal application making use of this service:\n\n.. code:: html\n\n \n \n \n Snorky is easy\n \n \n \n \n
\n \n \n
\n
    \n
\n \n \n \n\nThis is the JavaScript code:\n\n.. code:: javascript\n\n var snorky = new Snorky(WebSocket, \"ws://localhost:8002/websocket\", {\n \"pubsub\": Snorky.PubSub\n });\n var pubsub = snorky.services.pubsub;\n\n pubsub.subscribe({channel: 'messages'})\n .then(function() {\n // Confirmation received! (optional)\n });\n\n pubsub.messagePublished.add(function(messageObject) {\n $('#messages').append(\n $('
  • ', {\n text: messageObject.message\n }));\n });\n\n $('form').on('submit', function(event) {\n event.preventDefault(); // don't reload the page\n\n pubsub.publish({\n channel: 'messages',\n message: $('#message').val()\n });\n });\n\nDataSync service with Django and Angular\n========================================\n\nThe following code shows a Django model integrated with Snorky. The ``@subscribable`` decorator adds event handlers that send notifications to the Snorky server configured in Django's ``settings.py`` file.\n\n.. code:: python\n\n from django.db import models\n from snorky.backend.django import subscribable\n\n @subscribable\n class Task(models.Model):\n title = models.CharField(max_length=100)\n completed = models.BooleanField(default=False)\n\n def jsonify(self):\n # This is the model representation sent to Snorky\n # In this case it is generated by Django REST Framework,\n # but it could a simple `return json.dumps(...)`.\n from .serializers import TaskSerializer\n return TaskSerializer(self).data\n\nThe following code shows the Snorky server. It contains two registries, a frontend one (public), which is exposed to the end users and a backend one (private) who is exposed only to the server applications, protected by a password.\n\n.. code:: python\n\n #-----------------------------------------------------------------------------#\n # Dealers (model classes and filters) #\n #-----------------------------------------------------------------------------#\n\n class AllTodos(BroadcastDealer):\n name = \"AllTasks\"\n model = \"Task\"\n\n #-----------------------------------------------------------------------------#\n # Server startup #\n #-----------------------------------------------------------------------------#\n if __name__ == \"__main__\":\n # Create two services\n datasync = DataSyncService(\"datasync\", [AllTodos])\n datasync_backend = DataSyncBackend(\"datasync_backend\", datasync)\n\n logging.basicConfig(level=logging.INFO)\n\n # Register the frontend and backend services in different handlers\n frontend = ServiceRegistry([datasync])\n backend = ServiceRegistry([datasync_backend])\n\n # Create a WebSocket frontend\n app_frontend = Application([\n SnorkyWebSocketHandler.get_route(frontend, \"/ws\"),\n ])\n app_frontend.listen(5001)\n\n # Create a backend, set a secret key, port and address\n app_backend = Application([\n (\"/backend\", BackendHTTPHandler, {\n \"service_registry\": backend,\n \"api_key\": \"swordfish\"\n })\n ])\n app_backend.listen(5002)\n\n # Start processing\n try:\n IOLoop.instance().start()\n except KeyboardInterrupt:\n pass\n\n\nDealers, like ``AllTodos`` are classes that track client subscriptions to certain kinds of models. There are several kinds of dealers. *Broadcast* dealers notify of all changes to all subscribers, but there are other dealers that allow to specify arbitrary filtering.\n\nData change notifications are sent from Django ORM to the ``DataSyncBackend`` service in the backend registry, accessible through port 5002. Clients connect to receive notifications to the ``DataSyncService`` from the frontend registry, accessible through port 5001.\n\nThis is the API views file, built with `Django REST Framework `_. It supports ``GET``, ``POST``, ``PUT`` and ``DELETE``.\n\n.. code:: python\n\n from . import models\n from rest_framework import viewsets\n import snorky.backend.django.rest_framework as snorky\n\n class TaskViewSet(snorky.ListSubscribeModelMixin,\n viewsets.ModelViewSet):\n model = models.Task\n dealer = \"AllTasks\"\n\nUsing ``ListSubscribeModelMixin``, the view will accept an optional HTTP header, ``X-Snorky: Subscribe`` allowing the client to request a *subscription token* that can be exchanged for real time notifications over WebSocket.\n\nFinally, the following code shows how data can be fetched in AngularJS, in this case querying the REST API with `Restangular `_:\n\n.. code:: javascript\n\n var snorky = new Snorky(WebSocket, \"ws://localhost:5001/ws\", {\n \"datasync\": Snorky.DataSync\n });\n var deltaProcessor = new Snorky.DataSync.CollectionDeltaProcessor();\n snorky.services.datasync.onDelta = function(delta) {\n // Called each time a data change notification (delta) is received.\n // CollectionDeltaProcessor is a class that applies these deltas\n // in a collection (usually an array).\n deltaProcessor.processDelta(delta);\n\n // Here we could also inspect the delta element and show alerts to the\n // user or play a sound when data changes.\n };\n\n var tasks = Restangular.all(\"tasks\").getListAndSubscription()\n .then(function(response) {\n var taskArray = response.data;\n\n // A collection wraps an array over an interface which is understood\n // by deltaProcessor.\n //\n // e.g. when an insertion delta is received, deltaProcessor will push\n // an element in the collection.\n //\n var taskCollection = new Snorky.DataSync.ArrayCollection(taskArray, {\n transformItem: function(item) {\n // Allows us to define how a data element received from a delta as\n // simple JSON will be translated to an element of this array.\n\n // This is useful if we use fat elements (e.g. each element has a\n // .delete() method).\n return Restangular.restangularizeElement(\n null, item, \"tasks\", true, response.data, null\n );\n }\n })\n\n // Tell the collection delta processor: updates of elements of class Task\n // should be applied to taskCollection.\n deltaProcessor.collections[\"Task\"] = taskCollection;\n\n // Send our new subscription token to Snorky, so that we can receive\n // notifications for changes in tasks.\n snorky.services.datasync.acquireSubscription({\n token: response.subscriptionToken\n });\n\n // Return the array, which will be automatically updated thanks to\n // Snorky deltaProcessor.\n return taskArray;\n });\n\n``.getListAndSubscription()`` is an `extension method `_ that adds the ``X-Snorky: Subscribe`` header to the request and puts the content of the ``X-Subscription-Token`` response header in ``response.subscriptionToken``. Changes to taskArray will be automatically detected by AngularJS and will trigger the template code to update the view.\n\nThe following code shows how this array of tasks could be used in an AngularJS template:\n\n.. code:: html\n\n
      \n
    • \n
      \n \n\n \n\n \n
      \n
    • \n
    \n\nThe full demo code is available in `snorky/demos/snorky_todo`, based on `TodoMVC `_.\n\nOther protocols\n===============\n\nAlthough Snorky was built upon WebSocket, there is nothing in it preventing you to use other protocols. Indeed, Snorky comes with a `SockJS `_ so that you can use it with jurassic browsers (IE6+) with no WebSocket support, should you ever need that.\n\nLicense\n=======\n\nSnorky is licensed under the terms of `Mozilla Public License 2.0 `_.\n\nThis means you can use the software in both free and proprietary works of any other license without restrictions.\n\nIn case you modify the library code **and** make it available to others, those modifications are covered by the license too, which implies you must make source code available **for the modified library files**. This does not forbid you from developing extensions with other licenses though, as long as they don't modify Snorky source code or maintain the MPL license for these parts.", "description_content_type": null, "docs_url": null, "download_url": "UNKNOWN", "downloads": { "last_day": -1, "last_month": -1, "last_week": -1 }, "home_page": "http://snorkyproject.org", "keywords": null, "license": "MPL 2.0", "maintainer": null, "maintainer_email": null, "name": "snorky", "package_url": "https://pypi.org/project/snorky/", "platform": "UNKNOWN", "project_url": "https://pypi.org/project/snorky/", "project_urls": { "Download": "UNKNOWN", "Homepage": "http://snorkyproject.org" }, "release_url": "https://pypi.org/project/snorky/0.1.0a4/", "requires_dist": null, "requires_python": null, "summary": "Framework for developing WebSocket servers", "version": "0.1.0a4" }, "last_serial": 1931469, "releases": { "0.1.0a2": [ { "comment_text": "", "digests": { "md5": "ed48c481c9b087bdaf44d4c8af2d5a3b", "sha256": "74a00e83f84d0753ab505b31e09d9aa6559c40f377f90ffce800baf4359cc321" }, "downloads": -1, "filename": "snorky-0.1.0a2.tar.gz", "has_sig": false, "md5_digest": "ed48c481c9b087bdaf44d4c8af2d5a3b", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 43759, "upload_time": "2015-04-12T16:06:46", "url": "https://files.pythonhosted.org/packages/36/40/ed4529a3bc8285f3f3ea406493101a438d44e3db36eeb02d1c1becbae564/snorky-0.1.0a2.tar.gz" } ], "0.1.0a3": [ { "comment_text": "", "digests": { "md5": "01e853bf4fd180ec879f504f46bf61d1", "sha256": "d8ea9ab441bf9a4d5c85a9dbae6c9606742e524bf95891c2044cca16cab3dfd9" }, "downloads": -1, "filename": "snorky-0.1.0a3.tar.gz", "has_sig": false, "md5_digest": "01e853bf4fd180ec879f504f46bf61d1", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 44557, "upload_time": "2015-04-14T01:16:23", "url": "https://files.pythonhosted.org/packages/a3/42/7fe93202de94bd6b614a94b715e1e517f434aa4ce413eb7e050fefd21269/snorky-0.1.0a3.tar.gz" } ], "0.1.0a4": [ { "comment_text": "", "digests": { "md5": "64ed51072f600204536b219a1a34f1d5", "sha256": "d4d41a7c81113fcf0455bd8d52618a722cd47bdda7aa3aefeeb92f399c7bb084" }, "downloads": -1, "filename": "snorky-0.1.0a4.tar.gz", "has_sig": false, "md5_digest": "64ed51072f600204536b219a1a34f1d5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 44419, "upload_time": "2016-01-30T22:01:04", "url": "https://files.pythonhosted.org/packages/00/9d/cca31481fc9bad107fbf27140644b415bf9a826992bfe47d49afcc9df5f6/snorky-0.1.0a4.tar.gz" } ] }, "urls": [ { "comment_text": "", "digests": { "md5": "64ed51072f600204536b219a1a34f1d5", "sha256": "d4d41a7c81113fcf0455bd8d52618a722cd47bdda7aa3aefeeb92f399c7bb084" }, "downloads": -1, "filename": "snorky-0.1.0a4.tar.gz", "has_sig": false, "md5_digest": "64ed51072f600204536b219a1a34f1d5", "packagetype": "sdist", "python_version": "source", "requires_python": null, "size": 44419, "upload_time": "2016-01-30T22:01:04", "url": "https://files.pythonhosted.org/packages/00/9d/cca31481fc9bad107fbf27140644b415bf9a826992bfe47d49afcc9df5f6/snorky-0.1.0a4.tar.gz" } ] }