Caramel

A bootstrap project to create fully dynamic websites using jaggery

This project is maintained by wso2

Lets Start Tasting Caramel

Caramel is a MVC framework built for Jaggery. The intent of the framework is to bootstrap application development and enforce a standard for developing web applications using Jaggery. By no means Caramel trying to lock down developer creativity or freedom but simply trying enforce discipline to large development teams working on Jaggery projects.

A web application written according to Caramel, takes a certain structure, this is to separate the models, controllers and the views

Lets learn by an example; Our use case for the study is a static blog. where you drop posts as files to a defined location and the blog will pick them and show in an appropriately formatted UI

There are few core elements in Caramel

Apart from the above constructs, some of the important concepts behind Caramel is the use of Partials, Pages, Helpers and Modules

Before we start with the blog example; First let's write a simple Hello World Jaggery App.

A simple “Hello World” with Caramel

First the application config /hello/app.js

caramel.configs({
    context: '/blog',
    negotiation: true,
    themer: function () {
        return 'classic';
    }
});

The meaning of defining a theme is that, by a configuration change, a new theme can be plugged to the application, and the concept is native to Caramel

Lets call the homepage of the app “index.jag” (/hello/index.jag). In Caramel, the page that servers the request act as one of the controllers of the app. it defines what data needs to be presented in that particular page. Content of the page will be as follows

<%
   var caramel,
   body = "Lorem ipsum dolor sit amet";

   caramel = require('caramel').caramel;
   caramel.render({
     'title': {text :'Hello World'}, // set html title for index page
     'body': {text:body} //set html body for index page
   });
%>

This script will provide the title as "Hello World" and the body as "Lorem ipsum" to caramel render function.

Now lets move to the theme, first we need to set handlebars as the default caramel theming engine. Under the themes/classic directory lets create theme.js script

var engine = caramel.engine('handlebars', (function () {
}()));

lets write our handlebars template which is going to define the view for our theme. Create simple.hbs inside themes/classic/pages

<html>
<head>
    <title>{{include title}}</title>
</head>
<body>
    <div>{{include body}}</div>
</body>
</html>

This will let our theme to include the title and the body partials, which we are going to create in the next step. Partials come to play when you have a single template that you need to use in different contexts. For example you can use above template in different contexts by setting different titles and body contents.

Now lets create above inside themes/classic/partials/title.hbs and body.hbs directory,

title.hbs {{{title}}}

body.hbs {{{body}}}

Lets move to the renderer, Create inside themes/classic/renderers/index.js this is the rendering function for index.jag page. It define from which partial the title has to be rendered and whats the data set needed for the partial

var render = function (theme, data, meta, require) {
    theme('simple', {
    title: [
         { partial:'title', context: data.title}
     ],
     body: [
         { partial:'body', context: data.body}
     ]
    });
};

Hit hello/index.jag in the browser and that should render over caramalized hello world app. The final directory structure of the app will be

/index.jag
/themes
+---/classic
-------/theme.js
+------/renderers
------------/index.js
+------/partials
------------/title.hbs
------------/body.hbs
+------/pages
----------/simple.hbs

Lets write a real world application

As we talked about, lets take a blog engine as an example, and compose one using caramel.

First create a module with a function which go through files within a given directory and reads file content. Will call this module reader, create reader.js inside top level modules directory with readPosts function. [/modules/reader.js]

var readPosts = function () {
    var path = "content/posts/";
    var dir = new File(path);
    var list = dir.listFiles();
    var body = "";
    var timeArray = [];

    for(var i=0; i < list.length; i++) {
     var timestamp = list[i].getName(); //read the filename, file created //timestamp
     timeArray.push(timestamp);
         // sort filename array in //order to display newest post on the top
     timeArray.sort(function(a,b){return a-b}); 
    }

    while(filename=timeArray.pop()) {
         var blogFile = new File(path+filename);
         blogFile.open("r");
         var post = "";
         post = blogFile.readAll();
         body +=post;
         body +="<br/>";
         blogFile.close();
    }

    return body;
};

We need to require this module within our main Controller, so let's update our home page index.jag

<%
    var caramel,
    reader = require('/modules/reader.js'), //include our reader module
    caramel = require('caramel').caramel;

    caramel.render({
     'title': {text :'Blog'},
     'body': {text:reader.readPosts()}
    });
%>

As in our Hello World app, we need to define handlebars as the default themeing engine. Under the themes/classic directory lets create theme.js

var engine = caramel.engine('handlebars', (function () {
}()));

lets write some HTML with handlebars which is going to define the view for our theme classic. As we have done earlier, create themes/classic/pages/simple.hbs

<html>
<head>
<title>{{include title}}</title>
<link href="{{url "/themes/classic/css/styles.css"}}" rel="stylesheet">
{{css .}}
<script src="{{url "/themes/classic/js/scripts.js"}}"></script>
{{js .}}
</head>
<body>
{{>header}}
<div>{{include body}}</div>
{{>footer}}
</body>
<html>

The above is the page layout that will let us include the title, header, footer and the body sections (Partials)

{{css .}} and {{js .}} are two helpers that includes default css and js in respective directories, these helpers should be placed tothemes/classic/helpers`` and will look like

var resources = function (page, meta) {
    return {
    js: ['navigation.js', 'jquery.validate.js'],
    css: ['navigation.css']
    };
};

Inside Partials we have title.hbs , body.hbs and footer.hbs as same as in Hello World app

Our renderer is also the same at themes/classic/renderers

var render = function (theme, data, meta, require) {
    theme('simple', {
    title: [
         { partial:'title', context: data.title}
     ],
     body: [
         { partial:'body', context: data.body}
     ]
    });
};

Now create few HTML formatted files inside content/posts directory with created timestamp as the filename and browse our home page. You can checkout the sample app from GitHub