Saturday, April 24, 2010

GORM vs ColdMVC ORM

One of the more interesting session's at cf.Objective() was Matt Woodward's presentation CFML on Grails. In the presentation, Matt showed how he was able to create a CFML plugin for Grails in order to integrate ColdFusion and Grails.

While the code worked, it seemed a little sketchy at times trying to get the two languages to play nice together cohesively. Even so, Matt achieved his goal of integrating the languages, so he gets props for that.

In his presentation, Matt said one of the biggest advantages of using Grails is being able to leverage Grails Object Relational Mapping (GORM), which is essentially a user-friendly abstraction layer on top of Hibernate.

While Matt took the approach of integrating ColdFusion into Grails in order to use GORM, I took the opposite approach by trying to recreate GORM in ColdFusion inside my ColdMVC framework. Not everything from GORM has been ported over yet, but here's a set of examples for comparison's sake. The top line in each pair is Grails, while the second line is ColdMVC.


def count = Book.count()
var count = _Book.count();

def count = Book.countByTitle("The Shining")
var count = _Book.countByTitle("The Shining");

def count = Book.countByTitleAndAuthor("The Sum of All Fears", "Tom Clancy")
var count = _Book.countByTitleAndAuthor("The Sum of All Fears", "Tom Clancy");

def book = Book.find("from Book as b where b.author=:author",[author:'Dan Brown'])
var book = _Book.find("from Book as b where b.author=:author",{author='Dan Brown'});

def book = Book.findAll("from Book as b where b.author=? order by b.releaseDate",['Dan Brown'],[max:10, offset:5])
var book = _Book.findAll("from Book as b where b.author=? order by b.releaseDate",['Dan Brown'],{max=10, offset=5});

def book = Book.findByTitle("The Shining")
var book = _Book.findByTitle("The Shining");

def books = Book.findAllByTitleLike("%Hobbit%")
var books = _Book.findAllByTitleLike("Hobbit");

def books = Book.findAllByTitle("The Shining", [max:10, sort:"title", order:"desc", offset:100])
var books = _Book.findAllByTitle("The Shining", {max=10, sort="title", order="desc", offset=100});

def book = Book.findWhere(title:"The Shining", author:"Stephen King")
var book = _Book.findWhere({title="The Shining", author="Stephen King"});

def books = Book.findAllWhere(author:"Stephen King")
var books = _Book.findAllWhere({author="Stephen King"});

def book = Book.get(1)
var book = _Book.get(1);

def books = Book.getAll(2,1,3)
var books = _Book.getAll(2,1,3);

def books = Book.getAll([1,2,3])
var books = _Book.getAll([1,2,3]);

def books = Book.list()
var books = _Book.list();

def books = Book.list(max:10, offset:100, sort:"title", order:"desc")
var books = _Book.list({max=10, offset=100, sort="title", order="desc"});


As you can see, aside from a couple small syntax differences, they're almost identical.

Wednesday, April 21, 2010

ColdMVC: Event Listeners

ColdMVC provides your application with several interception points throughout the lifecyle of a request. This is possible thanks to centralized event dispatching from ColdMVC’s EventDispatcher component. In a typical ColdMVC request, the following events will be dispatched:

• requestStart
• actionStart
• preAction
• pre:{controller}Controller
• pre:{controller}Controller.{action}
• action
• post:{controller}Controller:{action}
• post:{controller}Controller
• postAction
• actionEnd
• requestEnd

Any controller within the application can listen for these events by applying metadata to the desired listener method. The events metadata is a comma-separated list of regular expressions, providing quite a bit of flexibility in intercepting. As an example, if you wanted a SecurityController to verify a user is logged in at the beginning of each request, you could have the following code:

component {

/**
* @events requestStart
*/
function verifyLoggedIn() {
if (!session.isLoggedIn) {
redirect({controller="security", action="logout"});
}
}

}

Furthermore, ColdMVC will implicitly invoke certain methods on your request’s controller if they are defined. Before the request’s action is invoked, ColdMVC will invoke the controller’s pre and pre{Action} methods if they exist. Next, ColdMVC will invoke the action for the request, followed by the post{Action} and post methods if they exist. For example, if the current requst is /product/list, ColdMVC will invoke ProductController.pre(), ProductController.preList(), ProductController.list(), ProductController.postList(), and finally ProductController.post().

Tuesday, April 20, 2010

ColdMVC: Plugins

Plugins are custom functions that are available to your views and layouts that help keep your presentation layer clean. Plugins are defined using a simple .cfm file that maps a plugin name to a method on either a helper or a bean defined within ColdSpring.

When you first create an application using ColdMVC, you'll already have access to a standard set of plugins, defined inside /coldmvc/config/plugins.cfm. The most prominent plugin is the linkTo() method that builds URLs for your views.

When ColdMVC loads, it will load any plugins that are found inside your application's /app/config/plugins.cfm, then any plugins inside /coldmvc/config/plugins.cfm. If you would like to override one of ColdMVC's plugins, simply define your own plugin with the same name as the ColdMVC plugin.

While your views and layouts still have access to all of your application's helpers, it is recommended to use plugins rather than the helpers. Even though they will generate the exact same HTML, #linkTo({controller="post", action="list"})# reads a lot better than #$.link.to({controller="post", action="list"})#.

ColdMVC: Custom Tags

Writing views and layouts can be tedious work, especially if you want to ensure you’re using consistent HTML markup around your form fields. To make views a little less painful, it’s best to use custom tags to generate the HTML for you. That way, you don’t have to worry small things like radio button markup and instead can focus on more important things, like your application’s business logic.

A new installation of ColdMVC will already have a standard library of custom tags available to use inside your views, such as <c:input />, <c:select />, and <c:textarea />. These files are all located within /coldmvc/tags.

You can create your own custom tags in a ColdMVC application simply by placing a .cfm file inside your application’s /app/tags folder. For example, if you created a file located at /app/tags/tony.cfm, you would then have access to that custom tag inside your views and layouts by using <c:tony />.

When ColdMVC first loads, it will load any custom tags located within your application’s /app/tags folder, then any custom tags inside /coldmvc/tags folder that haven’t been loaded yet. These custom tags will then be available to all of your views and layouts.

Most of the default custom tags inside ColdMVC simply delegate their functionality to a ColdMVC helper component, so extending the default custom tags can be done by extending the helpers. If you would like to completely override a custom tag that ships with ColdMVC, simply create a custom tag with the same file name inside your application’s /app/tags folder, and ColdMVC will load your custom tag rather than ColdMVC’s custom tag.

By default, ColdMVC will import the custom tags using a “c” prefix. If you would like to change the prefix to something else, such as “foo”, you can do so inside your /config/config.ini file by setting tagPrefix=foo. However, it is recommended as a best practice to use the default tag prefix of “c” to provide framework consistency across projects.

In a typical ColdFusion application, you need to import your custom tags into each ColdFusion template using the <cfimport /> tag. However, you do not have to do this inside a ColdMVC application. For each view and layout in your application, ColdMVC will automatically generate a corresponding .cfm file inside your application’s /.generated folder that contains the content of your template along with the <cfimport /> tag.

Most of the templates are generated the first time they are requested, with the exception of files beginning with an underscore, which are considered “private” templates by convention and therefore are generated when the application first loads.

Monday, April 19, 2010

ColdMVC: Helpers

ColdMVC allows you to create helper components that are globally available to your entire application. These helper components are good for containing useful functions similar to those found on cflib.org.

When ColdMVC first loads, it will load any helpers located within your application’s /app/helpers folder, then any helpers inside ColdMVC’s /helpers folder that haven’t been loaded yet. These helpers will then be globally available throughout your application inside the $ variable.

ColdMVC ships with a handful of helpers that can be found inside /coldmvc/helpers. If you would like to override the functionality of one ColdMVC’s helpers, simply create a .cfc with the same name inside your application’s /app/helpers directory, extend the corresponding ColdMVC helper, and make your changes.

You can create your own helpers by creating a .cfc and placing it inside your application’s /app/helpers folder. The newly created helper is then available throughout your application inside the $ variable. For example, if you created a file located at /app/helpers/tony.cfc, you would then have access to that helper throughout your application by using $.tony.

Friday, April 16, 2010

ColdMVC: Routing

At the most basic level, all a web application does is accept a user’s request, process some data, then send a response back to the user. Figuring out what and how the application should process is what routing is all about.

A typical URL inside a ColdMVC application will look like following:

http://myapp.com/index.cfm/product/show/1

Using pattern matching defined in a config file, ColdMVC is able to determine the appropriate controller and action that should be executed for the request, as well as any other parameters that should be populated into the request. For example, the previous URL would result in the show method being executed on the ProductController, while also setting params.id to “1”. This is because the incoming request, “/product/show/1” matches the default pattern “/:controller/:action/:id”, defined inside /coldmvc/config/routes.cfm.

You’re able to create your own custom routes inside your application’s /config/routes.cfm file. Custom routes are especially handy when creating user-friendly URLs. For example, you could create a pattern that routes anything matching “/user/:name” to UserController.show(). Here’s what that route would look like inside the routes.cfm config file:

<cfset add("/user/:name", {defaults = {controller="user", action="show"}}) />

ColdMVC offers several other features for handling incoming routes, such as parameter requirements, default parameters, and computed parameters, that I'll cover in more detail in future posts. However, knowing just the basics should cover most routing scenarios.

Recognizing routes makes up only half of routing. Since it’s possible for ColdMVC to consume URLs that match a pattern, its equally important that ColdMVC is able to generate those same URLs as well.

The easiest way to generate a URL inside a view or a layout is by using the #linkTo()# plugin. This method can accept three arguments: a struct of parameters, a querystring, and a name. For most links in the application, you’ll only need to specify the parameters, which typically consists of keys for the controller, action, and id.

For example, if you want to link to a product’s record, your code might look like this:

<a href=”#linkTo({controller=”product”, action=”show”, id=product})#”>#product.name()#</a>.

If you don’t specify a controller or an action in the parameters and a matching route can’t be found, the router will look again, only this time it will add the current request’s controller and action to the parameters.

There are several more advanced features of URL generation that I haven’t discussed yet, such as named routes and model-based routes, so I’ll go over them in more detail in future posts as well. In the meantime, if you’re curious to see how these features work, take a look at the routes defined for the sample blog application, located at /coldmvc/samples/blog/config/routes.cfm.

Thursday, April 15, 2010

ColdMVC: Params Scope and Flash Scope

In addition to the standard ColdFusion scopes, ColdMVC “adds” two additional scopes to your application: params and flash. This is possible by leveraging ColdFusion’s getPageContext().getFusionContext().hiddenScope variable.

The params scope will contain all FORM and URL variables, as well as any parameters created by the current request’s route. All variables within the params scope are automatically copied into the variables scope of views and layouts for quicker reference.

The flash scope is a slightly temporary scope. Any data put into the flash scope lasts for the life of the current request, and is then copied into the params scope of the next request. This is useful for displaying one-time status messages to the end user when records are successfully added, edited, or deleted.

ColdMVC: Layouts

Layouts allow your application to have a consistent look and feel as well as help keep your views clean and focused. Layouts are simply .cfm files located inside your application’s /app/layouts/ folder.

By default, ColdMVC will look for a layout with the same name as your controller. For example, if you were make a request to http://myapp.com/index.cfm/product/list, ColdMVC would look for a layout located at /app/layouts/product.cfm. If it cannot find a product.cfm, it look look for the default layout, located at /app/layouts/index.cfm. This is useful if your application has several controllers but only 1 main layout.

If you want to pass data to your layout, create a LayoutController.cfc inside your application’s /app/controllers/ folder and have it extend coldmvc.LayoutController. Then create a function inside the LayoutController with the same name as the request’s controller. In the previous example, you would create function named product. Inside the function, any data put into the params scope will be automatically copied into the variables scope of the layout. If a product function isn’t defined inside the LayoutController, ColdMVC will look for an index method and call that instead.

By default, each controller’s corresponding layout is the same name as the controller. You can change this by adding @layout metadata to your controller’s metadata. If you would like to change the layout for an individual action within a controller, you can do this by adding @layout metadata to the function.

If your request uses a layout, your layout is in charge of rendering the view’s content. This can be done simply by calling #render()# inside your layout where you want your view’s content to be displayed.

Wednesday, April 14, 2010

ColdMVC: Directory Structure

Since ColdMVC is a convention-based framework, it’s important that applications follow a similar directory structure in order to take advantage of the framework’s conventions. A typical ColdMVC directory structure looks like the following:


root/
app/
controllers/
LayoutController.cfc
layouts/
index.cfm
model/
views/
config/
coldspring.xml
config.ini
environment.txt
hibernate.hbmxml
plugins.cfm
routes.cfm
public/
css/
images/
js/
index.cfm
Application.cfc


Here's a brief description of the various files and folders. Any file or folder marked with an asterisk(*) is considered optional. The framework is not dependent on them.

root/ - The root of your project.
root/app/ - Contains the majority of your ColdMVC application (.cfm and .cfc files).
root/app/controllers/ - Contains your application’s controllers.
root/app/controllers/LayoutController.cfc* - Handles putting data into layouts.
root/app/layouts/* - Contains your application’s layouts.
root/app/layouts/index.cfm* – The default layout.
root/app/model/ - Your domain model. Contains any persistent entities and services.
root/app/views/ - Contains your views.
root/config/* - Contains your application’s configuration files.
root/config/coldspring.xml* – Contains any custom ColdSpring bean definitions.
root/config/config.ini* – Contains your application’s settings.
root/config/environment.txt* – Contains the name of your current environment, which is used to determine your settings.
root/config/hibernate.hbmxml* – Contains your Hibernate mappings.
root/config/plugins.cfm* – Contains any custom view plugins.
root/config/routes.cfm* – Contains any custom routes for your application.
root/public/ - Your application’s web root.
root/public/css/* - Contains any cascading stylesheets.
root/public/images/* - Contains any images.
root/public/js/* - Contains any JavaScript files.
root/public/index.cfm – Your application’s index page. All requests are routed throught his file.
Application.cfc – Your project’s application page. Typically just extends coldmvc.Application.

ColdMVC: The "MVC" Part

ColdMVC follows the Model-View-Controller (MVC) design pattern. In this type of application, incoming requests are handled by a controller, which retrieves data from the model, and passes it to the appropriate view for rendering.

The model layer contains your application’s business logic, typically inside persistent entities or service objects. Models in a ColdMVC application serve two purposes: to represent individual records in your database through properties as well as to provide database access through an abstraction layer on top of Hibernate ORM.

Models in ColdMVC contain several methods that help you easily query your database. Most of the methods are based on domain class methods found in Grails. Here’s a quick list of the what’s currently supported:

• add()
• count()
• countWhere()
• exists()
• findAll()
• findAllWhere()
• findWhere()
• get()
• has()
• list()
• new()

In addition to the methods mentioned above, ColdMVC also supports dynamic methods that are evaluated at runtime, which is made possible by leveraging ColdFuion’s support of onMissingMethod. For example, assuming you had a User model with properties for user name and password, the following code is completely valid without having to actually define the method on the User model:

params.user = _User.findByUserNameAndPassword(params.userName, params.password);

The view layer is the presentation layer of your application, in charge of displaying data from the model and providing the user with forms to input new data. Views are located inside your application’s /app/views/ folder and are chosen based on the incoming request. For example, a request to “/product/list” would display the view located at /app/views/product/list.cfm.

The controller layer is responsible for accepting a request, interacting with the model, and rendering the appropriate view. Controllers in a ColdMVC application are found inside the /app/controllers/ folder. The name of a controller should be the corresponding model name followed by “Controller”. For example, if your application has products, you would have a ProductController. As long as you follow this naming convention, ColdSpring will automatically find your controllers and autowire them as singleton objects.

By default, the corresponding model will be automatically autowired into the controller and be available as _{Model}. For example, the ProductController will have access to products through a _Product variable. Any other models can be autowired into a controllers using properties. If you wanted your ProductController to access categories, you can autowire a Category model into your controller by adding a _Category property.

Tuesday, April 13, 2010

An Introduction to ColdMVC

Since cf.Objective() is less than 10 days away, I thought it would be a good time to provide a little more insight into my recently released framework, ColdMVC. That way, if people are interested in it or have questions, they can bug me in person at the conference.

I'm going to try to write a blog post every day for the next couple of days, with each one focusing on a different aspect of the framework. Hopefully they're somewhat helpful and educational.