Wednesday, 13 July 2011

Back of an Envelope Node.js Framework - Discuss

I had a quiet afternoon today and while "keeping up with recent developments in my field" or as you might call it "wasting my afternoon googling" I got to thinking what I'd like a Node.js MVC framework should or could contain.
How design shouldn't be done


Firstly, I've been playing with Node.js for about three weeks now and I'm impressed, but I can't seem to find a framework that I'm happy with. Backbone looked good, but I wasted three days on it and it was too much like hard work. Similarly Spine which tried to be less complicated, but eventually failed. Finally I really wanted Brunch to work but it was a release behind backbone (which is a major component of it) and I didn't want to get lost in the mismatching documentation (the Backbone release was pretty major).

I have spent a whole lot of time over the last couple of years using python frameworks, especially Flask and I like their approach of starting with a good foundation (in their case Werkzeug) and adding the bits that are still needed. Could that work with Node? I mean writing a basic HTTP server looks pretty straight forward and there are tons of modules to add the other functionality.

So with this possibly naive starting position I grabbed my pen and scribble down some stuff I thought I'd need. Now bear in mind that I haven't much used any of these modules yet, and I pinched several of them from the bits that go to make up Brunch.


I'd appreciate any feedback, suggestions and so on that those more learned than I might care to offer. Few of you will be less learned I imagine.


Here are the transcribed notes with some links:

Server
Routing
Data Store
Models
  • JS Object/JSON?
  • ORM?
Views
    -CSS
    -Templating
Controllers
Middleware
  • how? Express
Other Stuff

If you spot any typos, inaccuracies or just plain lies then please let me know.

Monday, 27 June 2011

Binding Updates to Model Changes in Backbone/CoffeeScript

My previous post showed a View that looked like this:



But what happens if we have a more complicated app and something else changes the Model?

Well as I said I'm just learning myself here, co the correct way that should be handled is to bind Model changes to the Views render function which looks like this:



Note the bind instruction which means that every time the counter changes, it calls the render function. You might also notice the fat arrow now on the render which make "this" still mean the View rather than the caller (which would be the Model otherwise.)

The explanation for all this is at http://jashkenas.github.com/coffee-script under "Function Binding".

Sunday, 26 June 2011

Simplest Ever Backbone.js/CoffeeScript Example

I'm teaching myself to use Backbone because after having done quite a lot of JavaScript in work lately, it has become obvious to me that I need a framework.

I'm also learning how to use CoffeeScript, which is close enough to python/ruby for me to understand.

First thing that I noticed is that most of the examples for Backbone out there are in pure JavaScript, so I thought I'd do my bit by providing something in CoffeeScript.

To that end, here is a very simple app which displays a number. You can increase or decrease the count by clicking buttons.

The app.coffee code:



and the html to go with that:



In detail

At the top, we set up global hashes to store our objects. In a bigger app this would make referencing bits and pieces much easier, although we don't really need it in this example.



Next the controller. This one is almost empty except it has two routes both taking you to the home control which is also part of the controller. The route with no reference (the empty string) and the one for #home tagged on the end of your URL (by way of an example) both call the home function in this case.

The home control just tells the view to render itself.



Next the Counter model which has one attribute called count which defaults to zero. In reality I think you would want to synchronize your models with a database, but we'll do that in another post soon.



And now for the View. The View is obviously what you are seeing in the browser. In our trivial example we only have one view, but a real app would probably have several. A view lumps together a unit of the app, sometimes nowadays we call these widgets, or blocks or display regions or whatever. You get the idea.

In the initialize function, we just connect the view to the model that backs it. The events connect the button to the functions that they call. The render function draws the result into the #count div; the @ symbol at the end is shorthand for this. In CoffeeScript (like Ruby) the last line of a function gets returned even if you don't specifically say "return". So in other words it means "return this". This is handy since it lets us chain together functions.

The inc and dec functions increase and decrease the count in the model and then update the display. Please see the update here.



Finally the equivalent of the main() function which instaniates the bits and bobs and then starts the history. I've no idea why they called it history, but basically it starts up all the listeners.



That's all for now. Next time I'll show you how to connect all this to your database using python and Flask.

If you see anything wrong with what I've written here or if you spot spelling or other mistakes then please let me know.

Saturday, 25 June 2011

Set Up Your Own Git Repository Hosting Service in Three Minutes

You might have seen my earlier post "A Review of Free Code Repositories".

Well I forgot about hosting your own. Really handy for hobbyist development. I have one of those free developer servers from Amazon out there somewhere. I don't use it much except to demo the occasional site and so on.

I decided that it was a good idea to set it up as a git repository and i show you here how to do this. If you are using something Ubuntu-ish remotely then this should take about three minutes.

A Word of Explanation
This is the simple setup, it avoids creating 'git' user on the remote server, but you DO need to use a seperate key for using the git service and I show you that first.

On your local machine:

# echo "Host gitserver
  Hostname ec2-XXX-XXX-XXX-XXX.compute-1.amazonaws.com
  User git-col
  PreferredAuthentications publickey
  IdentityFile ~/keys/git-col.pub
" >> ~/.ssh/config

# mkdir ~/keys
# ssh-keygen
Generating public/private rsa key pair.
Enter file in which to save the key (/home/col/.ssh/id_rsa): git-col.pub # do NOT accept the default
# cp git-col* ~/keys
# scp git-col.pub ec2-XXX-XXX-XXX-XXX.compute-1.amazonaws.com:
# ssh 
ec2-XXX-XXX-XXX-XXX.compute-1.amazonaws.com

On your remote host you are now logged in as 'ubuntu':

# sudo apt-get install git
# git clone git://github.com/sitaramc/gitolite
# ./gitolite/src/gl-system-install 
# gl-setup -q ~/git-col.pub 
# exit

On your local machine:

# git config --global user.name "Your Name"
# git config --global user.email your@email.address
# git clone gitserver:gitolite-admin
# exit

That's it really. To create a new repository called dummy and initialize it, you can do something like this.

On your local machine:

# set the new repository name
export repo=dummy

# update the gitolite conf file with your new repository
cd ~/gitolite-admin
echo "        repo    ${repo}" >> conf/gitolite.conf
echo "                RW+     =   @all" >> conf/gitolite.conf

# push the config to your server
git commit -m "added new repository ${repo}" conf/gitolite.conf
git push 

# make sure you cd to an existing directory!
mkdir ~/Projects
cd ~/Projects

# pull back the new empty repository
git clone gitserver:${repo}

# go to repository and make any change
cd ${repo}
echo "*.pyc" >> .git/info/exclude # I use python
touch README

# do your first push back to master
git add .
git commit -am "repository ${repo} pushed to origin on gitserver"
git push origin master

You'll be using that last code section again, so you might want to make it into a script.

Never type anything you don't understand. 

If I've made any mistakes then please let me know.

Tuesday, 21 June 2011

Flask-Coffee - Fill your flask with coffee

Flask-Coffee is a Flask extension that compiles .coffee CoffeeScript files into .js JavaScript files for you if they have changed before your app renders.

This is a nice idea in development and not so nice an idea in production, so please keep that in mind.

Prerequisites

You will need to have to have Node.js installed with NPM and coffee-script.

You can follow the instructions in my previous article to install Node.js and then it's just

npm -g install coffee-script

Installation

Install Flask-Coffee with pip:

pip install flask-coffee

Usage

from flaskext.coffee import coffee

coffee(app) 

This will watch your app’s static media directory and automatically render .coffee files into .js files in the same (sub)directory.

The best way to incorporate Flask-Coffee into your development is as described for flask-lesscss in my earlier flask-script tutorial.

Contribute

If you want to contribute email me at the email at http://pypi.python.org/pypi/Flask-Coffee after checking out the code at http://bettercodes.org/projects/flask-coffee.

Monday, 20 June 2011

CoffeeScript Development in 3 mintutes

Any obvious reference to coffee needs a picture
The idea of CoffeeScript just sounds great me. I like the idea of compiling decent JavaScript from code you can actually read. But what do you do if you have no Node.js available to compile it?

Well the answer is to compile to JS at runtime. However, I had an awful time getting the right setup to get going. If you are interested in playing with CoffeeScript where Node.js is not available (I'm using it in work to show what it's capable of) then just follow these instructions.

Create a directory to work in and inside that create an index.html file thus:

<!DOCTYPE html>
<html>
<head>
    <title>CoffeeScript Demo</title>
    <script data-main="scripts/requirements" 
               src="scripts/require.js"></script>
</head>
<body>
    <h1>CoffeeScript Demo</h1>
</body>
</html>

and a directory called 'scripts' into which you put the following.

require([
    'scripts/jquery-1.6.1.min.js',
    'cs', 
    'cs!app',
]);

  • And a file called app.coffee containing:
square = (x) -> x * x
alert square 42

That's it!

Now load the index.html page into your browser and the CoffeeScript code app.coffee should execute.

Of course you will now have to go away and read the docs so that you know why that worked, but then so will I.

Wednesday, 15 June 2011

Flask Extensions For Authorization with Examples

In my opinion, there is one serious omission to Flask at the moment and that is that there is no authoritative method for authorizing users. 

In a way this goes with the general ethos of micro frameworks - it isn't being forced down your throat unless you want it.

However in this case I think that at least some structured 'recipes' are required to provide peer guidance, best practice or whatever you want to call it. Too often the advice is "roll your own", but I'm not sure I'd be comfortable recommending that to the inexperienced.

So where are we?

Flask-Principal and Flask-Login

At the moment there is only one approved auth extension called Flask-Principal which takes care of authorizing different roles of user. It does not dictate what those roles should be and it does not force you to use any particular method to authenticate those users.

A second extension called Flask-Login is currently awaiting approval, which I suspect it will soon get. This extension also takes care of authorizing users, but only understands one type of user role, so in other words you are either a user or not. Like Flask-Principal it does not force you to use any particular method to authenticate those users.

Example Code

The code below is sort of a basic outline that we will fill in using proper code in a moment.

The User model is very silly. If you create a User by passing an id of '1' you get a User with a username of 'user1' and a password of 'user1_secret'. To help with the examples we then create users one to twenty (user1 to user20). Obviously you would normally have them stored in a database or something, but we're keeping it simple.

At this stage there is nothing protecting access to the 'home' resource and 'login' and 'logout' don't work yet either.

from flask import Flask, Response, redirect, \
    url_for, request, session, abort

app = Flask(__name__)

# config
app.config.update(
    DEBUG = True,
    SECRET_KEY = 'secret_xxx'
)

# middlewares etc go here
      
# silly user model
class User(object):

    def __init__(self, id):
        self.id = id
        self.name = "user" + str(id)
        self.password = self.name + "_secret"        
    def __repr__(self):
        return "%d/%s/%s" % (self.id, self.name, self.password)


# create some users with ids 1 to 20       
users = [User(id) for id in range(1, 21)]


# we'd like to protect this resource
@app.route('/')
def home():
    return Response("Hello World!")


# somewhere to login    
@app.route('/login', methods=['GET', 'POST'])
def login():
    pass # this does not work yet

# somewhere to logout
@app.route("/logout")
def logout():
    pass # this does not work yet
    
        
# handle login failed
@app.errorhandler(401)
def page_not_found(e):
    return Response('Login failed')


@app.errorhandler(403)
def page_not_found(e):
    return Response('Unauthorized')


if __name__ == "__main__":
    app.run()


Download the code!

Flask-Principal Example
Now that we know how that works, or doesn't, let's look first at how flask principal might set about protecting the home resource.

As I said earlier, Flask-Principal doesn't tell you what roles you need for your app. In this example I'm only using one called 'normal' which we need to tell the extension about before we tell Flask to initialize the extension:

# flask-principal
principals = Principal()
normal_role = RoleNeed('normal')
normal_permission = Permission(normal_role)
principals._init_app(app)

After that we can protect the 'home' resource thus:

@normal_permission.require(http_exception=403)

If your request's session doesn't have all the stuff it's asked for (ie. has not logged in) then Flask-Principal gives it a HTTP 403 (Forbidden) response which redirects it to:

@app.errorhandler(403)
def page_not_found(e):
    session['redirected_from'] = request.url
    return redirect(url_for('login'))

Which, as you can see stores the URL that you wanted in the first place ('home' in this case) and then redirects you to the login page. Since by default this request is a GET, you are shown the login form and not the login process which is used for POSTs.

Enter a valid username/password combination, e.g. 'user7' and 'user7_secret' and you get logged in and sent back to the 'home' page. The login sends a signal to the extension to tell it who has logged in and congratulations you get to see the 'Hello World' message!

If you enter an invalid username/password combination you get served an HTTP 401 (Unauthorized) and thus you end up in the 401 errorhandler seeing 'Login Failed'

If you go to 'logout' you the code will destroy any of the relevent session information and tell you you are 'Logged Out':

@app.route("/logout")
def logout():
    for key in ['identity.name', 'identity.auth_type', 'redirected_from']:
        try:
            del session[key]
        except:
            pass
    return Response('Logged out
')

Try out the code!

Flask-Login Example
With Flask-Login you need to tell Flask to use the extension and this time tell the extension where the login page is (in this case "login"):

# flask-login
login_manager = LoginManager()
login_manager.setup_app(app)
login_manager.login_view = "login"

Secondly you have to use a mixin to create your User class. The mixin just makes some standard functions available for the User.

class User(UserMixin):

Thirdly, you need to set up a user_loader that will load your user based on the user id:

# callback to relaad the user object        
@login_manager.user_loader
def load_user(userid):
    return User(userid)

This time when you go to 'home' and you haven't yet logged in, you don't get a HTTP 403 (Forbidden), but instead get sent to the resource you defined earlier; 'login'.

Login successfully and this time the login retrieves the actual User object and tells the extension using the login_user function. Again, congratulations!

Try out the code!

You can work out for yourself how to store the User object in a database or datastore and how to check the passwords. There are a good ways to do both, but this is not an article about that. Should we have one in a future article?