Showing posts with label mvc. Show all posts

Boss MVC - A Lightweight Coldfusion MVC Framework

I don't normally do side-projects using ColdFusion, but one night some time last year, after drinking some neat vodka, I ended up playing around with ColdFusion and building an MVC framework.

The code isn't by any means production ready, and is only the product of two nights (and several glasses of vodka) but it was an experiment into what kind of nice MVC framework type functionality could be exposed in CF.  I have only ever used Fusebox, which as an MVC framework is found wanting in many instances, however, I know there are lots of other MVC frameworks in CF, that undoubtedly have nicer features.

Anyway, it's all over on my GitHub, but I thought I would re-post the details here. Just for funsies.

Some of the main things I wanted to experiment with were involved with simplifying of the framework and removal of boiler plate code. E.g. :

  • Annotation or config defined mapping for URL > Controller methods
  • Simple returns from controllers - just returning view name and model data (to remove the temptation of building HTML code in controllers - a clearer separation of controller and view
  • clearly & appropriately scoped variables in the views (everything to local, as under the hood, everything is being rendered from a CFC, so if you don't local scope the view then you may get surprises)

Controllers:

At the moment, it is dead simple to configure, just grab the framework and create a controller as follows (it assumes that there will be a directory in the root of your webapp called controllers)
/controllers/Home.cfc:
/**
 * @Controller
 **/
component output = "false"  {

    /**
     * @RequestMapping
     **/
    public Array function default( Struct resourceParams="" ){
        return [ "homepage" ];
    }
}
The above code will direct all requests to the application root ("/") to the default() function. The URL pattern for either the @Controller or @RequestMapping can be left blank (or populated) - the values provided are concatenated to produce the URL mapping for a function.
You can also have variable sections of the URL: /controllers/User.cfc
/**
 * @Controller /user
 **/
component output = "false"  {

    /**
     * @RequestMapping /:username
     **/
    public Array function default( Struct resourceParams="" ){
        return [ "userpage" ];
    }
}
In the above example, you can see that @Controller and @RequestMapping have a URL pattern defined. You will also note the @RequestMapping has a ":" before username - this indicates a variable section of the URL. This code will handle requests that hit the URL /user/rob etc - Any named variable section of the URL will be available to the controller function in teh resourceParams struct (as seen above).

Views:

View definition and resolution is also real simple (hopefully). Firstly, you can define a view layout by name - this defines a named view and CFM template, along with any templates that make up the overall view. This is all configured in JSON notation.
At startup, the framework scans the /views directory for any "*.config" files - any it finds it loads up, and then the views defined will be available for use in the application.
/views/view-layout.config:
[
    {
        "name": "homepage",
        "template": "home.pagetemplate",
        "include": {
            "header": "home.header",
            "body": "home.body",
            "footer": "home.footer"
        }
    },
    {
        "name": "userpage",
        "template": "user.pagetemplate",
        "include": {
            "header": "user.header",
            "body": "user.body",
            "footer": "user.footer"
        }
    }
]
The above defines two views, "homepage" & "userpage" - both are made up of similar components. The convention for the templates is relative to the /views directory, and uses "." instead of "/" (e.g. "template": "user.pagetemplate" will expect a file /views/user/pagetemplate.cfm ).
As you can hopefully work out from the above, the "homepage" view is based on /views/home/pagetemplate.cfm and is made up of the three templates /views/home/header.cfm, body.cfm, footer.cfm.
All nested templates are simply placed in the local scope (for the template being rendered), so our pagetemplate cfm looks like this:
/views/home/pagetemplate.cfm:
<html lang="en">
    <cfoutput>#local.header#</cfoutput>
    <body>
        <cfoutput>#local.body#</cfoutput>
        <cfoutput>#local.footer#</cfoutput>
    </body>
</html>
Once we have built a view definition, and have created our templates, a controller simply has to return the viewname as the first argument of an array. If we look again at our Home controller:
/controllers/Home.cfc:
/**
 * @Controller
 **/
component output = "false"  {

    /**
     * @RequestMapping
     **/
    public Array function default( Struct resourceParams="" ){
        return [ "homepage" ];
    }
}
We see that we just want to render the "homepage" view for all requests. Simple controller & simple view composition. Pretty boss.

Models:

Obviously, most of the time we will want to pass data (in the form of a model) to our views for rendering, this is also really simple!
We have seen that in the controller, if there are variable names in the URL that is passed to the controller function in the arguments.resourceParams struct - That data is also automatically passed through to the view template, but can be supplemented by any data accessed in the controller.
Let's look again at out User controller, but lets say we want to pass some additional data to the view for rendering:
/controllers/User.cfc:
/**
 * @Controller /user
 **/
component output = "false"  {

    /**
     * @RequestMapping /:username
     **/
    public Array function default( Struct resourceParams="" ){
        return [ "userpage", { greeting: "Yo!"} ];
    }
}
This time, we are returning a second element in the array - this is a Struct of the model data, this Struct could contain any data you have fetched in the controller that you want to pass to your view template for rendering. As we have a variable URL section (username) and are passing some model data back to the view, both of these can be used.
/views/user/body.cfm:
<h1><cfoutput>#local.model.greeting#</cfoutput> <cfoutput>#local.model.username#</cfoutput>!</h1>
The URL variable sections and the returned model data are all available to the templates in "local.model".

Spring and Neo4J integration - A Quick WebApp



Following my experimentation with MongoDB and the SpringData integration, I was keen to try out a few other NoSQL options - notably something outside of a document based data store. I had heard about a competition asking for submissions for Neo4J on the Heroku platform, which initially sparked my interest (although as of yet I have not tried it on Heroku) and seeing as Spring Data already had the Neo4J integration I thought I would have a go at it. Plus, I enjoy graphs as a data structure. I have fairly recently done some work on graphs having to implement Dijkstra's algorithm (also the A*) so this seemed like it was going to be fun.

Firstly, the documentation, in the form of an online e-book, for Neo4J Spring Data is awesome. Some guys have put together the e-book based on their experience setting up a Neo4J website and it really does cover everything from the first steps through to more complex stuff around traversal and transaction management. This also means, there is little point in me going through my every step in my app, as it wouldnt be very different from their advice, so most importantly, read this!!!

I will however, just highlight some interesting points i discovered along the way:

1. Simple Annotation based Entites
As you would expect, much like JPA or MongoDB, Neo4J supports simple annotation based configuration of your domain model to simply and quickly describe some pretty rich graph relationships. This is not that unexpected, as it is Spring after all, but worth noting that it is pretty comprehensive and easy to use.


2. Magic Repository Methods
A bit like in Grails, the Neo4J supports "magic" methods, that allow you to just define method names and the query implementation will be completed for you. For example, in your Movie repository (As per the book example, and also my example) if you defined a method such as:

Movie getMovieById(String id);

Then you do not need to provide an implementation, and Spring will automagically do the rest. (the convention being get[DOMAIN_ENTITY]By[ENTITY_PROPERTY]()

From the book: "In our wildest dreams we imagined the method names we would come up with, and what kinds of queries those could generate"

3. Arbor JS Graph Framework
A little while ago I discovered the Arbor JS framework for visualisation of graphs, and its really nice - I had to try and achieve something similar some time last year and got some of the way using core html5/JS but this library is a lot more comprehensive so wanted to have a go.
First, you should check out the demos over at http://arborjs.org/, they are really nice, and make for a really nice graph UI (see screenshot of my app).


4. The Data
I wanted to get some real data, and looked at a few big datasets available online such as the Stanford Large Data Set Network but being as Im only running locally, and I didnt really want to spend too much time getting the data into my graph model I decided to go for a far smaller, simpler dataset - this years Oscar nominations (actors/directors/movies/awards - hopefully you can make out the graph from the screenshot). The data I got from the Guardian (probably UK only) data set blog which also had some nice data sets in it, but this one seemed to be a small enough size for me not to loose too much time to the data.


As always, the code is up on my github, so feel free to check it out, fork/download/mistreat etc..

Hands On NoSQL - Integrating MongoDB & Spring MVC


Over the weekend, I took some time out of my side project to have a play with MongoDB. I was interested in looking at NoSQL and getting my head around some of the implementation details and had heard document oriented NoSQL DBs would be a good place to start.

So I stripped out the guts of a web app I have built, and dropped in a very simple domain model (the app just allows creation of resumes - I chose it as I wanted a model that made sense to use a document oriented DB rather than trying to crowbar in a model that is still relational) - then using Spring I looked at wiring all the MongoDB stuff. I was pleasantly suprised with how easy it was to drop it all in.

This also gave me a chance to finally play with CloudFoundry - I've had my beta account for a while now, but seeing as CF supports MongoDB and Spring/Java apps I figured this would be a perfect chance to test it out. Again, it was relatively easy to configure the app and CF to get the app up and running using the Clouds mongo DB service.

The other bit of experimentation I have done is rather than writing this up in a massive blog post, I have signed up for slideshare and uploaded a presentation with the details - you can check out the presentation embedded above - would love to hear feedback as to whether you prefer slideshows or traditional blog posts - for me its harder to get a lot of details in slides without over-crowding them, but then if its a very detailed walkthrough it can make for an over-long blog post.

As always, the complete source code for the app (Spring MVC web app with full mongoDB configuration, including config for both localhost and CloudFoundry deployment) is available here!