=====================================
How to use JavaScript backend of PyPy
=====================================

Author:
=======

Maciej Fijalkowski, fijal@genesilico.pl

Purpose:
========

This tutorial explains how to use `PyPy`_'s JavaScript backend. The
reader should have some previous knowledge of writing `RPython`_
programs.

The JavaScript backend lets you write `RPython`_ source code which gets
translated into JavaScript and executed in a browser. The resulting
JavaScript code should not depend on a browser (this is a backend
responsibility) and should integrate as much as possible with a web
server.

.. _`PyPy`: http://codespeak.net/pypy
.. _`RPython`: http://codespeak.net/pypy/dist/pypy/doc/coding-guide.html#restricted-python

Getting started:
----------------

You can use a JavaScript backend in many different ways. One of those is
to simply run `jscompile`_ from the 'bin' directory with a module name and a
list of functions, this makes that the listed functions get compiled. Another
way is to use the rpython2javascript function from `js.main`_ to generate
your javascript on-the-fly (e.g. when serving a web page).

.. _`jscompile`: http://codespeak.net/svn/pypy/dist/pypy/bin/jscompile.py
.. _`js.main`: http://codespeak.net/svn/pypy/dist/pypy/translator/js/main.py

Here is a simple example using `MochiKit`_'s logDebug call to show a
message::

  from pypy.translator.js.modules import mochikit

  def simple_example():
      mochikit.createLoggingPane(True)
      mochikit.logDebug("Hello")

Save this to a python file, e.g. simpledemo.py. Then use jscompile to
create a javascript version::

  $ python pypy/bin/jscompile.py simpledemo simple_example

Note that you specify the module as a python module which must be on the
python path.

When you run this you should see some information on the compilation
process scrolling past. The JavaScript is written to a file which looks
something like
`/tmp/usession-1/some_strange_function_which_will_never_be_called.js`.

You can call the compilation process programatically using
rpython2javascript::

  from pypy.translator.js.main import rpython2javascript
  
  import simpledemo
  
  js_src = rpython2javascript(simpledemo, ["simple_example"])
  print js_src

.. _`MochiKit`: http://www.mochikit.com/
.. _`rpython2javascript`: 

Easy AJAX:
----------

The idea is simple: Normal python method calls get rendered as
communication over `XMLHttpRequest`_ without too much user intervention.
To achieve this, you must tell PyPy that *these* particular method calls
need to be rendered in this way.

To achieve this, first you must subclass BasicExternal from `bltregistry`_ and
provide an instance of it to RPython code (for instance as global variable).
Then you need to set class instance's `_render_xmlhttp` to `True`, to tell the
JS backend to render it using xmlhttp communication. Because the web server
code does not need to be rpython, you have to supply some way of telling PyPy
what types can be returned out of it. This is done using the decorator
`@callback(retval = <used type>)` from `bltregistry`_. For example you may
provide::

  from pypy.translator.js.lib.support import callback

  @callback(retval = {str:str})
  def some_fun(self, some_arg = 3):
    ....

The decorator tells the compiler that this function will return mapping from
string to string and will take integer as an argument. You can simply specify
the arguments using keyword arguments with an example, or pass an args
dictionary to the described decorator.

Then you must supply a method which returns JSON data in the server
itself (for example, this is done automatically by `TurboGears`_ using
the `@expose(format='json')` decorator).

.. _`XMLHttpRequest`: http://en.wikipedia.org/wiki/XMLHttpRequest
.. _`TurboGears`: http://www.turbogears.org/
.. _`bltregistry`: http://codespeak.net/svn/pypy/dist/pypy/rpython/ootypesystem/bltregistry.py

Ajax Ping Example:
------------------

To create a simple javascript method which pings a server you need two parts, a server side python class which knows how to talk via XMLHttpRequest to a client side call.

On the server side::

  from pypy.rpython.ootypesystem.bltregistry import BasicExternal
  from pypy.translator.js.lib.support import callback
  
  class PingHandler(BasicExternal):
      _render_xmlhttp = True

      @callback(retval={str:str})
      def ping(self, ping_str="aa"):
          return dict(response="PONG: %s" % ping_str)

  ping_handler = PingHandler()    

This uses the BasicExternal class and the described decorator to let PyPy know
how to deal with the input and output of the methods. Now you can use an
instance of this class to pass to other parts of the RPython code.

On the client you call the ping_handler.ping method with a callback::

  from pypy.translator.js.modules import mochikit
  from somewhere import ping_handler
  
  def callback(response):
      mochikit.logDebug("Got response: " + response["response"])
  
  def ping():
      ping_handler.ping("PING", callback)

You compile this to JavaScript using jscompile or rpython2javascript and
the ping method. The resulting javascript is passed to a web browser,
while there needs to be a server which knows how to deal with a HTTP
request to `/ping` (the name is derived from the name of the method
decorated with described). The server then calls the `ping_handler.ping`
method with the data from the call and returns a JSON dictionary.

Integration with TurboGears or any other web framework:
-------------------------------------------------------

There is nothing special in this case. The JS backend can work with
virtually any web framework. In some of the examples TurboGears is used,
but just for simplifying generation of the JSON data. You can use
`simplejson`_ to generate a JSON response from any framework. The `django ping example`_ shows how to do this in `Django`_.

.. _`simplejson`: http://cheeseshop.python.org/pypi/simplejson
.. _`Django`: http://www.djangoproject.com/

Further examples:
-----------------

There is bub'n'bros client working in javascript available `here`_ (No
working copy on-line right now, sorry) and a simple example of JS a
`python console`_. There is also a simple `django ping example`_.

.. _`here`: http://codespeak.net/svn/pypy/dist/pypy/translator/js/examples/bnb/start_bnb.py
.. _`python console`: http://codespeak.net/svn/pypy/dist/pypy/translator/js/examples/pythonconsole.py
.. _`django ping example`: http://codespeak.net/svn/pypy/dist/pypy/translator/js/examples/djangoping
