Mason Components for AJAX
David P. Bushong
AvantGo/iAnywhere, Inc.
Overview
- Know Your Tools
- Don't Repeat Yourself
- Survive JavaScriptlessness
- Get Organized
- Be Secure
What is Mason?
Mason is a Perl app-server
You can embed Perl code in your HTML and build OO-style components which
inherit and extend functional components of your site.
Mason: Embed Perl in HTML
<html><body>
What do you get when you multiply six by nine?
<br/>
<% 6 * 7 %>
</body></html>
Mason: Calling Components
From inside HTML:
<& some-comp, arg1 => 'value', arg2 => 'value' &>
From inside a Perl block:
$m->comp('some-comp', arg1 => 'value', arg2 => 'value'
On a line by itself:
<ul>
% for my $row (@rows) {
<li><% $row->{name} |h %></li>
% }
</ul>
Mason JavaScript Escaping
Mason makes HTML and URI encoding easy with
|h and
|u;
make it equally easy for JavaScript with
|j:
$m->interp->set_escape(j => sub {
my $str = shift;
$str =~ s/\\/\\\\/g;
$str =~ s/'/\\'/g;
$str =~ s/\n/\\n/g;
$str
});
What is AJAX?
Asynchronous JavaScript And XML
-
Basically, making a call to XMLHttpRequest() from the JavaScript on
an already-loaded page so as to send, receive (or both) information, usually
resulting in an update of some part of the page without reloading the whole
page.
-
Data is sent using XMLHttpRequest() via GET or POST, usually URI
encoded.
AJAX 101
First and foremost, don't reinvent the wheel: use prototype.js or a
similar JavaScript framework
- new Ajax.Request(url, { opts });
- { key: 'value', key2: 'value2' } syntax
- function () { ... } syntax
- $('id_of_element')
Receiving AJAX Data
Data can be received and used a number of ways:
Receiving JSON
JavaScript Over Network (JSON)
- e.g.: { foo: [1, 2, 3], bar: 'hello' }
- Great for getting back a data structure you can easily access
Receiving XML
XML (the X in AJAX)
- e.g. <results><foo>42</foo><bar id="42"
/></results>
- Great for getting back a data tree you can (fairly) easily navigate.
- Easier to navigate with XPath extensions showing up in some browsers
- Only option for back-end server you don't control (tie-in to REST Services)
Can be transformed with XSLT to produce HTML snippets.
Receiving HTML
An HTML snippet to insert/replace into page using innerHTML
- e.g. <div><ul><li>foo</li><li>bar</li></ul></div>
- Saves a lot of DOM coding to build new HTML chunks
- Easiest for appending/replacing inline
- Since server produces all HTML, avoids duplicated functionality
Overview
- Know Your Tools
- Don't Repeat Yourself
- Survive JavaScriptlessness
- Get Organized
- Be Secure
Don't Repeat Yourself
Avoiding duplication of code is a very, very good thing (D.R.Y.)
There are many tools and techniques available to avoid duplication in web
development:
- Use CSS: code your style in one place, reuse it all over. Even within your
CSS stylesheets you can avoid duplication through the use of grouped class/id
assignments of shared style elements
- Put data-related functions used in more than one place in Perl modules
- Put reused HTML snippets into Mason components. Even when there's no
reuse, this isn't a bad idea to split up the design a bit
Reusing Existing Code
-
Since you can detect an XMLHTTPRequest on the server side, you may be able to
reuse your existing action receiver, depending on how it is designed.
-
The same component you use to render a part of the page in full-page
rendering mode can be used to render a newly created HTML chunk you're
inserting with .innerHTML
-
And since we're reusing existing code wherever possible, that'll make it easy
to support JavaScriptless environments:
Overview
- Know Your Tools
- Don't Repeat Yourself
- Survive JavaScriptlessness
- Get Organized
- Be Secure
Survive JavaScriptlessness
Modern websites should perform gracefully without JavaScript
- Important for accessibility
- Makes them more scriptable, which makes testing easier
- Provides broader browser support
- Lets clients fall back to basic functionality should your JavaScript
explode for some reason (gasp!)
HTML for !JavaScript
- Provide real href and action attributes for AJAXified forms and links
- Return false in the onclick and onsubmit handlers.
- If the JavaScript doesn't or can't run, the "old-fashioned" behavior will
run.
Perl for !JavaScript
-
Trap AJAX-specific request parameters or headers, and provide minimally
different behavior in an existing/shared action handler to process the
results.
-
Often the AJAX-specific behavior is as simple as not redirecting back to
the previous page or emitting the newly minted HTML chunk
Overview
- Know Your Tools
- Don't Repeat Yourself
- Survive JavaScriptlessness
- Get Organized
- Be Secure
Cliché Example
- We're building... a blog!
- You can post a new entry and delete an old one
- Since we don't want to repeat ourselves, let's make sure the JavaScript
and the HTML aren't re-rendering the same page snippets
Layout Overview
Directory layout:
action/ -- HTTPable pages with no data (AJAX or 302)
autohandler -- turns off header/footer
delete-post -- deletes the given post id
edit-post -- updates (currently just creates) a post
comp/ -- non-HTTPable HTML snippets
post -- a complete form or display post
field -- a field for such a post
func/ -- non-HTTPable Mason code
setup -- shared setup code for all pages in the app
includes/ -- browser includes (JS & CSS)
masonblog.css -- stylesheet for app
masonblog.js -- custom js for app
prototype.js -- Prototype library
scriptaculous/ -- Scriptaculous libraries
autohandler -- func/setup + header/footer
index.html -- index page (currently only "real" page!)
HTML Subsection Components
MasonBlog™ will have two non-request HTML components:
- HTML snippet for an individual post
- HTML snippet for an individual field of a post
Field HTML Component
[comp/field]
- Creates a text input or displays the text; starting point for more general
set of HTML generation components
Post HTML Component
[comp/post]
- "Create Form" and "View" will share same component
- Not necessarily (or even often) the best approach
- Balance of reuse vs. spaghetti code
- Uses supplied parameters for efficiency if available
Action Components
- delete-post:
Accepts a post id, and deletes it (no authentication!)
- edit-post:
Creates or edits a post entry given author and content.
In AJAX mode, sends back the HTML of the new post entry from
the post component
Alternative Approach
- A Mason component with a <%args> section (like post) can be
transparently accessed using <& ... &> tags or
via a direct HTTP request
- This means that the component that you used as part of a larger page to
render the HTML could be called directly via XMLHTTPRequest to
render a snippet of HTML for presentation.
- In practice, not all that useful
Overview
- Know Your Tools
- Don't Repeat Yourself
- Survive JavaScriptlessness
- Get Organized
- Be Secure
Be Secure
- Much like private, public, and protected in
your favorite B&D language, your web app should have correct permissions
- autohandler and dhandler should not be web-fetchable
- The contents of CVS/ and .svn/ directories should not
be web-fetchable
- Mason components (HTML or code) that are only called from other Mason
components should not be web-fetchable (in our case, func/
and comp/)
Overview
- Know Your Tools
- Don't Repeat Yourself
- Survive JavaScriptlessness
- Get Organized
- Be Secure
- Don't Repeat Yourself