
commentit
Introduction
Revolutionize your web pages – easy, natural and
self-documenting. No more mind-numbing work-arounds. No more
redundant style-sheets and/or scripts in your page source code. No more
XML namespaces just to have your HTML markup validate. No more JSP Tags
(thank gawd!). Use HTML markup like it was intended to be used.
Really, it’s absolutely transparent.
And what else makes this solution absolutely indispensable?
- It works seamlessly with ANY existing expression-language
framework, for instance MVEL, OGNL, JEXL, String-Template etc. You pick what you want and run with it. - You can visually test your pages in your favorite browser, without a
web-server. You can put all your style-sheets and scripts on the
static page, then when you are satisfied with the result, you
simply “commentit”, and the page will be ready for production. - It captures the essence of fast page-loading by providing the
leanest page possible and a place-holder for “body” scripts,
which should execute after the page is loaded and rendered. The page output lends itself easily to compression and
g-zipping. The solution does not attempt in any way to
manipulate the response however. It assumes you should be able
to manipulate the appropriate response headers yourself.- You can optionally apply the BundleFilter which ships with the library to bundle up (put in one file) all your stylesheets and javascript, and in addition compress (g-zip) your output if the filter detects that the client browser supports g-zip.
- Perfect replacement for Sitemesh and Tiles if you are NOT working with JSPs
Ok, so how do you do this? The solution is to make use
of markup comments to describe the structure of your page.
That’s it!!
Breaking It Down
At the end of the day, whatever solution you use to structure or lay-out your
page, the browser sees your page as one input-stream and it
renders this stream progressively from top to bottom. This is the
same concept used by the “commentit” solution – build your page
output progressively from top to bottom.
”CommentIt” introduces some five very simple concepts (definitions) that
capture the general structure of a web-page.
- A Page – a page is the file which is targeted for decoration using a specified
“layout” file. The page may within itself contain
“widgets” and/or “snippets”. - A Widget – A widget is a self-contained markup file. It has it's own js and css. It can be reused across different pages and also on different projects. For this last reason, widgets definitions are ideal candidates for storing in a database table and having the parser resolve which one to use during runtime
- A Snippet – A snipppet is basically the same as a widget. However, it may or may not have it's own js and css. For this reason, snippets are ideal candidates for reuse in different pages within a single project. Using a snippet on a different project would more likely than not, require changes to its css and/or js
- A Layout – this is the markup which decorates a page, and
determines to a large extent the look and feel of the website. The layout contains place-holders for different
parts of the page which are resolved and inserted dynamically. - Static Removable – this is markup on your definitions which is used
ONLY while statically testing your page in your browser. This
markup is STRIPPED OFF when the page template is being created.
Complete Example
samples/layout.html
``` Hello Footer ```samples/widget.html
``` First Widget Widget (1) content ```samples/snippet.html
``` Testing Snippet Snippet (1) content ```samples/page.html
``` Sample PagePage (1) content
Everything inside this definition will be stripped of in the final generated page Some markup that is outside the definition for the page - won't be included in the final generated page ```assembled page output
``` Final Page Title Widget (1) contentPage (1) content
Widget (1) content Snippet (1) content Hello Footer ``` The first thing that strikes you is how each definition is a full-blown
(natural) HTML page – you can test it statically in your browser without a web-server.
The second thing is how each definition is self-documenting. You
can literally tell how the final generated page will look like
just by reading through the comments.
A third striking thing is how the assembled page is WITHOUT ANY redundancy. There is NOT a single character in that page that was not intended to be in it. It's clean and lean!
Let’s eye-ball each of the definitions a bit more closely now:
Layout.html
To define a file as a layout, there is nothing special to it. It is referenced through the page definition. It can contain the following five exclusive tags in it,
which are populated dynamically:
: place-holder for a<!-- c:put(title) -->
title value which is provided in Page.html
: place-holder for<!-- c:put(styles) -->
style-sheets. These are declared in a page, the snippets and the
widgets.
: place-holder for<!-- c:put(scripts) -->
“head” scripts. These are declared in a page, the snippets and
the widgets.
: place-holder for<!-- c:put(page) -->
the generated page
: place-holder<!-- c:put(bscripts) -->
for “body” scripts. These are declared in a page, the snippets
and the widgets.
The layout page may also contain placeholder for inserting widgets or snippets
Widget.html
To define a file as a widget, you need to wrap the content inside
the tags below:
<!-- c:def(type=”widget”) -->
YOUR_WIDGET_MARKUP
<!-- c:end -->
Other permissible attributes of the widget definition are:
- base: indicates the base directory to load all
style-sheets and scripts declared on the current page, relative
to the page itself. For dynamic web apps, what I recommend is making the value of "servletContext.getContextPath()" available as a variable, say 'root' in your templating code, and then passing this through the 'base' attribute, which would look something close to 'base=@{root}/'
- styles: declares all the style-sheets this page
would require to style itself. If more than one, the file paths
are separated using a semi-colon “;”.These styles will be
inserted in the
section of the layout page,<head>
Unknown end tag for </head>
where there is a corresponding
place-holder.<!--c:put(styles) -->
- scripts: declares all the java-script files this
page would require to manipulate itself before the page renders.
If more than one, the file paths are separated using a
semi-colon “;”.These scripts will be inserted in the
section of the layout page, where there is a<head>
Unknown end tag for </head>
corresponding
place-holder.<!--c:put(scripts) -->
- bscripts: declares all the java-script files
this page would require to manipulate itself after the page
renders. If more than one, the file paths are separated using a
semi-colon “;”.These scripts will be inserted in the
section of the layout page, where<body>
Unknown end tag for </body>
there is a corresponding
place-holder.<!--c:put(bscripts) -->
To insert a widget into a file by resolving from the specified url:
<!-- c:ins(widget(url=file_name)) -->
To insert a widget into a file by resolving from the widgetsContext: (ver 1.1.1)
<!-- c:ins(widget(res=file_name)) -->
Snippet.html
To define a file as a snippet, you need to wrap the content inside
the tags as shown below:
<!-- c:def(type=”snippet”) -->
YOUR_SNIPPET_MARKUP
<!-- c:end -->
Other admissible attributes of the snippet definition are exactly
the same as those of a widget.
To insert a snippet into a file, by resolving from the specified url:
<!-- c:ins(snippet(url=file_name)) -->
There is no concept of a snippets context, unlike widgets
Page.html
To define a file as a page, you need to wrap the content inside the
tags as shown below:
<!-- c:def(type=”page”) -->
YOUR_PAGE_MARKUP
<!-- c:end -->
Other admissible attributes of the page definition are:
- title: this is the page title in the browser. It
will be inserted in the
section of the<head>
Unknown end tag for </head>
layout page where there is a corresponding
place-holder.<!--c:put(title)-->
- layout: this indicates the layout page which
should contain the current page. The entire content of this page
will be inserted in the
section of the<body>
Unknown end tag for </body>
layout page where there is a corresponding
place-holder.<!--c:put(page)-->
The remaining admissible attributes of the page definition are
exactly the same as those of the WIDGET/SNIPPET.
Removable Section
This is not a file like the rest of the definitions. It’s instead a
section of the page which will show up in the browser when viewing
the page statically (without a web-server), but will be stripped off
in the final dynamic page (with a web-server).
A removable section can be declared inside of LAYOUT, WIDGET, PAGE
or SNIPPET. To define a removable section inside any definition
described aobve, you need to wrap the content inside the tags as
shown below:
<!-- c:rem -->
YOUR_REMOVABLE_MARKUP
<!-- c:end -->
And finally...
And that is really it! Whip out your favorite IDE and start
test-driving this solution. Look at the Wiki section for the Integration (http://code.google.com/p/commentit/wiki/Integration) article to start using commentit. Tell me if you don’t love it!
Note (only with version 1.1.1 and below)
You should pay attention to ensure that each definition has
the first 10 characters written out correctly:
Features Updates
Version 1.1 adds two cool features;
- The parser will automatically cache any page file it parses and uses the cached version on subsequent requests. This is the default mode. You can however set the development = true parameter in the servlet context to bypass this behavior
<context-param>
<param-name>development</param-name>
<param-value>[TRUE|FALSE]</param-value>
</context-param>
- The parser can now load up files outside of the web app folder. To do this you would have to add a parameter named "fs_templates_dir" in the servlet context and have that point to your desired location.
<context-param>
<param-name>fs_templates_dir</param-name>
<param-value>[YOUR LOCATION]</param-value>
</context-param>
- Version 1.1.1 add the ability to resolve widgets from a widgetsContext. The widgetsContext is simply a map of database records. It is able to map a specified key to url location, as stores in the database. This adds enormous potential to a widget, and it enalbes a widget to be reusable across different project
public TemplateParser(ServletContext servletContext, Map<String, String> widgetsContext) {
this(servletContext);
this.widgetsContext = widgetsContext;
}
- Version 1.1.2 is a complete re-write, based on improvements borrowed from a python POC for the same library. Look at the new features update here
Stay tuned for more feature updates...