<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/">
	<channel>
		<title>Codestore Activity Log</title>
		<description>Latest ten updates to codestore, be they blogs or articles.</description>
		<language>en-gb</language>
		<link>http://www.codestore.net</link>
		<lastBuildDate>Thu, 2 Jul 2009 03:36:44 -0500</lastBuildDate>
		<atom:link href="http://www.codestore.net/store.nsf/rss.xml" rel="self" type="application/rss+xml" />

		<item>
			<title>More Haste, Jake, More Haste | Blog</title>
			<pubDate>Thu, 2 Jul 2009 03:36:44 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>Last night, like an idiot, after a couple of glasses of wine I completely forgot about <a href="http://vowe.net/archives/010621.html">what I'd read</a> on Volker's site and upgraded to Firefox 3.5. This broke Gears. This on the eve of moving in to the testing phase of the Gears project I've been working on for about a month now. </p> <p>While I could have used IE or Chrome instead, Firefox is the only one for which Gears <em>and</em> <a href="http://www.httpwatch.com">HTTPWatch</a> work together and you really need a HTTP sniffer if you're debugging Gears apps!</p> <p>Luckily, this morning I managed to downgrade back to 3.0.11, which is a simple case of downloading and running the installer. You don't even need to uninstall 3.5. That's one of the things I've always liked about Firefox - it never loses your settings.</p> <h4>Will Firefox 3.5 Even Need Gears</h4> <p>One thing I did get out of my brief look at FF3.5 is the (exciting) knowledge that it's moving towards implementing its own kind of Gears. I've not digested it all fully but if you read the <a href="https://developer.mozilla.org/en/DOM/Storage">DOM Storage</a> page on Mozilla's site and the <a href="http://dev.w3.org/html5/webstorage/">W3C's Web Storage page</a> you'll see what I mean. It looks to me like browsers of the future will all have <a href="http://dev.w3.org/html5/webstorage/#databases">local databases</a> to use for persistent data across sessions. Hopefully without the need for 3rd party plugins.</p> <h4>More To It Than "Going Offline"</h4> <p>While using Gears this last month I've been thinking more and more about what other uses it could have, apart from "going offline". </p> <p>All you have to do is imagine you have a local SQL database that is easily accessed from JavaScript and you'll get an idea what you can do. It's like having a massive cookie to use for whatever you like. One example would be to keep a cache of values which are regularly used in forms/type-ahead by the user but which rarely change at the server. Why keep fetching them with Ajax when you could get them in an instant locally! </p> <p>Hopefully I'll get chance to show some alternative usages of Gears shortly.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20090702?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20090702</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20090702</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20090702?Open#comments</comments>
			<slash:comments>5</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20090702</wfw:commentRss>
		</item>
		<item>
			<title>Life With Windows Mobile 6 and HTP Snap (stinks) | Blog</title>
			<pubDate>Wed, 1 Jul 2009 03:10:22 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>Having spent a couple of weeks with <a href="http://www.codestore.net/store.nsf/unid/BLOG-20090611?OpenDocument">my HTC Snap</a> and Windows Mobile 6 I'm starting to regret my choice of phone.</p> <p>There's nothing wrong with the phone itself, per se. I love the look, feel, size, usability etc. It's just the software on it that sucks. </p> <p>Yesterday I was trimming my finger nails and was conscious of not going <em>too</em> far back on the thumb as I need it on a daily basis to remove the battery cover to do a hard restart!</p> <p>It seems to crash at least twice a day. Seemingly for no reason at all. Although it has been a bit better since I stopped using HTC's customised home screen, which I prefer to what I have now, so that's a pain too, but there you go. </p> <p>It even managed to lose all my email and text messages on one occasion. Not a big deal, as I had copies of the emails in Thunderbird too, but the fact it has the potential to just forget all email account settings and delete the messages is somewhat worrying.</p> <p>I've tried using Windows Update to get any "service packs" there might be but that too fails. Something about the date and time being wrong on the device. Great.</p> <h4>Who To Blame?</h4> <p>Things that don't work really get on my nerves. I don't mind (and expect) the odd bug in software but when I have to mess about removing the battery cover and waiting for it to startup on such a regular basis it gets a bit daft.</p> <p>If this were any other piece of electronic equipment that I'd spent +?ú200 on (camera, DVD player, whatever) then it would be straight back to the shop and I'd be demanding my money back. So, why do I feel reluctant to even try and return the Snap? As if whoever I speak to is just going to say, derr, it's Windows, what did you expect!?</p> <p>Who should I contact -- the seller, HTC or Microsoft? I guess I should start with <a href="http://www.devicewire.co.uk/">the seller</a>? I think I'll start with them and see how far I get.</p> <h4>No Way Back</h4> <p>Having used a smartphone for a couple of weeks I can now see that I need one and don't want to go back to not having one. I just think that Windows Mobile probably isn't the way forward. Perhaps <a href="http://www.htc.com/uk/product/hero/overview.html">Android is</a>...</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20090701?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20090701</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20090701</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20090701?Open#comments</comments>
			<slash:comments>22</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20090701</wfw:commentRss>
		</item>
		<item>
			<title>When Hard Coding is Ok - My Very Own Year 2100 Bug | Blog</title>
			<pubDate>Mon, 29 Jun 2009 03:46:41 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>As part of my current job of porting an application to Google Gears I wrote a snippet of JavaScript to validate that a user enters a date in the format ddmmyyyy or ddmmyy. The users are using PDAs so we <em>really</em> don't want them to have to do it in the format dd/mm/yyyy. The less key taps needed the better.</p> <p>Here's the code:</p><pre><span class="TPkeyword2">var </span>re <span class="TPoperator">= /^</span><span class="TPbracket">(</span>0<span class="TPbracket">&#91;</span><span class="TPnumber">1</span><span class="TPoperator">-</span><span class="TPnumber">9</span><span class="TPbracket">&#93;</span><span class="TPoperator">|</span><span class="TPbracket">&#91;</span><span class="TPnumber">12</span><span class="TPbracket">&#93;&#91;</span><span class="TPnumber">0</span><span class="TPoperator">-</span><span class="TPnumber">9</span><span class="TPbracket">&#93;</span><span class="TPoperator">|</span>3<span class="TPbracket">&#91;</span><span class="TPnumber">01</span><span class="TPbracket">&#93;)(</span>0<span class="TPbracket">&#91;</span><span class="TPnumber">1</span><span class="TPoperator">-</span><span class="TPnumber">9</span><span class="TPbracket">&#93;</span><span class="TPoperator">|</span>1<span class="TPbracket">&#91;</span><span class="TPnumber">012</span><span class="TPbracket">&#93;)((</span><span class="TPnumber">19</span><span class="TPoperator">|</span><span class="TPnumber">20</span><span class="TPoperator">|</span><span class="TPbracket">)</span>\d\d<span class="TPbracket">)</span>$<span class="TPoperator">/</span>;

<span class="TPcomment">//Test format first</span>
<span class="TPkeyword2">if </span><span class="TPbracket">(</span>re.test<span class="TPbracket">(</span><span class="TPkeyword5">input</span><span class="TPbracket">)) {</span>

 <span class="TPkeyword2">var </span>bits <span class="TPoperator">= </span><span class="TPkeyword5">input</span>.<span class="TPkeyword5">match</span><span class="TPbracket">(</span>re<span class="TPbracket">)</span>; 
 <span class="TPkeyword2">if </span><span class="TPbracket">(</span>bits<span class="TPbracket">&#91;</span><span class="TPnumber">4</span><span class="TPbracket">&#93;</span><span class="TPoperator">==</span><span class="TPstring">""</span><span class="TPbracket">){</span>
  bits<span class="TPbracket">&#91;</span><span class="TPnumber">3</span><span class="TPbracket">&#93;</span><span class="TPoperator">=</span><span class="TPstring">"20"</span><span class="TPoperator">+</span>bits<span class="TPbracket">&#91;</span><span class="TPnumber">3</span><span class="TPbracket">&#93;</span>; <span class="TPcomment">//Will last 91 years before breaking!</span>
 <span class="TPbracket">}</span>
 
 <span class="TPkeyword2">var </span>entered <span class="TPoperator">= </span><span class="TPkeyword2">new Date</span><span class="TPbracket">()</span>; 
 
 entered.<span class="TPkeyword5">setFullYear</span><span class="TPbracket">(</span>bits<span class="TPbracket">&#91;</span><span class="TPnumber">3</span><span class="TPbracket">&#93;</span>, bits<span class="TPbracket">&#91;</span><span class="TPnumber">2</span><span class="TPbracket">&#93;</span><span class="TPoperator">-</span><span class="TPnumber">1</span>, bits<span class="TPbracket">&#91;</span><span class="TPnumber">1</span><span class="TPbracket">&#93;)</span>;

 <span class="TPcomment">//Validate range here if needs be</span>
                                                        
<span class="TPbracket">} </span><span class="TPkeyword2">else </span><span class="TPbracket">{</span>
 <span class="TPkeyword5">alert</span><span class="TPbracket">(</span><span class="TPstring">"Not a valid date!"</span><span class="TPbracket">)</span>;
<span class="TPbracket">}</span>
</pre>
<p>The end result is a JavaScript Date object representing the date entered, which you can do with as you must. 
<h4>When Hard Coding <em>Is</em> Ok</h4>
<p>What keeps making me think about the code is the bit where I've hard coded the 21<sup>st</sup> century in to any date where the century part isn't specified. In 91 years it will break. As an opponent of all things hard coding I can't decide how happy I am with it. 
<p>Although I know the application itself will, like me, be long dead by the year 2100 it's just bugging me. Is it ever ok to knowingly introduce a bug, even if you know the bug will never get to live? 
<h4>A Simple Fix</h4>
<p>In this case it would be trivial to remove the bug, so that it always added the current century when none was specified: <pre>bits<span class="TPbracket">&#91;</span><span class="TPnumber">3</span><span class="TPbracket">]</span><span class="TPoperator">= </span><span class="TPkeyword2">new Date</span><span class="TPbracket">()</span>.<span class="TPkeyword5">getFullYear</span><span class="TPbracket">()</span>.<span class="TPkeyword5">substr</span><span class="TPbracket">(</span><span class="TPnumber">0</span>,<span class="TPnumber">2</span><span class="TPbracket">) </span><span class="TPoperator">+ </span>bits<span class="TPbracket">&#91;</span><span class="TPnumber">3</span><span class="TPbracket">]</span>;
</pre>
<p>I just can't decide whether I should retrofit this fix or not. Maybe I should. Then, should a source of eternal life be found by then, I'll be guilt-free.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20090629?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20090629</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20090629</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20090629?Open#comments</comments>
			<slash:comments>18</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20090629</wfw:commentRss>
		</item>
		<item>
			<title>Friday Fun Challenge | Blog</title>
			<pubDate>Fri, 26 Jun 2009 03:10:37 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>On the topic of web-form spam and the ever-evolving ways in which I deal with it, I made a change to Rockalldesign.com's Contact Us page so that it sends me a daily digest of the entries made. It gets used so little for the purpose it was intended that it's safe to check once a day if a <em>real</em> person has used it. Most won't mind waiting 24 hours for a reply, I'm sure.</p> <p>Anyway, here are the email addresses of the "people" who posted on Rockall's contact "us" page over the last 24 hours. Rather kindly, I always think, the spammer make it pretty obvious it's spam. </p> <h4>The Challenge</h4> <p>Your challenge is to make a short story that includes all of the words used below and in the order they appear!</p><pre>present@rain.com
system@claim.com
shine@sheet.com
five@such.com
milk@tail.com
ground@character.com
fruit@safe.com
island@search.com
difficult@during.com
problem@felt.com
carry@size.com
pick@warm.com
while@wind.com
believe@back.com
bell@difficult.com
decimal@invent.com
since@state.com</pre>
<p>Extra point for anybody who can make it topical and about <a href="http://www.bbc.co.uk/go/homepage/int/pr/michael_jackson_dead_1/h1/-/news/1/hi/entertainment/8119993.stm">Michael Jackson</a> (the word "five" is in there, how hard can it be!?). The less extra words you use to pad out those above the better.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20090626?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20090626</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20090626</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20090626?Open#comments</comments>
			<slash:comments>8</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20090626</wfw:commentRss>
		</item>
		<item>
			<title>Ten Years On And Still Learning -- Notes Agent Data Stores | Blog</title>
			<pubDate>Thu, 25 Jun 2009 03:36:37 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>Rob Shaver replied to yesterday's post about <a href="http://www.codestore.net/store.nsf/unid/BLOG-20090624">using the LastRun property of Scheduled Notes Agents</a> with an another, even better, alternative.</p> <p>It turns out that Scheduled Agents have their own document/storage area that you can add to and read from. I never knew that!</p> <p>You get to it via the SavedData property of the Session object (in LotusScript). See <a href="/help/help85_designer.nsf/0/5d50ccaae77d8d74852574cf006b94c0?OpenDocument">help page</a> on it for more.</p> <p>With a bit of tweaking I re-wrote the code I mentioned yesterday to more accurately find the documents created since it last ran, which is posted below.</p> <p>This gets round the fact that the agent's LastRun property is the time it last <em>finished. </em>Any documents created while it ran won't ever get processed. Using the agents "saved data" area we can add a more reliable time to state when it <em>began</em>. </p> <p>&nbsp;</p><pre><span class="TPkeyword1">Set </span>thisAgent <span class="TPoperator">= </span>Session.currentAgent
<span class="TPkeyword1">Set </span>agentDoc <span class="TPoperator">= </span>Session.SavedData
        
<span class="TPkeyword1">Dim </span>query <span class="TPkeyword1">As String</span>, datepart <span class="TPkeyword1">As String</span>
        
<span class="TPcomment">'Default date string</span>
datepart <span class="TPoperator">= </span><span class="TPstring">"@Adjust(["</span><span class="TPoperator">+</span><span class="TPkeyword1">Cstr</span><span class="TPbracket">(</span>thisAgent.LastRun<span class="TPbracket">) </span><span class="TPoperator">+</span><span class="TPstring">"];0;0;0;0;0;-30)"</span>
        
<span class="TPkeyword1">If </span>thisAgent.HasRunSinceModified <span class="TPkeyword1">Then</span>
 <span class="TPkeyword1">If Not </span>agentDoc <span class="TPkeyword1">Is </span><span class="TPkeyword2">Nothing </span><span class="TPkeyword1">Then</span>
  <span class="TPkeyword1">If  </span>agentDoc.HasItem<span class="TPbracket">(</span><span class="TPstring">"LastBeganAt"</span><span class="TPbracket">) </span><span class="TPkeyword1">Then</span>
   datepart <span class="TPoperator">= </span><span class="TPstring">"["</span><span class="TPoperator">+</span><span class="TPkeyword1">Cstr</span><span class="TPbracket">(</span>agentDoc.LastBeganAt<span class="TPbracket">(</span><span class="TPnumber">0</span><span class="TPbracket">))</span><span class="TPoperator">+</span><span class="TPstring">"]"</span>
  <span class="TPkeyword1">End If</span>
 <span class="TPkeyword1">End If</span>
<span class="TPkeyword1">Else</span>
 datepart <span class="TPoperator">= </span><span class="TPstring">"@Adjust(@Now; 0; 0; -1; 0; 0; 0)"  </span>
<span class="TPkeyword1">End If</span>
        
query <span class="TPoperator">= </span><span class="TPbracket">{</span>Form<span class="TPoperator">=</span><span class="TPstring">"post" </span>&amp; @Created<span class="TPoperator">&gt;=</span><span class="TPbracket">}</span><span class="TPoperator">+</span>datepart<span class="TPoperator">+</span><span class="TPbracket">{ </span>&amp; Approved<span class="TPoperator">=</span><span class="TPstring">"0"</span><span class="TPbracket">}</span>
        
agentDoc.LastBeganAt <span class="TPoperator">= </span><span class="TPkeyword1">Now </span><span class="TPcomment">'Set this after query is built!</span>
        
<span class="TPkeyword1">Set </span>collection <span class="TPoperator">= </span>db.Search<span class="TPbracket">(</span>query, <span class="TPkeyword2">Nothing</span>, <span class="TPnumber">0</span><span class="TPbracket">)</span>

<span class="TPcomment">'do stuff here</span>

agentDoc.Save<span class="TPbracket">(</span><span class="TPkeyword2">True</span>, <span class="TPkeyword2">True</span><span class="TPbracket">)</span></pre>
<h4>Forever Learning</h4>
<p>How long have I been using Notes now!? Must be getting on for, if not more than, ten years. How this nugget passed my by I don't know. Going back to <a href="http://www.codestore.net/store.nsf/unid/BLOG-20090622">Monday's whine</a> this is an example of one of the things I do love about this site. Without it I wouldn't know half what I do. The learning process is two-way! Thanks Rob.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20090625?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20090625</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20090625</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20090625?Open#comments</comments>
			<slash:comments>10</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20090625</wfw:commentRss>
		</item>
		<item>
			<title>Documents Created Since Agent Last Ran - An Alternative | Blog</title>
			<pubDate>Wed, 24 Jun 2009 03:27:39 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>When using Notes scheduled agents we have the ability to easily find all the documents in the database that were created and/or modified since the Agent last ran.</p> <p>&nbsp;<img title="ScreenShot003" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; margin: 10px 0px; border-right-width: 0px" height="125" alt="ScreenShot003" src="http://www.codestore.net/store.nsf/rsrc/C159BAFCCCD30E69862575DF002E79D2/$file/ScreenShot003_2566cdba-1ff9-46f1-960e-ffc590b6161d.gif" width="312" border="0"> </p> <p>Inside the code we then just access a NotesDocumentCollection of all the new documents, like so:</p><pre><span class="TPkeyword1">Dim </span>collection <span class="TPkeyword1">as </span><span class="TPkeyword3">NotesDocumentCollection</span>
<span class="TPkeyword1">Set </span>collection <span class="TPoperator">= </span>db.UnprocessedDocuments
</pre>
<p>All is well, until you want to restrict the documents you process. To do this the <em>Notes Way</em> you add a Document Selection criteria to the Agent, like so:</p>
<p><img title="ScreenShot004" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; margin: 10px 0px; border-right-width: 0px" height="146" alt="ScreenShot004" src="http://www.codestore.net/store.nsf/rsrc/1BB1B8AFED0E312E862575DF002E7A1A/$file/ScreenShot004_a027841c-68e4-4178-a75f-9d83dc9e059d.gif" width="522" border="0"> </p>
<p>&nbsp;</p>
<p>So now the collection of documents will be restricted to those built using the Form "post" and those where the field called Approved has a value of "0".</p>
<p>At least that's the way I've always assumed it to work. I always have trouble getting it to work as I'd expect it to though. Recently I cooked-up an alternative that I think is much cleaner and less error-prone. </p>
<h4>The Alternative</h4>
<p>A newby to Notes looking at the code above might wonder how on earth it works. It's not unless you know about (and notice) the Document Selection part that you'll see how the document selection is being made. </p>
<p>Another, much cleaner (in that somebody reading your code can see what's going on directly from the script), method is to build the document collection ourselves, based on the time the agent last ran.</p>
<p>Here's how:</p><pre><span class="TPkeyword1">Set </span>thisAgent <span class="TPoperator">= </span>Session.currentAgent
        
<span class="TPkeyword1">If </span>thisAgent.HasRunSinceModified <span class="TPkeyword1">Then </span>
 query <span class="TPoperator">= </span><span class="TPbracket">{</span>Form<span class="TPoperator">=</span><span class="TPstring">"post" </span>&amp; @Created<span class="TPoperator">&gt;=</span><span class="TPbracket">[}</span><span class="TPoperator">+</span><span class="TPkeyword1">Cstr</span><span class="TPbracket">(</span>thisAgent.LastRun<span class="TPbracket">) </span><span class="TPoperator">+</span><span class="TPbracket">{] </span>
  &amp; Approved<span class="TPoperator">=</span><span class="TPstring">"0"</span><span class="TPbracket">}</span>
<span class="TPkeyword1">Else </span>
 query <span class="TPoperator">= </span><span class="TPbracket">{</span>Form<span class="TPoperator">=</span><span class="TPstring">"post" </span>&amp; @Created<span class="TPoperator">&gt;=</span>@Adjust<span class="TPbracket">(</span>@<span class="TPkeyword1">Now</span>; <span class="TPnumber">0</span>; <span class="TPnumber">0</span>; <span class="TPoperator">-</span><span class="TPnumber">1</span>; <span class="TPnumber">0</span>; <span class="TPnumber">0</span>; <span class="TPnumber">0</span><span class="TPbracket">) </span>
  &amp; Approved<span class="TPoperator">=</span><span class="TPstring">"0"</span><span class="TPbracket">} </span>
<span class="TPkeyword1">End If</span>
 
<span class="TPkeyword1">Set </span>collection <span class="TPoperator">= </span>db.Search<span class="TPbracket">(</span>query, <span class="TPkeyword2">Nothing</span>, <span class="TPnumber">0</span><span class="TPbracket">)</span>
</pre>
<p>Et, voila! You can now build the collection exactly as you want it and it's always immediately obvious what's going on to the next person.</p>
<p>Notice how, if the agent has been modified then we need to cater for this as the LastRan property can be used. In my case I just look for all documents in the last 24 hours.</p>
<h4>A Little Gotcha</h4>
<p>I don't know how the logic behind the <em>Notes Way</em> works but there's a gotcha in this method. If, for whatever reason, the agent takes a long time to run one day then the LastRun property could be, let's say, 10 minutes after it started. So, any documents created in that 10 minute gap won't be included the next time the agent runs.</p>
<p>You can get round this by wrapping the LastRun date with an @Adjust like this:</p><pre>@Created<span class="TPoperator">&gt;=</span>@Adjust<span class="TPbracket">([}</span><span class="TPoperator">+</span><span class="TPkeyword1">Cstr</span><span class="TPbracket">(</span>thisAgent.LastRun<span class="TPbracket">) </span><span class="TPoperator">+</span><span class="TPbracket">{]</span>;<span class="TPnumber">0</span>;<span class="TPnumber">0</span>;<span class="TPnumber">0</span>;<span class="TPnumber">0</span>;<span class="TPnumber">0</span>;<span class="TPoperator">-</span><span class="TPnumber">30</span><span class="TPbracket">) </span></pre>
<p>I've taken 30 seconds off. How many you do depends on how long you think it might take.</p>
<h4>Puttin It To Use On CodeStore</h4>
<p></p>
<p></p>
<p></p>
<p>I first put this technique to use on a "document indexing system" for a client. I then remembered it and used it last week while making a change to the way codestore's backend runs.</p>
<p>Until last week <em>every</em> comment posted on this site triggered an individual email to me. This meant I spent a lot of time deleting email from spammers who often attack in waves of 30 or more messages a time. This distraction cost me time and hence money.</p>
<p>I now have a two-hourly agent that sends me a summary email of unapproved comments (those on blogs older than a week). I then glance over it to look for real names (surprisingly obvious which are, as most my spam comes from the likes of "zyzuaeyuwqhun"). I now delete one email every two hours rather than 30 or more. Doesn't sound like much but it has made a big difference.</p>
<p>The down side is that legit posters on old blog entries have to wait up to 2 hours to see it go live. This happens so rarely that it's not really an issue.</p>
<p>My fight to keep the site CAPTCHA-free continues!</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20090624?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20090624</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20090624</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20090624?Open#comments</comments>
			<slash:comments>13</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20090624</wfw:commentRss>
		</item>
		<item>
			<title>My Little Angel Turns One | Blog</title>
			<pubDate>Tue, 23 Jun 2009 03:43:53 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p><img title="minnie" style="border-right: 0px; border-top: 0px; display: inline; margin: 10px 0px; border-left: 0px; border-bottom: 0px" height="315" alt="minnie" src="http://www.codestore.net/store.nsf/rsrc/AD801FEE6FFA26F2862575DE002FF665/$file/minnie_f7f49706-c57d-4020-b2e3-cb2dffdb487c.jpg" width="216" align="right" border="0">About this time a year ago I was in an ambulance, lights flashing, rushing to hospital. Moments later <a href="http://www.codestore.net/store.nsf/unid/BLOG-20080624?OpenDocument">out popped</a> little Minnie "Mooker" Howlett.</p> <p>One year on and she's turned in to a beautiful little girl. Maybe you shouldn't say it about your own child, but Minnie is probably the cutest baby in the world.</p> <p></p> <p></p> <p></p> <p>She's not a baby any more though and that's what I have to keep reminding myself. A year is a long time for a baby but flies by for the parent. It seems like last month she was born.</p> <p>Later on today I'll try and get a photo of her in the same style I've been <a href="http://www.codestore.net/store.nsf/unid/BLOG-20081215?OpenDocument">doing each year with Felix</a>. That's if I can keep her still long enough. She really doesn't like being made to lye down against her will. </p> <p>That's the other thing that you start to see in children after a year - their personality coming through. I can already see that Minnie is going to be a fun and caring girl but also have strong will power and, in later life, I think she'll be the kind of girl who knows how to get exactly what she wants.</p> <p>Happy birthday Mooker!</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20090623?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20090623</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20090623</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20090623?Open#comments</comments>
			<slash:comments>6</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20090623</wfw:commentRss>
		</item>
		<item>
			<title>My Website is My Business | Blog</title>
			<pubDate>Mon, 22 Jun 2009 09:08:22 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>Karen and I have an arrangement where 9 til 5 each weekday is "my time" and I spend most of it out in the office undisturbed. On the odd day I'll request an early start and Karen will normally agree but not without saying something to the effect of:</p> <blockquote>Ok, but as long as you're not just dicking about on your website!</blockquote> <p>At this point I'll remind her that, without codestore, my company wouldn't exist and we'd probably not be able to afford the house we live in, lifestyle we live etc etc. More importantly we wouldn't have the quality of life that me working from home affords us.</p> <h4>Codestore &amp; Rockall</h4> <p>There's no doubt that Rockall exists because of Codestore. In the first place that is. Whether Rockall continues because codestore does I don't know. I tell myself it does as this is what keeps me going with this site at the time when I wonder whether I should or not. The idea that, without the site, I would lose customers and end up like any other non-descript independent contractor and back on the open market place desperate for work isn't one I cherish.</p> <p>While it's true that all my customers are in some way connected to this site it's also the case that 90-odd % of my income comes from about half a dozen repeat customers. I've had this same set of customers for the past five years or more. Although I do get new customers coming along now and then, most of them are people who have been with or known of codestore for the duration. None of them are "new visitors" who have happened upon the site and then approached me for my services. This leads me to wonder whether codestore is really the business marketing vehicle I think is.</p> <p>I've been thinking about this more and more recently as I continue to contemplate the site's future. As it is I'm becoming less and less happy with the quality of the site and am getting to the point where I'm wondering why I do it and whether it's always better to quit while ahead.</p> <h4>Change of Direction</h4> <p>Half the reason I am becoming less happy with this site is that I don't feel it really reflect either me, my personality or what I'm really interested in. Following on from the idea that I need this site to continue working from home comes the idea that this site needs to be professional. For this reason I've put less of myself in to it of recent. That's never a good thing for a blog.</p> <p>The other notion I have is that I need to keep you guys happy. Happy readers = more readers = more business. But then I've not had any new business come about from my activity on here for years now. So, who am I kidding? </p> <p>What I need to ask myself is not so much <em>why</em> am I blogging but <em>who</em> am I blogging for. Should I be blogging for me or for you? The answer is probably a bit of both, but over the last couple of years I feel like I've been trying to make it more about you and so my interest has waned. Unless it's fun for me then there's little incentive.</p> <h4>Blogging as Fun</h4> <p>My thinking about this was brought to a head on Friday just gone when I posted an entry that received "No Comments". This is the first time that has happened pretty much since I began blogging. Although I can see why it got none (it's very specific and of little interest to you guys) the fact is comment numbers have become something of an obsession to me. So Friday was a bit of a welcome relief as I can now go forward from a clean start and worry not about how many of you respond.</p> <h4>Moving Forward</h4> <p>Friday's blog entry about Google Gears was a good example of what I've <em>not</em> been doing through fear of upsetting/losing my readers and that is talking about something you don't appear interested in. What I should be doing is talking about whatever it is I <em>am</em> interested and/or involved in at the time.</p> <p>Within a day of Friday's post Google had it as the <a href="http://www.google.co.uk/search?q=gears+manifest+ignorequery">top entry for the same query I used</a> in vain to try and find the solution the day before. That's what blogging is about for me. The fact I've added back to the community from which I take. Hopefully it will help somebody else out and then I've done my bit and will feel satisfied.</p> <p>Flex is another example of something I've talked about recently but then stopped as I didn't sense much interest. The effect then is that I continue working away with Flex but not talking about it here. Instead I talk about nothing and it looks like the site is going to the dogs.</p> <p>Rather than have long silences on here I think I'm going to try and talk about more random/fun things and more of what I want to talk about. Without worrying about it from a professional aspect or whether I'm keeping you all happy. Hopefully I'll get back to making a post a day. I do have plenty of stuff to talk about. Just maybe not what you want to hear all the time.</p> <p>The title to this blog has a double-meaning. Not just about the notion that Rockall is only because of codestore but also that what goes on my website should be my business and not so much yours. See what I did there!?</p> <p>Let's see where we end up in six months... hopefully I'll be putting far less emphasis on comment counts by then and having fun with this site once again.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20090622?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20090622</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20090622</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20090622?Open#comments</comments>
			<slash:comments>35</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20090622</wfw:commentRss>
		</item>
		<item>
			<title>Google Gears' Manifest Files - Lessons Learnt | Blog</title>
			<pubDate>Fri, 19 Jun 2009 03:42:37 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>Yesterday I got stuck working with a <a href="http://code.google.com/apis/gears/api_localserver.html#manifest_file">Gears Manifest file</a>. I found out why in the end and wanted to share what I learnt here so Google can find it for other users, as I'm finding there's a little bit of a lack of help out there for Gears. It being the new frontier n'all.</p> <p>My problem started when I wanted to create a local cache* of a file at a URL like:</p> <p><code>http://www.mysite.com/mydatabase.nsf/MyForm?ReadForm</code></p> <p class="sidePanel">* Although I use the term cache, Gears isn't actually adding the files to the browser's cache. Instead it keeps them in it's own folder for serving up from its own "LocalServer" when offline.</p> <p>I created a manifest file which looked a bit like this:</p><pre><span class="TPbracket">{</span>
  <span class="TPstring">"betaManifestVersion"</span>: <span class="TPnumber">1</span>,
  <span class="TPstring">"version"</span>: <span class="TPstring">"v1"</span>,
  <span class="TPstring">"entries"</span>: <span class="TPbracket"></span>
        <span class="TPbracket">{ </span><span class="TPstring">"url"</span>: <span class="TPstring">"MyForm?ReadForm"</span>, 
                <span class="TPstring">"src"</span>:<span class="TPstring">"MyForm?ReadForm&amp;offline=true"</span>, 
                <span class="TPstring">"ignoreQuery"</span>:<span class="TPkeyword1">true </span>
        <span class="TPbracket">}</span>,
        <span class="TPbracket">{ </span><span class="TPstring">"url"</span>: <span class="TPstring">"gears_init.js" </span><span class="TPbracket">}</span>
    <span class="TPbracket"></span>
<span class="TPbracket">}</span>
</pre>
<p>It didn't work and neither file were "cached" and, subsequently, going offline failed. After what seemed like hours of hair-pulling agony I worked out why.</p>
<p>If you are using the ignoreQuery option for a URL entry then you should not add a query to the URL part of the entry. The correct manifest would look like this:</p><pre><span class="TPbracket">{</span>
  <span class="TPstring">"betaManifestVersion"</span>: <span class="TPnumber">1</span>,
  <span class="TPstring">"version"</span>: <span class="TPstring">"v1"</span>,
  <span class="TPstring">"entries"</span>: <span class="TPbracket"></span>
        <span class="TPbracket">{ </span><span class="TPstring">"url"</span>: <span class="TPstring">"MyForm"</span>, 
                <span class="TPstring">"src"</span>:<span class="TPstring">"MyForm?ReadForm&amp;offline=true"</span>, 
                <span class="TPstring">"ignoreQuery"</span>:<span class="TPkeyword1">true </span>
        <span class="TPbracket">}</span>,
        <span class="TPbracket">{ </span><span class="TPstring">"url"</span>: <span class="TPstring">"gears_init.js" </span><span class="TPbracket">}</span>
    <span class="TPbracket"></span>
<span class="TPbracket">}</span>
</pre>
<p>I guess it makes sense really. If you're telling Gears to ignore bits like ?ReadForm why add it to the URL stored in the cache in the first place!? Just doesn't seem obvious to me. Hope that helps somebody else out.</p>
<p>Note that ignoreQuery only applies to the URL specified in the manifest entry and <em>not</em> to the SRC URL that is actually fetched and stored!</p>
<p>By specifying ignoreQuery the following URLs will all open the same page when in offline mode:</p>
<p><code>http://www.mysite.com/mydatabase.nsf/MyForm</code></p>
<p><code></code><code>http://www.mysite.com/mydatabase.nsf/MyForm?ReadForm&amp;id=1</code></p>
<p><code>http://www.mysite.com/mydatabase.nsf/MyForm?ReadForm&amp;id=2</code></p>
<p><code>http://www.mysite.com/mydatabase.nsf/MyForm?OpenForm&amp;new</code></p>
<p>If we hadn't specified ignoreQuery only the first of the above URLs would work.</p>
<h4>Case Sensitivity</h4>
<p>Another problem I wanted to find a solution to was that of case-sensitivity. Gears' LocalServer is very sensitive about the URL you use to get back in to an app in offline mode, should you have closed your browser since going offline. </p>
<p>With the above manifest in mind the following URLs won't get you back to the app in offline mode:</p>
<p><code>https://mysite.com/mydatabase.nsf/myform</code></p>
<p>This URL would fail for three reasons. </p>
<ol>
<li>The use of HTTPS 
<li>Lack of WWW from domain part 
<li>Lower case "myform"</li></ol>
<p>The URL must be exactly the same. You can see why if you look at how Gears stores the files in the OS:</p>
<p><img title="gearspic" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; margin: 10px 0px; border-right-width: 0px" height="294" alt="gearspic" src="http://www.codestore.net/store.nsf/rsrc/C092A7F9DA8867CD862575DA002FD8D6/$file/gearspic_6ffffef4-158a-49cb-a3f1-ebccdcbe6cc8.gif" width="530" border="0"> </p>
<p>See how the folder structure is strictly dictated by the URL used to go offline. Even down to the port number used!</p>
<p>You can however cater for any known cases where casing of the URL might matter. For example, let's say you know there are users out there alternating between the following URLs:</p>
<p><code>http://www.mysite.com/mydatabase.nsf/MyForm</code></p>
<p><code>http://www.mysite.com/mydatabase.nsf/myform</code></p>
<p>If they went offline at the first then they might expect to get back on if they typed in the second, no?</p>
<p>To cater for this you can use the following manifest file:</p><pre><span class="TPbracket">{</span>
  <span class="TPstring">"betaManifestVersion"</span>: <span class="TPnumber">1</span>,
  <span class="TPstring">"version"</span>: <span class="TPstring">"v1"</span>,
  <span class="TPstring">"entries"</span>: <span class="TPbracket"></span>
        <span class="TPbracket">{ </span><span class="TPstring">"url"</span>: <span class="TPstring">"MyForm"</span>, 
                <span class="TPstring">"src"</span>:<span class="TPstring">"MyForm?ReadForm&amp;offline=true"</span>, 
                <span class="TPstring">"ignoreQuery"</span>:<span class="TPkeyword1">true </span>
        <span class="TPbracket">}</span>,
        <span class="TPbracket">{ </span><span class="TPstring">"url"</span>: <span class="TPstring">"myform"</span>, <span class="TPstring">"redirect"</span>:<span class="TPstring">"MyForm" </span><span class="TPbracket">}</span>,
        <span class="TPbracket">{ </span><span class="TPstring">"url"</span>: <span class="TPstring">"gears_init.js" </span><span class="TPbracket">}</span>
    <span class="TPbracket"></span>
<span class="TPbracket">}</span>
</pre>
<p>Notice this second entry added. If the user typed the page name in in lower-case the Gears LocalServer would send the browser a 302 redirect message to open the proper-cased URL and all would be well.</p>
<p>You could keep adding entries to cater for other options like "myForm" but where you stop is down to you.</p>
<h4>When Things Go Wrong</h4>
<p>While testing offline mode I had lots of failed attempts at creating local versions of the required URLs. If for any reason a version failed then the easiest way to get back online is to remove the files stored. To do this you need to <a href="http://code.google.com/apis/gears/api_localserver.html#filesystem_location">find the files</a> and then remove the localserver.db file from the Gears folder and then the folder called "managed" inside the folder it keeps for your site/app.</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20090619?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20090619</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20090619</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20090619?Open#comments</comments>
			<slash:comments>0</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20090619</wfw:commentRss>
		</item>
		<item>
			<title>Position: SQL Consultant. Pay: Perhaps | Blog</title>
			<pubDate>Thu, 18 Jun 2009 02:53:32 -0500</pubDate>
			<author>Jake Howlett</author>
			<description><![CDATA[ <p>Any of you guys SQL experts and willing to help me out on an as-needed basis? Pay would be on performance/by the hour/beer tokens/in kind/whatever you liked.</p> <p>I'm confident enough with SQL to get most things done but get a bit stuck when it gets advanced (for me at least).</p> <p>There's an application I've been working on that has about five inter-dependent tables - joined by an ID from one that matches another. It all works as expected, just not as <em>fast</em> as expected as the amount of data grows. This is where my knowledge falls down.</p> <p>At the moment I'm struggling with working out how to create an index to make a query run faster. Specifically a query that has an ORDER BY clause on it. With no ordering it takes about 0.0001s. With the ordering it takes 0.3s. Still not long but that's on a smallish dataset. As it grows so will this time.</p> <p>I kind of get the principle of the index and why I need one in this case (the column sorted on isn't in any index) but I'm struggling implementing it and think I either need to go back to first principles or get/pay somebody else to sort it.</p> <p>Anybody?</p> <p>If you need to see a schema I can post that here.</p> <p><strong>Update:</strong> I just <a href="http://stackoverflow.com/questions/1011440/how-to-create-a-sql-index-to-import-order-by-performance">asked the question more specifically</a> over on stackoverflow.com, which seems like an increasingly useful resource, so I'll see how that goes.</p> <p><strong>Update 2:</strong> No more than 30 minutes have passed and stack overflow has answered my question. It's the first question I've asked there and I have to say I'm very impressed. That's how technical forums should be built Lotus!!</p>
<p><a href="http://www.codestore.net/store.nsf/unid/BLOG-20090618?open#post"><img border="0" src="http://www.codestore.net/store.nsf/images/rss_reply.gif" alt="Click here to post a response" /></a></p> 		]]></description>
			<link>http://www.codestore.net/store.nsf/unid/BLOG-20090618</link>
			<guid isPermaLink="true">http://www.codestore.net/store.nsf/unid/BLOG-20090618</guid>
			<comments>http://www.codestore.net/store.nsf/unid/BLOG-20090618?Open#comments</comments>
			<slash:comments>3</slash:comments>
			<wfw:commentRss>http://www.codestore.net/store.nsf/blog.xml?Open=20090618</wfw:commentRss>
		</item>


	</channel>
</rss> 
