2A 27 2C 197 4ACD
|
Unsuitable on Unsuitable: Rendering Articles
Samuel A. Falvo II
kc5tja -at- arrl.net 2010 Aug 07 13:26 PDT
Viewing articles on most blogs implies receiving a plurality of advertisements and other distractions as well. This reduces both the reader's attention span for the material presented, and valuable screen real-estate. Unsuitable renders an article with a minimum of distraction. With more room on the screen for content, I can provide a nicer layout that makes reading easier for the consumer. 1 IntroductionTake a careful look at the screenshot1 below, and see if you can find all of the things wrong with it.
Which of the following things do you consider sins against all mankind?
I concede some lack of fairness on my part; readers should not take Technorati as a representative of all blogs. However, I argue the majority of blogs attempting to derive ad-based revenue, particularly as their sole source of income, will possess one or more of the aforelisted sins against their customers. While you can configure Unsuitable for this kind of Alice in Wonderland presentation with some effort, for Unsuitable lets you configure the visual appearance via its theme directory and various HTML-generating macros, I deliberately chose to keep its out-of-the-box presentation utterly simple and to the point. I abhore online distractions. Below, I detail both the page rendering logic, which should remain (mostly) unchanged regardless of the page layout, and the individual macros used to provide the default theme. 2 Code Walk-ThroughRegrettably, the walk-through which follows will not follow the definitions in source listing order. I observed in a previous article, yet failed to highlight at that time, the importance of definition order in successfully defining macros so that they will not interfere with HTML response generation at run-time2. For both index and RSS generation modules, so few macros existed that I considered the order of definition a non-issue. With m-articles.fs, however, large numbers of macro-related definitions separates the two halves of the page rendering logic. Readers may find this distracting, so I've decided to elide or re-arrange chunks of code to help illustrate how the pieces fit together. 2.1 Page Rendering Logicm-articles.fs accepts an article identifier parameter on the URL path. If the user insists on omitting this parameter, we present the user with the blog's index page. end-of-url ¶meters - constant /parameters : oops s" m-index.fs" included bye ; : |parameters|>=2 /parameters 2 u< if oops then ; |parameters|>=2 We calculate the address of the first byte of the parameter in the URL string. ¶meters 1+ constant &id We expect an integer parameter; anything else drops us back to the index page of the blog. : -eon dup end-of-url u>= if drop scan valid then ; : -0-9 dup [char] 0 < swap [char] 9 > or ; : *10+ dup c@ [char] 0 - id @ 10 * + id ! ; : digit dup c@ -0-9 if drop oops then *10+ ; : numeric &id begin -eon digit char+ again ; numeric Once we know we have a numeric parameter, we need to perform a full table scan to locate the corresponding article information needed to render the page. The variable id already contains the desired article number. : n! articleId n @ < n @ -1 = or if articleId n ! then ; : nxt articleId id @ > if n! then ; : prv articleId id @ < articleId p @ > and if articleId p ! then ; : exists nxt prv ; : row dup articleIds - article! articleId -1 xor if exists then ; : -all [ articleIds /afields + ] literal u< ; : scan articleIds begin dup -all while row cell+ repeat drop ; I apologize if the above code confuses you; it was written before I fully realized the DItI pattern. I'll attempt to summarize what's happening. scan performs a full-table scan, looking not only for the current article ID, but also its immediate predecessor and successor as well. See On Determining an Article's Next and Previous Links: A Quick Thought for a mathematical definition of predecessor and successor in the context of Unsuitable. When scan completes, three variables, later used with macro expansions, will refer to relevant articles. Note: none of the variables listed below refer to row offsets in the articles table. All variables hold identifiers. For example, if you use article ID 1035 in the URL, id will equal 1035, p 1034, and n 1036.
Once we know as much as we need to know about the article requested, we declare our state valid, which invokes HTML response generation. variable s variable end : mime ." Content-type: text/html" cr cr ; : valid mime here s ! s" theme/article.html" slurp here end ! s" response.fs" included bye ; 2.2 MacrosUnsuitable renders individual articles with a relatively large set of macros. Macro selection and desired page layout influence each other; therefore, many macros listed below exist primarily to facilitate the desired page layout. If the layout were to change in the future, I will replace legacy macros with macros relevant to the new layout.
To see how these macros work in context, see section 4, the listing for theme/article.html. The ~pred and ~succ macros affect whether other rendering macros operate on the current article's predecessor or successor. which maintains this state by pointing either p or n, depending on whether ~pred or ~succ last executed, respectively. variable which : succ n which ! ; : pred p which ! ; The ~span, ~/span, and ~label macros all depend on the state of which, and whether or not the appropriate article exists. Unsuitable employs a two-dimensional jump matrix which maps the macros to the desired behaviors. create +/- ' +span , ' +/span , ' +label , 0 , ' -span , ' -/span , ' -label , 0 , : go which @ @ -1 = 4 cells and + @ execute ; : span [ +/- ] literal go ; : /span [ +/- cell+ ] literal go ; : label [ +/- 2 cells + ] literal go ;
At the time I wrote this code, I originally used four macros to render the Each macro may vector to an article-exists variant (+x) or to an article-absent variant (-x). : -span s\" <span style=\"color: #ddd;\">" respond ; : -/span s" </span>" respond ; : -label s" No article written yet." respond ; : url safely s" http://www.falvotech.com/blog2/blog.fs/articles/" respond articleId s>d <# #s #> respond ; : +span s\" <a href=\"" respond url s\" \">" respond ; : +/span s" </a>" respond ; : +label safely title gob! get, ; I introduce a word safely, extending Forth's control flow words such that I may execute arbitrary Forth code in the context of either the successor or predecessor article, without visibly affecting the current article selection from the user's standpoint. safely preserves relevant state for us, enabling us to continue to work with the user's article selection later on. : invoke >r ; : safely r> article >r which @ @ articleWithId! invoke r> article! ; The ~timestamp macro prints the article's publication time. : timestamp >web id @ articleWithId! timestamp .time >con ; The remaining three macros wrap the getters found in the articles.fs module by augmenting each of them with the ability to dump their contents to the HTML response. : title ['] title .f ; : lead ['] lead .f ; : body ['] body .f ; If a field comes up missing, however, we want to ensure no output occurs. : exists: dup -1 = if r> 2drop then ; : .f id @ articleWithId! execute exists: gob! get, ; 3 What's NextWell, this article concludes the detailed walk-through of Unsuitable's program code. I hope you have enjoyed reading about how Unsuitable works as much as I've enjoyed writing these articles. Some readers have e-mailed me with some suggestions to make the blog more usable for them. As I complete work on the feature requests submitted, I'll post articles detailing the changes I make to Unsuitable to accomodate each feature. I have other Forth projects in the works which I'll talk about here in the future, as well. And, if you're good boys and girls, I just might have another Over The Shoulder video for you too. Stay tuned. 4 Source Listing for theme/article.html<html>
<head>
<title>
Sam's All New, New and Improved, New News news news
</title>
<link rel="stylesheet" href="/blog2/theme/css.css" />
</head>
<body>
<div class="blogHead">
Falvotech.
</div>
<div class="blogSubhead">
Conquering the world is easy — what do you do with it afterwards?
</div>
<hr />
<table width="100%">
<tr>
<td width="15%" valign="top" align="left">
<div>
<p align="center">L I N K S</p>
<hr />
<p>~pred ~span ⇐ ~label ~/span </p>
<p>~succ ~span ⇒ ~label ~/span </p>
<hr />
</div>
</td>
<td width="85%" valign="top" align="left">
<div class="blogArticleTitle">
~title
</div>
<div class="blogArticleTimestampAuthor">
<div class="blogArticleAuthor">Samuel A. Falvo II<br />kc5tja -at- arrl.net</div>
<div class="blogArticleTimestamp">~timestamp </div>
</div>
<div class="blogArticleLead">
~lead
</div>
<div class="blogArticleBody">
~body
</div>
</td>
</tr>
</table>
</body>
</html>
5 Source Listing for m-articles.fsrequire mappings.fs require general.fs require articles.fs require slurp.fs require time.fs require respond.fs end-of-url ¶meters - constant /parameters : oops s" m-index.fs" included bye ; : |parameters|>=2 /parameters 2 u< if oops then ; |parameters|>=2 variable id 0 id ! variable p -1 p ! variable n -1 n ! variable which : -span s\" <span style=\"color: #ddd;\">" respond ; : -/span s" </span>" respond ; : -label s" No article written yet." respond ; : invoke >r ; : safely r> article >r which @ @ articleWithId! invoke r> article! ; : url safely s" http://www.falvotech.com/blog2/blog.fs/articles/" respond articleId s>d <# #s #> respond ; : +span s\" <a href=\"" respond url s\" \">" respond ; : +/span s" </a>" respond ; : +label safely title gob! get, ; create +/- ' +span , ' +/span , ' +label , 0 , ' -span , ' -/span , ' -label , 0 , : go which @ @ -1 = 4 cells and + @ execute ; : span [ +/- ] literal go ; : /span [ +/- cell+ ] literal go ; : label [ +/- 2 cells + ] literal go ; : succ n which ! ; : pred p which ! ; variable s variable end : mime ." Content-type: text/html" cr cr ; : valid mime here s ! s" theme/article.html" slurp here end ! s" response.fs" included bye ; : exists: dup -1 = if r> 2drop then ; : .f id @ articleWithId! execute exists: gob! get, ; : title ['] title .f ; : lead ['] lead .f ; : body ['] body .f ; : timestamp >web id @ articleWithId! timestamp .time >con ; : n! articleId n @ < n @ -1 = or if articleId n ! then ; : nxt articleId id @ > if n! then ; : prv articleId id @ < articleId p @ > and if articleId p ! then ; : exists nxt prv ; : row dup articleIds - article! articleId -1 xor if exists then ; : -all [ articleIds /afields + ] literal u< ; : scan articleIds begin dup -all while row cell+ repeat drop ; ¶meters 1+ constant &id : -eon dup end-of-url u>= if drop scan valid then ; : -0-9 dup [char] 0 < swap [char] 9 > or ; : *10+ dup c@ [char] 0 - id @ 10 * + id ! ; : digit dup c@ -0-9 if drop oops then *10+ ; : numeric &id begin -eon digit char+ again ; numeric 1 Screenshot captured on 2010 Aug 7, on my home computer running GoboLinux 014rc2 and Firefox 3.6. 2 Defining macros in another module and including them midway through the m-articles.fs source listing would ameliorate this problem greatly. Of course, one only thinks of these kinds of things when retrospecting on one's work. |