Django decorators

I am developing a page with Django and I had one interesting requirement: limit the number of users. The idea is to let a fixed number of users use the site and show a message telling the others to wait. I will like to share how I did it.

We need to check in every view used by a user. Instead of repeating the same code, the easiest way is to add a decorator for each function. Is really similar to AoP.

decorators.py:

from django.conf import settings
from django.shortcuts import redirect

MAX_NUMBERS_OF_USERS = getattr(settings, 'MAX_NUMBERS_OF_USERS', 100)

def check_user(view_function):
    def _wrapped_view_func(request, *args, **kwargs):
        auth_user = request.user
        
        if auth_user.id <= MAX_NUMBERS_OF_USERS:
            return view_function(request, *args, **kwargs)
        return redirect('coming_soon')
    return _wrapped_view_func

views.py:

from app.decorators import check_user
from django.contrib.auth.decorators import login_required
from django.shortcuts import render

@login_required
@check_user
def restricted_view(request):
    #Do stuff

def coming_soon(request):
    return render(request, 'coming_soon.html', {})

And that’s it ! Easy, clean and simple ! You can do more complex stuffs like logging.

Configure Juniper VPN Web Client Ubuntu 14.04 64 bits

So i am doing a project with Mercado Libre and they use Juniper VPN.

I love to code using Linux, it has a lot of advantages. At this moment I am using Ubuntu 14.04 64 bits.

Mercado Libre sent me the instructions on how to connect with my login/password. So I followed them, but thanks to the awesome people of Juniper, they does not have support for Linux 64 bits. At least they have this guide.

That guide did not work for me. I had to struggle and read a lot over the internet, view system logs, etc. After one day I was able to connect, so this are the instructions i followed:

  1. sudo dpkg –add-architecture i386
  2. sudo aptitude purge firefox
  3. sudo aptitude install firefox:i386 icedtea-7-plugin:i386 openjdk-7-jdk:i386
  4. Open Firefox, enter the login/password/token, then it will load the plugin and ask to allow and accept a lot of things, click all yes and that is it. You need to make sure Firefox have all the permissions to run the java applet.

That guide works if you are running the client directly from the computer, not from the browser. We just need to be sure that Firefox will load the Juniper VPN applet running on a 32 bits JVM.

Django + nginx + uWSGI

I had to deploy a web application I just created using Django. I will describe the steps I followed.

  1. Make sure you have you Django setted up correctly. I followed this guide.
  2. Install uWSGI into your virtualenv:
    pip install uwsgi
    
  3. Install nginx:
    sudo aptitude install nginx
    sudo service nginx start
    
  4. Edit nginx.conf file (it is in /etc/nginx if you are using ubuntu), it should be something like this:
    upstream django {
     server unix:///path/to/your/mysite/mysite.sock;
    }
    
    server {
     listen 80 default_server;
     listen [::]:80 default_server ipv6only=on;
    
     server_name mysite.com;
    
     charset utf-8;
    
     # max upload size
     client_max_body_size 5M; # adjust to taste
    
     location /robots.txt {
     alias /path/to/your/mysite/robots.txt;
     }
    
     # Django media
     location /files {
     alias /path/to/your/mysite/files; # your Django project's media files - amend as required
     }
    
     location /static {
     alias /path/to/your/mysite/static; # your Django project's static files - amend as required
     }
    
     # Finally, send all non-media requests to the Django server.
     location / {
     uwsgi_pass django;
     include /path/to/your/mysite/uwsgi_params; # the uwsgi_params file you installed
     }
    }
    

    Make sure it matches your Django app settings.py configuration:

    PROJECT_ROOT = os.path.abspath(os.path.dirname(os.path.dirname(__file__)))
    MEDIA_ROOT = os.path.join(PROJECT_ROOT, 'files')
    MEDIA_URL = '/files/'
    STATIC_ROOT = ''
    STATIC_URL = '/static/'
    
  5. Configure you uWSGI .ini file:
    # mysite_uwsgi.ini file
    [uwsgi]
    
    # Django-related settings
    # the base directory (full path)
    chdir = /path/to/your/mysite
    # Django's wsgi file
    module = mysite.wsgi
    # the virtualenv (full path) .virtualenvs at home folder
    home = /path/to/your/mysitevirtualenv  
    
    # process-related settings
    # master
    master = true
    # maximum number of worker processes
    processes = 10
    # the socket (use the full path to be safe
    socket = /path/to/your/mysite.sock
    # ... with appropriate permissions - may be needed
    chmod-socket = 666
    # clear environment on exit
    vacuum = true
    lgger = syslog
    
  6. Add uwsgi_params file to your project:
    uwsgi_param QUERY_STRING $query_string;
    uwsgi_param REQUEST_METHOD $request_method;
    uwsgi_param CONTENT_TYPE $content_type;
    uwsgi_param CONTENT_LENGTH $content_length;
    
    uwsgi_param REQUEST_URI $request_uri;
    uwsgi_param PATH_INFO $document_uri;
    uwsgi_param DOCUMENT_ROOT $document_root;
    uwsgi_param SERVER_PROTOCOL $server_protocol;
    uwsgi_param HTTPS $https if_not_empty;
    
    uwsgi_param REMOTE_ADDR $remote_addr;
    uwsgi_param REMOTE_PORT $remote_port;
    uwsgi_param SERVER_PORT $server_port;
    uwsgi_param SERVER_NAME $server_name;
    
  7. Run in emperor mode:
    # create a directory for the vassals
    sudo mkdir /etc/uwsgi
    sudo mkdir /etc/uwsgi/vassals
    # symlink from the default config directory to your config file
    sudo ln -s /path/to/your/mysite/mysite_uwsgi.ini /etc/uwsgi/vassals/
    /usr/local/bin/uwsgi --emperor /etc/uwsgi/vassals --uid www-data --gid www-data
    

    You can run the last command on screen or when the system starts.

And thats it, hope this can be helpful. You can find more info here.

CV

This time I wanted to share my latex CV template. It took me some time to get it done and I know this can be useful to anybody.

%______________________________________________________________________________________________________________________
% @brief    LaTeX2e Resume for Federico Ponte
\documentclass[oneside,12pt,letterpaper]{article}

\usepackage{hyperref}
\usepackage{epsf}
\usepackage[pdftex]{graphicx}
\usepackage{epstopdf}
\usepackage{epsfig}
\usepackage{wrapfig}
\usepackage{fancyhdr}
\usepackage{anysize}
\usepackage{titlesec}
\usepackage{indentfirst}
\usepackage[tmargin=1in,bmargin=1in,lmargin=1.25in,rmargin=1.25in]{geometry}
\usepackage{xcolor}

\renewcommand*\rmdefault{cmss}

\definecolor{MSBlue}{rgb}{.204,.353,.541}
\definecolor{MSLightBlue}{rgb}{.31,.506,.741}

\titleformat*{\section}{\Large\bfseries\sffamily\color{MSBlue}}
\titleformat*{\subsection}{\large\bfseries\sffamily\color{MSLightBlue}}
\titleformat*{\subsubsection}{\itshape\subsubsectionfont}


\pagestyle{fancy}
\lhead{\Large Curriculum Vitae}
\rhead{Federico Ponte}
\cfoot{Software Engineer}
\setlength{\headheight}{50pt}
\marginsize{2.8cm}{3.5cm}{1.2cm}{1.2cm}

%______________________________________________________________________________________________________________________
\begin{document}
    %__________________________________________________________________________________________________________________
    % Contact Information
    \section*{Personal Information}

    \begin{wrapfigure}{lhc}{0.2\textwidth}
    \vspace{-20pt}
    \centering
    \includegraphics[scale=0.51]{me.jpg}
    \end{wrapfigure}

    \noindent $6^{th}$ transversal Res. Villa Quintana, PH-A, Los Palos Grandes, Caracas 1060, Venezuela\\
    tel: +582122867498\\
    cel: +584242561666\\
    skype: fedep3\\
    email: \href{mailto:fedep3@gmail.com}{fedep3@gmail.com}\\

    {\setlength{\parindent}{22pt} I was born November 19th of 1988, in Caracas, Venezuela. There I studied and got my school and bachelor degree. I have been always interested in computers and mathematics. I like the logic behind programming, it's like a challenge to me.}

    When I am not working I enjoy sports, playing computer games or going out with my friends. I swim few days in the week near my house. Of course I like and play soccer. On the other hand my hobbies are reading and watching tv.

    My best skill, I will say is learning. I am always reading and trying to know new things.

    I find my self more in the backend development of applications. I like the challenge it envolves in terms of security, scalability and efficiency.

    My objectives are to keep learning, improving my skills and always being up to date with new technologies. Also, I like to keep swimming and try new sports, like tennis, running or bicycle.

    \textbf{Favorite quote:} "Be the change that you wish to see in the world" --Mahatma Gandhi


    %__________________________________________________________________________________________________________________
    % Education
    \section*{Education}

    \noindent\textbf{Universidad Sim\'on Bol\'ivar}, Caracas, Venezuela \vspace{2mm}\\\vspace{1mm}%
    \textsl{Computer Engineering} \hfill \textbf{ Octuber 2006 - November 2011}\vspace{-3mm}\\\vspace{-1mm}%
    \begin{itemize}
        \item[--] \textbf{Thesis  advisor:} Emely Arr\'aiz. I made it with Alexander De Sousa a carrer companion.
        \item[--] \textbf{Thesis  title:}  Clustering de Datos Num\'ericos por medio de Algoritmos Basados en Poblaci\'on e Inteligencia Colectiva (in english: Numeric Data Clustering with algorithms based on Population and Collective Intelligence). 
        \item[--] I am specialized in Computer Languages and Algorithms Design. Beside I saw three electives: Robotics, Artificial Intelligence I and Business Economics. 
    \end{itemize}\vspace{-1.5mm}
    \textbf{Colegio Santiago de Le\'on de Caracas}, Caracas, Venezuela \vspace{2mm}\\\vspace{1mm}%
    \textsl{High School Degree} \hfill \textbf{1994 - 2006}\vspace{-3mm}\\\vspace{-1mm}%

    %__________________________________________________________________________________________________________________
    % Professional Experience
    \section*{Job Experiences}
	{\setlength{\parindent}{0pt}
		\textbf{Nedra}, Caracas, Venezuela \vspace{2mm}\\\vspace{1mm}%
		\textsl{Software Engineer} \hfill \textbf{May 2012 - Present}\vspace{-3mm}\\\vspace{-1mm}\\
	}
	\begin{itemize}
		\item[--]  Created different web pages for real estate and auctions using Microsoft technology.

		\item[--]  Programmed in Java various requirements for SiteScout: video ads, web app for ads agencies, other for data migration, a server to make calls to Proximic, Peer39, etc.
	\end{itemize}
	{\setlength{\parindent}{0pt}
		\textbf{Kentriki}, Caracas, Venezuela \vspace{2mm}\\\vspace{1mm}%
		\textsl{Software Engineer} \hfill \textbf{January-April 2012}\vspace{-3mm}\\\vspace{-1mm}\\
	}
	\begin{itemize}
		\item[--] Did some test to see the feasibility of installing puppet on servers, for that i created and configure some virtual machines. Programmed page requirements (Python, HTML and jQuery)
	\end{itemize}
	{\setlength{\parindent}{0pt}
		\textbf{Novared}, Caracas, Venezuela \vspace{2mm}\\\vspace{1mm}%
		\textsl{IT Programmer} \hfill \textbf{Octuber-December 2011}\vspace{-3mm}\\\vspace{-1mm}\\
	}
	\begin{itemize}
		\item[--] Work tasks included evaluating the application written in VB to C\#, creating some RESTful web services using Java and polishing the website.
	\end{itemize}

    %__________________________________________________________________________________________________________________
    % Volunteer Experience & Causes
    \section*{Volunteer Experience and Causes}
	    {\setlength{\parindent}{0pt}
		    \textbf{TECHO}, Caracas, Venezuela \vspace{2mm}\\\vspace{1mm}%
		    \textsl{Volunteer} \hfill \textbf{2013 - Present}\vspace{-3mm}\\\vspace{-1mm}\\
	    }
	    \begin{itemize}
		    \item[--]  I helped collecting money in the streets. Also giving logistic and technical help during two of their main events.
	    \end{itemize}

    %__________________________________________________________________________________________________________________
    % Computer Skills
    \section*{Computer Software}
    I have used a lot of different softwares: snv, git, Team Fundation Server, MySQL, Oracle SQL, SQL Server, PostgreSQL, Jetty, Tomcat, Glassfish, Jersey, Django, IIS, Hadoop, Virtual Box, .NET, etc.

    \section*{Programming} 
    \begin{description}
        \item{\textbf{Advanced}} Java, Python, C
        \item{\textbf{Intermediate/Advanced}} C\#, Javascript, CSS, HTML, VB, SQL, bash
        \item{\textbf{Intermediate}} Prolog, Haskell, PHP, MatLab, Intel x86 assembly, Ruby, \LaTeX
    \end{description}

    %__________________________________________________________________________________________________________________
    % Language Skills
    \section*{Language Skills}
    \indent My native language is Spanish, but almost everything I write and read is in English both in connection to computers. Also, in my last work, I had to talk and interact with the customer directly and the language use was English.

    \textbf{TOEFL Score:} 100 

%______________________________________________________________________________________________________________________
\end{document}


%______________________________________________________________________________________________________________________
% EOF

Here is the result.

Python expressiveness

Here i will post my solution of the first two problems in the qualifying round in the Google Code Jam. Both are easy problems.

The first one is:

https://code.google.com/codejam/contest/2974486/dashboard#s=p0

#!/usr/bin/python

def read_board(row_number):
    for i in xrange(4):
        if i + 1 == row_number:
            row = map(int, raw_input().split(' '))
        else:
            raw_input()
    return row 

ncases = int(raw_input())

for ncase in xrange(ncases):
    first_row_number = int(raw_input())
    first_row = read_board(first_row_number)

    second_row_number = int(raw_input())
    second_row = read_board(second_row_number)
    match_count = 0
    number = 0
    for i in first_row:
        for j in second_row:
            if i == j:
                number = i
                match_count += 1
                break

    answer = 'Case #' + str(ncase + 1) + ': '
    if match_count == 0:
        print answer + 'Volunteer cheated!'
    elif match_count == 1:
        print answer + str(number)
    else:
        print answer + 'Bad magician!'

And the other one was https://code.google.com/codejam/contest/2974486/dashboard#s=p1:

#!/usr/bin/python

ncases = int(raw_input())

for ncase in xrange(ncases):
    data = map(float, raw_input().split(' '))
    C, F, X = tuple(data)

    if X <= C:
        print 'Case #' + str(ncase + 1) + ': ' + str(X/2.0)
    else:
        house_times = [C/2.0]
        cookies_per_second = [2.0]
        min_time = X/2.0

        for i in xrange(int(X)/int(C)):
            new_cookies_per_second = cookies_per_second[i] + F
            new_time = house_times[i] + X/new_cookies_per_second
            house_times.append(house_times[i] + C/new_cookies_per_second)
            cookies_per_second.append(new_cookies_per_second)

            min_time = min(min_time, new_time)

        print 'Case #' + str(ncase + 1) + ': ' + str(min_time)

Python 🙂

Setting up Jetty with IntelliJ IDEA

I wrote this in November of last year, I didn’t have time to share it :(.

  1. Download lastest Jetty stable version here
  2. Uncompress it (tar -zxvf jetty-distribution-9.1.0.v20131115.tar.gz)
  3. cd jetty-distribution-9.1.0.v20131115/
  4. Add the –module=jmx to start.ini archive (echo –module=jmx >> start.ini)
  5. Setup  the Default Jetty Server Local configuration
  6. Click configure and add the path to the folder you just uncompressed

  7. Click Ok and put the following:

    VM Options: -Dcom.sun.management.jmxremote= -Dcom.sun.management.jmxremote.port=1099 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false

    Jetty Server Settings: select etc/jetty-jmx.xml and etc/jetty.xml

  8. Now just add your new Jetty configuration, for that just click on the plus sign

    and search for Jetty Server Local

  9. Go to deployment tab and add your war(s) file(s)
    step5
  10. Add the path you want to use
  11. Add the name you want
    step7
  12. Click ok

That’s all, hope this is useful!

Some photos of my contry protests

I live in Venezuela, and we are in some political crisis. All the following photos were taken in a protest in Caracas the 3rd of March of this year.

Here are some of the messages I saw:

Frase

And with what money I’m going to carnivals?

Grafitti

More love please

Oscars

Nominees for treason:
* Nicolas Maduros / GNB
* Diosdado Cabello / FANB
* Rafael Ramírez / CNE
* Luisa Ortega Díaz / TSJ
* Elías Jagua

Saw

LETS PLAY A GAME!
CORRUPTS
KEEP STEALING WITH AN
INEPT PRESIDENT
AND
AN ANGRY VENEZUELA
IT IS NOT EASY

Accion Poetica

IF THERE ARE MORE SCHOOLS OF
MUSIC THAN MILITARY ONES IN
THE STREET, THERE WILL BE MORE
ARTISTS THAN ASSASINS

And this are panoramic photos I took:

Panoramic Photo Two

Panoramic Photo One

Recommended Pages

Hello, long time I don’t write, I have been really busy!

Here are some things I want to share:

  • I tried a new (for me)  javascript framework: AngularJS

    I have used Backbone.js and Knockout, you can check my post about them here. I gotta say this one is pretty good. The code is organized and you have some cool options like the directives. The only tricky part I found is the authenticating process. The problem is that AngularJS is intended for one page apps, but it can be solved. Here is how.

    You should try it !

  • If you like Python and programming, do Design of Computer Programs course in Udacity!  Here is the link. It is taught by Peter Norvig and it is pretty interesting.

See ya!

 

Backbone.js & Knockout (English version)

I had a lot of work and things to do, so I couldn’t write.

The last year my boss asked me to look up all the libraries like Backbone.js and Knockout. The idea was to check all the options and take the more convenient.

To accomplish that I began to read and look a lot of information. Besides, I took the opportunity to try each one and feel them. For that, I made an application in Backbone.js and then observed how it would be using Knockout.

I will show you the code and then give my opinion. That way you will have a better decision.

Backbone.js

It’s really powerful and used by a lot of people, but it have an hard learning curve. You have a model and a bunch of things to define. If you follow all the conventions, the code will be easy to mantain. Here you can see more information and details.

It is ideal for REST web servicies.

Creating this application took me one noon and part of the morning.

The html:

<div>
    <fieldset>
        <legend>Person List - Using Backbone</legend>
        <div id="person-container">
            <input type="button" value= "Create New" class="Add" id="btn-create-new-person" />
            <table id="person-list">
                <tr>
                    <th>
                        Name
                    </th>
                    <th>
                        Description
                    </th>
                    <th>
                    </th>
                </tr>
            </table>
        </div>
        <script id='person-template' type='text/template'>
            <td><%=ID%></td> <td><%=FirstName%></td> <td><%=LastName%></td> <td><%=Age%></td>
            <td><input type="button" value="Edit" class="person-edit" /> | <input type="button" value="Details" class="person-detail" /> | <input type="button" value="Delete" class="person-delete" /></td>
       </script>
    </fieldset>
</div>

Javascript:

$(function () {

    // Person Model
    var Person = Backbone.Model.extend({
        urlRoot: '/Person/List/',
        initialize: function () {
            console.log('Person initialize');
        },
        defaults: {
            ID: 0,
            FirstName: 'Unknown',
            LastName: 'Unknown',
            Age : 0
        }
    });

    // Person Collection
    var PersonCollection = Backbone.Collection.extend({
        model: Person,
        url: '/Person/List/'
     });

     // Person View - el returns the template enclosed within a tr
     var PersonView = Backbone.View.extend({
         template: _.template($('#person-template').html()),
         tagName: "tr",
         initialize: function () {
             console.log('PersonView initialize');
             this.model.bind('change', this.render, this);
             this.model.bind('remove', this.unrender, this);
         },
         render: function () {
             console.log('PersonView render');
             $(this.el).html(this.template(this.model.toJSON()));
             return this;
         },
         unrender: function () {
             console.log('PersonView unrender');
             $(this.el).remove();
             return this;
         },
         events: {
             "click .person-edit": 'EditPerson',
             "click .person-delete": 'DeletePerson'
         },
         EditPerson: function () {
             console.log('PersonView EditPerson');
             this.model.set({ FirstName: 'Unknown' });
             var self = this;
             this.model.save(this.model, { success: function () {
                 $("input:button", $(self.el)).button();
             }});
        },
        DeletePerson: function () {
            console.log('PersonView DeletePerson');
            this.model.destroy();
        }
    });

    // Actual App view
    var AppView = Backbone.View.extend({
        initialize: function () {
            console.log('AppView initialize');
            this.collection.bind('add', this.AppendPerson, this);
        },
        el: '#person-container',
        counter: 3,
        events: {
            "click #btn-create-new-person": "AddNewPerson"
        },
        AddNewPerson: function () {
            console.log('AppView AddNewPerson');
            this.counter++;
            var newPerson = new Person({ ID: this.counter, FirstName: 'Unknown ' + this.counter, LastName: 'Damn ' + this.counter, Age: this.counter });
            this.collection.add(newPerson);
            newPerson.save(newPerson, { success: function () {
                $("input:button", "#person-list").button();
            }});
        },
        AppendPerson: function (person) {
            console.log('AppView AppendPerson');
            var personView = new PersonView({ model: person });
            $(this.el).find('table').append(personView.render().el);
        },
        render: function () {
            console.log('AppView render');
            if (this.collection.length > 0) {
                this.collection.each(this.AppendPerson, this);
            }
            $("input:button", "#person-list").button();
        }
    });

    var persons = new PersonCollection();
    var view = new AppView({ collection: persons });

    persons.fetch({ success: function () {
        console.log('Fetch called');
        view.render();
    }});
});

In the server (my case asp.net MVC with C#):

public class PersonController : Controller
{
    List<Person> Persons = new List<Person> {
        new Person (1, "Federico", "Ponte" , 23),
        new Person (2, "Alejandro", "Drago" , 23),
    };

    // Get Person
    [AcceptVerbs(HttpVerbs.Get)]
    public JsonResult List(int? ID)
    {
        if (ID.HasValue)
        {
            Person PersonResult = Persons.Find(p => p.ID == ID);
            return Json(PersonResult, JsonRequestBehavior.AllowGet);
        }
        return Json(Persons, JsonRequestBehavior.AllowGet);
    }

    // Update Person
    [AcceptVerbs(HttpVerbs.Put)]
    public JsonResult List(int ID, Person UpdatedPerson)
    {
        Person PersonResult = Persons.Find(p => p.ID == ID);
        PersonResult.FirstName = UpdatedPerson.FirstName;
        PersonResult.LastName = UpdatedPerson.LastName;
        PersonResult.Age = UpdatedPerson.Age;
        return Json(PersonResult, JsonRequestBehavior.DenyGet);
    }

    //Add Person
    [AcceptVerbs(HttpVerbs.Post)]
    public JsonResult List(Person CreatePerson)
    {
        Persons.Add(CreatePerson);
        return Json(CreatePerson, JsonRequestBehavior.DenyGet);
    }

    //Delete Person
    [AcceptVerbs(HttpVerbs.Delete)]
    public JsonResult List(int ID)
    {
        Person PersonResult = Persons.Find(p => p.ID == ID);
        Persons.Remove(PersonResult);
        return Json(PersonResult, JsonRequestBehavior.DenyGet);
    }
}
public class Person
{
    public Person(int _ID, string _FirstName, string _LastName, int _Age)
    {
        ID = _ID;
        FirstName = _FirstName;
        LastName = _LastName;
        Age = _Age;
    }

    public int ID { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
}

As you can see it is laborious, but it have advantages: highly configurable, it works on cellphones, you use web services. It is easy to scale with it.

It’s pretty tricky at the begging, but once you understand it, is simple.

Knockout

It use the pattern Model-View-View Model. It is quite easy to learn and pretty powerful. It comes with a complete template system and a lot of functions. You need to create a model and modify it directly in order to update the views. It have a pretty good documentation and it’s simple. To know more enter here.

One model would be:

function PersonModel () {
    var self = this;

    self.ID = ko.observable(0);
    self.FirstName = ko.observable('Unknown');
    self.LastName = ko.observable('Unknown');
    self.Age = ko.observable(0);
}

function PersonCollectionModel () {
    var self = this;

    self.Persons = ko.observableArray()
}

$(function (){
    ko.applyBindings(new PersonCollectionModel());
});

The functionality is pretty boring and it is not structurated. An example would be:

function PersonModel () {
    var self = this;

    self.ID = ko.observable(0);
    self.FirstName = ko.observable('Unknown');
    self.LastName = ko.observable('Unknown');
    self.Age = ko.observable(0);

    self.changeAndAlert = function (){
        self.FirstName('Hola');
        self.LastName('Chao');

        alert('Me cambie a ' + self.FirstName() + ' ' + self.LastName());
    }
}
<div>
    <fieldset>
        <legend>Person List - Using Knockout</legend>
        <div id="person-container">
            <input type="button" value= "Create New" class="Add" id="btn-create-new-person" />
            <table id="person-list">
                <tr>
                    <th>
                        Name
                    </th>
                    <th>
                        Description
                    </th>
                    <th>
                    </th>
                </tr>
                <!-- ko foreach : persons -->
                    <tr>
                        <td data-bind="text : FirstName"></td>
                        <td data-bind="text : LastName"></td>
                        <td><input type="button" data-bind="click : changeAndAlert" value="ChangeAndAlert" /></td>
                    </tr>
                <!-- /ko -->
            </table>
        </div>
    </fieldset>
</div>

The rest of the functionality in the case of Backbone is quite easy to do. With a good jQuery knowledge or another library, it just take some Ajax calls and modify the model.

Comparision

Backbone is quite tricky and hard to learn. It gives maintainability and scalability.

I have used more Knockout and it is pretty simple.

At this time I am writting this i don’t have any preferences as before. Both can do the same, the decision is on what you need.

Others

This library is sponsored by Google.

Angular JS