News
Home Home Contact us Contact
home News Projects Tests Files Follow manatlan on Twitter Sign in
2008/11/08 14:26

Near the perfect ajax method #1

It's not my first try. I like to find the ultimate way to do this kind of thing. In this approach, I really needed a way to send multiple needs, and to retrieve many results. It's based on jQuery (clientside) and webpy (serverside) (with a json lib like demjson), as always. It's a proof of concept, but it feeds my needs (and perhaps yours)

On the client side, it's a classical ajax method :

function ajax( method, data, callback ) {...}

method define the server side method, data is a classical dict, and callback is a optional callback.

A simple example, on client side, could be :

<button onclick='ajax( "method1", {arg1:"hello"} );'>test</button>

On server side :

@Ajax.entry
def method1(arg1="bonjour"):
    return {"result":arg1*2}

Which does nothing, except returning a object which have got an attribut result which contains 'hellohello'. But the previous client side call don't care about the result. To use the result on the client side, we should use something like this :

<button onclick='ajax( "method1", {arg1:"hello"}, function(o) {alert(o.result);} );'>test</button>

Which should display 'hellohello' in an alert box.

Note, as on the server side, the python method method1 has got a default value for param arg1. It could be possible to call the ajax method without providing a arg1 :

<button onclick='ajax( "method1", {}, function(o) {alert(o.result);} );'>test</button>

Which should display 'bonjourbonjour' in an alert box.

Until here, it's a very classical ajax method : A call, and a return as a dict. A neat feature is that you can fill html tags on server side. On this simple example, client side :

<button onclick='ajax( "method2" );'>test</button>
<div id="out1"></result>
<div id="out2"></result>

server side :

@Ajax.entry
def method2():
    return {"[#out1]":"hello1","[#out2]":"hello2"}

The div#out1 and div#out2 will be automatically filled with the response of the server method. It use the "[]" trick to encapsulate a jquery selector. It's just a simple way, to feed some tag on the clientside. Another neat feature is that you can send back a javascript call in your response, by adding a key script in your result like this :

@Ajax.entry
def method2():
    return {"[#out1]":"hello1","[#out2]":"hello2","script":"alert(1)"}

which should feed div#out1 and div#out2, and display an alert box containing '1'. So it's easy to define javascript calls, or feed a client tag, by doing it on the serverside, in a python way.

But the great feature, is that you can call many methods from the server side in one client ajax call, by passing a list of methods, like this :

<button onclick='ajax( ["method1","method2"], {arg1:'ciao'});'>test</button>

method1 will be executed at first, and method2 at last. Each method will try to find its own parameters in the dict. Thus, parameters can be shared between methods.

And better : method1 could set parameters for method2 like this:

@Ajax.entry
def method1(arg1):
    return {"arg2":arg1*2}

@Ajax.entry
def method2(arg2):
    return {"[#out]":arg2*2}

arg2 is populated by method1, thus method2 can use it (although it was not defined at the ajax call). It should set 'ciaociaociaociao' in a div#out.

Here is the webpy(v0.3) code :

URLS = (
    '/',"Ajax",
    '/ajax',"Ajax",
)

import inspect
import demjson

class Ajax:
    """
        function ajax(methods,dico,callback)
        {
            if(!dico) dico={};
            dico["_methods_"] = methods;

            $.post("/ajax", dico,
                function(data){
                    var obj = eval("("+data+")");
                    if(obj._err_)
                        alert(obj._err_);
                    else {
                        if(obj._contents_) {
                            for(var idx in obj._contents_) {
                                var c_c=obj._contents_[idx];
                                $(c_c[0]).html(c_c[1]);
                            }
                        }

                        if(obj._scripts_) {
                            for(var idx in obj._scripts_)
                                eval(obj._scripts_[idx]);
                        }

                        if(callback)
                            callback(obj)
                    }
                }
            );
        }
    """
    class _MethodDict_(dict):
      def __call__(self, func):
        self[func.__name__] = func
    entry = _MethodDict_()  #decorator


    def POST(self):
        def log(a):
            print str(a)
        def ResultSet(dico):
            return demjson.encode(dico)
        def error(m):
            log("--> "+err)
            return ResultSet({"_err_":str(m)})

        data=web.input(_methods_=[])
        methods = data["_methods_"]
        del data["_methods_"]

        result = {}
        for method in methods:
            if method in Ajax.entry:
                fct=Ajax.entry[method]
            else:
                return error("Method '%s' doesn't exist"%(method,))
            fct_args=inspect.getargspec(fct)[0]
            args={}
            for a in fct_args:
                if a in data: # else, hope there is a default value in method def
                    args[a] = data[a]   #fill args for method
            log("ajax call "+method+"("+str(args)+")")
            try:
                rs = fct(**args)
                log("--> "+str(rs))
                if rs:
                    if type(rs)!=dict:
                        return error("Method '%s' don't return a dict"%(method,))
                    else:
                        for k in rs.keys():
                            if k[0]=="[" and k[-1]=="]":
                                result.setdefault("_contents_",[]).append( [k[1:-1],rs[k]] )
                                del rs[k]

                        if "script" in rs:
                            result.setdefault("_scripts_",[]).append(rs["script"])
                            del rs["script"]

                        result.update( rs )
                        data.update( rs )
            except Exception,m:
                err="Error in '%s' : %s : %s"%(method,Exception,m)
                return error(err)

        log("Return:"+str(result))
        return ResultSet(result)

    def GET(self):
        web.header("content-type","text/html")
        return """
        <html>
        <head>
            <script type="text/javascript" src="/static/jquery.js"></script>
            <script type="text/javascript">%s</script>
        </head>
        <body>
            <button onclick='ajax( "jack", {arg2:"hello1"} );'>go1</button>
            <button onclick='ajax( "jo", {arg1:"hello2"} );'>go2</button>
            <button onclick='ajax( "jo" );'>go3</button>
            <button onclick='ajax( ["jo","jack"], {} );'>go4</button>
            <button onclick='ajax( ["jo","jack"], {arg1:"hello5"} );'>go5</button>
            <div id="result"></div>
        </body>
        </html>
        """ % Ajax.__doc__


@Ajax.entry
def jo(arg1="koko"):
    return {"arg2":arg1,"script":"$('#result').css('border','1px solid red')"}

@Ajax.entry
def jack(arg2):
    return {"[#result]":arg2*2}

Comments (View)
2008/10/17 07:15

Did I told you that I loved python and GAE ?

I can't remember. But it's true. With the exception of the recents problems, GAE which give up hosting of naked domains, and this bug in webpy : this website was nearly unreachable. It takes me some days to find them, but it's always a real pleasure to hack python and GAE hosting. It's really easy and pleasant.

This website is now fully functional. I've started to migrate pages from my old websites to here, and this one will really be my main one. All tools (wiki, blog, files, tagging, comments from disqus) were really simple to implement and to maintain. Thanks to webpy too, in its 0.3 version (which is not fully compatible with GAE currently), this (anti-)framework fits well with GAE.

And more : it's friday ... I love friday too ;-)

Comments (View) Tags: gae
2008/10/14 20:47

Py2deb comes here and 0.4 is out

Py2deb comes on this host, which will be the official website. A new release is out too : the pydeb 0.4, which comes with some minor changes (should be compatible with debian too, and install itself in python2.6 path too). Pages on the old website will forward to here.

The gtk frontend will be released soon too !

Comments (View) Tags: py2deb
2008/10/14 19:43

Fricorder 0.7.40 : petites modifications

Toujours en français dans le texte, puisque Fricorder est une application uniquement disponible pour les français qui ont la chance de posséder une freebox.

Cette version contient uniquement de légères modifications cosmétiques : la fenêtre est maintenant redimensionnable, le titre de la fenêtre est maintenant correct et affiche le numéro de version correctement. Rien de bien neuf, juste du cosmétique, par rapport à la release précédente

Les versions deb/rpm et sources sont toujours disponibles ici

Comments (View) Tags: fricorder
2008/10/10 10:55

La version 0.7.37 de fricorder est dispo

En français dans le texte, puisque Fricorder est une application uniquement disponible pour les français qui ont la chance de posséder une freebox.

Cette nouvelle version apporte :

Cette version devrait rester néanmoins compatible avec VLC 0.8.* et l'ancienne playliste. On en parle dans la mailing et sur le forum d'ubuntu-fr. Les versions deb/rpm et sources sont disponibles ici

Comments (View) Tags: fricorder
2008/09/29 15:58

Times are changing ...

Microsoft will include jQuery in its MS VistualStudio ! It's amazing. An opensource projet will be shipped in a big commercial product of Microsoft (it's the first time, no ?). And it will shipped "as is", without a fork ! (they plan to contribute too). So .net developpers could include the google ajax jquery apis in their asp.net products, amazing ;-). Hope the credits will always goes to John Resig, in the future ... jQuery for masses (I am a bit scared). But, definitely, times are changing ..

Comments (View)
2008/09/01 16:44

Ubiquity and Sandy (iwantsandy.com)

Ubiquity is the new mozilla buzz (A kind of commandline for firefox). And Sandy is a reminder service. I wanted to use Sandy with Ubiquity.

This is my first try. It's able to make the authent if '_sandyAuthent' is configured. Without that, it can only write to Sandy if you are already logued in iwantsandy.com ;-).

Here is my contribution :

CmdUtils.CreateCommand({
  name: "sandy",
  homepage: "http://manatlan.com/",
  author: { name: "manatlan", email: "me@manatlan.com"},
  license: "GPL",
  description: "Write to Sandy (iwantsandy.com), make authent if needed",
  takes: {"text": noun_arb_text},

  _sandyAuthent: { "email": "xxxx", "password": "yyyy"},

  _write: function(msg) {
    var params = {"message[body]":msg};

    jQuery.ajax({
      type: "POST",
      url: "http://iwantsandy.com/email/create",
      data: params,
      error: function() {
        displayMessage("Where is sandy ?! a problem occured ;-)");
      },
      success: function() {
        displayMessage("Ok, Sandy will '"+msg+"'");
      }
    });
  },


  preview: function( pblock,txt ) {
    pblock.innerHTML = 'Sandy, "'+txt.text+'"';
  },


  execute: function(txt) {
    zis = this;

    jQuery.ajax({
      type: "GET",
      url: "http://iwantsandy.com/email/new",
      error: function() {
        displayMessage("Where is sandy ?! a problem occured ;-)");
      },
      success: function(data) {
        if(data.indexOf("Log in")>0) {
            // not logued yet ...

            // so log in
            jQuery.ajax({
              type: "POST",
              url: "http://iwantsandy.com/session",
              data: zis._sandyAuthent,
              error: function() {
                displayMessage("Where is sandy ?! a problem occured ;-)");
              },
              success: function(data) {
                if(data.indexOf("Log in")>0) 
                    displayMessage("Need authent to write to sandy (configure '_sandyAuthent')");
                else
                    zis._write(txt.text);
              }
            });


        }
        else {
            zis._write(txt.text);
        }
      }
    });

  }
})

two posts in one day ! great !

EDIT 02/09/2008 For now, put this script in your command-editor. In a near future, it will be hosted on this domain, in an ubiquity way. I'd like to find a way to prompt the user for its registration informations (any ideas ?). In a near future, it will be able to display (in preview), the daily digest and others listings ... Discussions have started here and here

Comments (View)
2008/09/01 06:36

Big day for ibraining

Two days after my last post. iBraining has now a reminder system, which works. It's able to send mail/reminder to any user which suscribe for. It's now features complete (like I wanted at the beginning ;-) ), until new ideas.

Sure, it's not GAE which do that, with its own limitations : no cron, no massmailer (only 3 to 5 mails by minutes are allowed). I had setup another web service on alwaysdata (wonderful python hosting service), which communicate with ibraining main site, in a restful way. It's a little bit complex, compare to a native solution, but a lot easier than if it was done on gae.

But it's a big day (and it's the 1st september). Users can now setup a "coach", to help them to train regularly. The next big thing is a new game.

Comments (View) Tags: ibraining
2008/08/30 07:49

Some news

Google had just released a Google App Engine Cookbook, and I had added a recipe How-to put any python object in a DataStore ;-), and find a lots of usefull tricks.

I really need to release a version of jbrout 0.3, which use exiv2, with the pyexiv2 python bindings. Just need to find the time !

I've got a new secretary : sandy ;-). I really like the concept behind this appointment/reminder service. You can manage easily your appointments by mail. It's just what I needed. And the best thing : you can snooze a reminder ;-) (which is boring on google agenda)

I really love the apis of google. Here is a simple translator using the google translate javascript api. It's amazing how it's simple.

Except that, ibraining is updated each week. Recently, new modal popups, new graphics ... And in a near future : a new game. And perhaps a way to set a reminder to remember to play regularly (But GAE doesn't allow to send more than 5 mails by minutes ;-( )

Comments (View) Tags: ibraining, gae
2008/07/02 20:45

iBraining is new

A lot of changes on iBraining ! Yesterday I had pushed a new revision with lots of changes. The home page is lighter. In fact, a lot of "403 over quotas" were presents. So I decided to review completly the page, to make less database calls (it seems they are always presents ;-( ). A new game appears : "colors". And a new google adsense tower is now present on the page's right (need to pay the domain name and perhaps "more quotas" in the future).

I really need to profile my app, to avoid theses "over quotas" (I had already profiled it, but except the google api calls : I really don't understand where the real problem is). BTW, Google App Engine is really great.

update 21/07/2008 : no more 403 quotas exceeded. After some profiling I had removed problems.

Comments (View) Tags: ibraining
<< oldest newest >>

Tags

RSS Python Powered Get Ubuntu
©opyleft 2008-2019 - manatlan