<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	>

<channel>
	<title>2tap.com</title>
	<atom:link href="http://2tap.com/feed/" rel="self" type="application/rss+xml" />
	<link>http://2tap.com</link>
	<description>Random projects and stuff by Russ Hall in London</description>
	<pubDate>Tue, 16 Jun 2009 22:41:15 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.7.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Throttling uploads on Linux</title>
		<link>http://2tap.com/2009/06/16/throttling-uploads-on-linux/</link>
		<comments>http://2tap.com/2009/06/16/throttling-uploads-on-linux/#comments</comments>
		<pubDate>Tue, 16 Jun 2009 22:41:00 +0000</pubDate>
		<dc:creator>Russ</dc:creator>
		
		<category><![CDATA[Linux]]></category>

		<category><![CDATA[PHP]]></category>

		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://2tap.com/?p=137</guid>
		<description><![CDATA[I have recently been developing some fancy AJAX upload progress meters for a project I&#8217;m working on. This is using the new(ish) hooks in PHP which, when coupled with an extension such as APC, allow for polling of the upload progress as a file uploads in a standard HTML form.
Developing on a local server, however, [...]]]></description>
			<content:encoded><![CDATA[<p>I have recently been developing some fancy AJAX upload progress meters for a project I&#8217;m working on. This is using the new(ish) hooks in PHP which, when coupled with an extension such as APC, allow for polling of the upload progress as a file uploads in a standard HTML form.</p>
<p>Developing on a local server, however, means that file uploads are near instantaneous which makes testing&#8230; problematic. How best to simulate a real user&#8217;s experience?</p>
<p>My first instinct was to see if there were any suitable modules for Apache to enable bandwidth throttling. Apache 1.3 has mod_throttle which seems to be up to the task but I&#8217;m using Apache 2 and I don&#8217;t believe mod_throttle has been ported yet.</p>
<p>There also seem to be some extensions for Firefox which enable bandwidth limiting but these, unfortunately, are written for Windows environments.</p>
<p>The solution? <a href="http://www.monkey.org/~marius/trickle/">trickle</a>. Trickle is a portable lightweight userspace bandwidth shaper. It allows bandwidth limiting on a per-program basis and can be simply called with the executable as one of its parameters. Even better, it&#8217;s available in the Ubuntu repositories:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">apt-get</span> <span style="color: #c20cb9; font-weight: bold;">install</span> trickle</pre></div></div>

<p>So, to restrict Firefox&#8217;s upload bandwidth we can run the following command:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;">trickle <span style="color: #660033;">-s</span> <span style="color: #660033;">-d</span> <span style="color: #000000;">1000</span> <span style="color: #660033;">-u</span> <span style="color: #000000;">10</span> firefox</pre></div></div>

<p>This limits the upload rate of Firefox to 10Kb/s. Perfect for testing form uploads.</p>
<p><strong>Note:</strong> The <strong>-d</strong> flag shouldn&#8217;t be necessary (according to the docs) but without this arbitrarily high setting, download bandwidth seems to be hampered. The <strong>-s</strong> flag merely instructs trickle to run in standalone mode (as opposed to running through the trickle daemon).</p>
]]></content:encoded>
			<wfw:commentRss>http://2tap.com/2009/06/16/throttling-uploads-on-linux/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Efficient caching of versioned JavaScript, CSS and image assets for fun and profit</title>
		<link>http://2tap.com/2009/05/18/efficient-caching-of-versioned-javascript-css-and-image-assets-for-fun-and-profit/</link>
		<comments>http://2tap.com/2009/05/18/efficient-caching-of-versioned-javascript-css-and-image-assets-for-fun-and-profit/#comments</comments>
		<pubDate>Mon, 18 May 2009 18:23:33 +0000</pubDate>
		<dc:creator>Russ</dc:creator>
		
		<category><![CDATA[JavaScript]]></category>

		<category><![CDATA[Linux]]></category>

		<category><![CDATA[PHP]]></category>

		<category><![CDATA[Subversion]]></category>

		<guid isPermaLink="false">http://2tap.com/?p=89</guid>
		<description><![CDATA[&#8220;The new image is showing but I think it&#8217;s using the old stylesheet!&#8221;
Sound familiar?
Caching?
Caching of a web page&#8217;s assets such as CSS and image files can be a double-edged sword. On the one hand, if done right, it can lead to much faster load times with less strain on the server. If done incorrectly, or [...]]]></description>
			<content:encoded><![CDATA[<p><strong><em>&#8220;The new image is showing but I think it&#8217;s using the old stylesheet!&#8221;</em></strong></p>
<p>Sound familiar?</p>
<h3>Caching?</h3>
<p>Caching of a web page&#8217;s assets such as CSS and image files can be a double-edged sword. On the one hand, if done right, it can lead to much faster load times with less strain on the server. If done incorrectly, or worse not even considered, developers are opening themselves up to all kinds of synchronisation issues whenever files are modified.</p>
<p>In a typical web application, certain assets rarely change. Common theme images and JavaScript libraries are a good example of this. On the other hand, CSS files and the site&#8217;s core JavaScript functionality are prime candidates for frequent change but it is not an exact science and generally impossible to predict.</p>
<p>Caching of assets is the browser&#8217;s default behaviour. If an expiry time is not specifically set, it is up to the browser to decide how long to wait before checking the server for a new version. Once a file is in a browsers cache you&#8217;re at the mercy of the browser as to when the user will see the new version. Minutes? Hours? Days? Who knows. Your only option is to rename the asset in order to force the new version to be fetched.</p>
<p>So caching is evil, right? Well, no. With a little forethought, caching is your friend. And the user&#8217;s friend. And the web server&#8217;s friend. Treated right, it&#8217;s the life of the party.</p>
<p>Imagine your site is deployed once and nothing changes for eternity. The optimal caching strategy here is to instruct the browser to cache everything indefinitely. This means that, after the first visit, a user may never have to contact the server again. Load times are speedy. Your server&#8217;s relaxed. All is well. The problem, of course, is that any changes you <strong>do</strong> inevitably make will never be shown to users who have the site in their cache. At least, not without renaming the changed asset so the browser considers it a new file.</p>
<p>So the problem is that we want the browser to cache everything forever. Unless we change something. And we want the browser to know when we do this. Without asking us. And it&#8217;d be nice if this was automated. Ideas?</p>
<h3>Option One - Set an expiry date in the past for all assets</h3>
<p>Never cache anything!</p>
<p>Not really an option, but it <strong>does</strong> solve half of the problem. The browser will never cache anything and so the user will always see the latest version of all site assets. It works, but we&#8217;re completely missing out on one of the main benefits of caching - faster loading for the user and less stress on the server. Next.</p>
<h3>Option Two - Include a site version string in every URL</h3>
<p>One commonly used strategy is to include a unique identifier in every URL which is changed whenever the site is deployed. For example, an image at the following URL:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">/</span>images<span style="color: #000000; font-weight: bold;">/</span>logo.png</pre></div></div>

<p>Would become:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #000000; font-weight: bold;">/</span>images<span style="color: #000000; font-weight: bold;">/</span>logo.82.png</pre></div></div>

<p>Here, 82 is a unique identifier. With some Apache mod_rewrite trickery, we can transparently map this to the original URL. As far as the browser is concerned, this is a different file to the previous <em>logo.81.png</em> image and so any existing cache of this file is ignored.</p>
<p>Generally, this technique is employed in a semi-automated way. The version number can either be set manually in a configuration file (for example) or pulled from the repository version number. With this technique, all assets can be set to cache indefinitely.</p>
<p>The above is a pretty good solution. I&#8217;ve used it myself. But it&#8217;s not the most optimal. Every time a new version of the site is deployed, any assets in the users cache are invalidated. The whole site needs to be downloaded again. If site updates are infrequent, this isn&#8217;t <strong>too</strong> much of a problem. It sure as hell beats never caching anything or, worse, leaving the browser to decide how long to cache each item.</p>
<h3>Option Three - Fine grained caching + Automated!</h3>
<p>Clearly, the solution is to include a unique version string <strong>per file</strong>. This means that every file is considered independently and will only be re-downloaded if it has actually changed. One technique for doing this is to use the files last-modified timestamp. This gives a unique ID for the file which will change every time the file contents change. If the file is under version control (your projects <strong>are</strong> versioned, right?) we can&#8217;t use the modified timestamp as-is since it will change whenever the file is checked out. But we can find out what revision the file was changed in (under SVN at least) so we&#8217;re still good to go.</p>
<p>The goal is as follows: To instruct the browser to cache all assets (in this case, JavaScript, CSS and all image files) indefinitely. Whenever an asset changes, we want the URL to also change. The result of this is that whenever we deploy a new version of the site, only assets that have actually changed will be given a new URL. So if you&#8217;ve only changed one CSS file and a couple of images, repeat visits to the site will only need to re-download these files. We&#8217;d also like it to be automated. Only a masochist would attempt to manually change URLs whenever something changes on any sufficiently complex site.</p>
<p>Presented here is an automated solution for efficient caching using a bit of PHP and based on a site in an SVN repository. It&#8217;s also based around Linux. It could easily be adapted to other scripting languages, operating systems and/or version control systems - these technologies are merely presented here as an example.</p>
<p>To achieve the automated part, we need to run a script on the checked out version of the site prior to its deployment. The script will search the project for URLs (for a specific set of assets) and will rewrite the URL for any that it finds including a unique identifier. In our case, we&#8217;ll use the <strong>svn info</strong> command to find out the last revision the file actually changed in. Another approach would be to simply take a hash of the file contents (<a href="http://en.wikipedia.org/wiki/MD5">md5</a> would be a good candidate) and use this as its last-changed-identifier.</p>
<p>Rather than renaming each file to match the included identifier we set in the URL, we&#8217;ll use mod_rewrite within Apache to match a given format of URL back to its original. So <strong>myasset.123.png</strong> will be transparently mapped back to its original <strong>myasset.png</strong> filename.</p>
<p>Here&#8217;s a quick script I knocked up in PHP to facilitate this process. It should be run on a checked out working copy. It scans a given directory for files of a given type (in my base, &#8220;<strong>.tpl</strong>&#8221; (HTML templates) and <strong>.css</strong> files). Within each file it finds, it looks for any assets of a given type referenced in applicable areas (href and src attributes in HTML, url() in CSS). It then converts each URL to a filesystem path and checks the working copy for its existence. If it finds it, the URL is rewritten to include the last modified version number (pulled from <strong>svn info</strong>). Once this is done we just need to include an Apache mod_rewrite rule as discussed above.</p>
<h3>The PHP</h3>

<div class="wp_syntax"><div class="code"><pre class="php php" style="font-family:monospace;"><span style="color: #339933;">&lt;</span> ?php
&nbsp;
<span style="color: #666666; font-style: italic;">//</span>
<span style="color: #666666; font-style: italic;">// config</span>
<span style="color: #666666; font-style: italic;">//</span>
<span style="color: #000088;">$arr_config</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
&nbsp;
    <span style="color: #666666; font-style: italic;">// file types to check within for assets to version</span>
    <span style="">'file_extensions'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="">'tpl'</span><span style="color: #339933;">,</span> <span style="">'css'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
&nbsp;
    <span style="color: #666666; font-style: italic;">// asset extensions to version</span>
    <span style="">'asset_extensions'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="">'jpg'</span><span style="color: #339933;">,</span> <span style="">'jpeg'</span><span style="color: #339933;">,</span> <span style="">'png'</span><span style="color: #339933;">,</span> <span style="">'gif'</span><span style="color: #339933;">,</span> <span style="">'css'</span><span style="color: #339933;">,</span> <span style="">'ico'</span><span style="color: #339933;">,</span> <span style="">'js'</span><span style="color: #339933;">,</span> <span style="">'htc'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">,</span>
&nbsp;
    <span style="color: #666666; font-style: italic;">// filesystem path to the webroot of the application (so we can translate</span>
    <span style="color: #666666; font-style: italic;">// relative urls to the actual path on the filesystem)</span>
    <span style="">'webroot'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">dirname</span><span style="color: #009900;">&#40;</span><span style="color: #000000; font-weight: bold;">__FILE__</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">.</span> <span style="">'/../www'</span><span style="color: #339933;">,</span>
&nbsp;
    <span style="color: #666666; font-style: italic;">// regular expressions to match assets</span>
    <span style="">'regex'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
        <span style="">'/(?:src|href)=&quot;(.*)&quot;/iU'</span><span style="color: #339933;">,</span> <span style="color: #666666; font-style: italic;">// match assets in src and href attributes</span>
        <span style="">'/url\((.*)\)/iU'</span>          <span style="color: #666666; font-style: italic;">// match assets in CSS url() properties</span>
    <span style="color: #009900;">&#41;</span>
<span style="color: #009900;">&#41;</span>;
&nbsp;
<span style="color: #666666; font-style: italic;">//</span>
<span style="color: #666666; font-style: italic;">// arguments</span>
<span style="color: #666666; font-style: italic;">//</span>
&nbsp;
<span style="color: #666666; font-style: italic;">// we require just one argument, the root path to search for files</span>
<span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #990000;">isset</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$_SERVER</span><span style="color: #009900;">&#91;</span><span style="">'argv'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
    <span style="color: #990000;">die</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">&quot;Error: first argument must be the path to your working copy<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span><span style="color: #009900;">&#41;</span>;
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #666666; font-style: italic;">//</span>
<span style="color: #666666; font-style: italic;">// execute</span>
<span style="color: #666666; font-style: italic;">//</span>
version_assets<span style="color: #009900;">&#40;</span><span style="color: #000088;">$_SERVER</span><span style="color: #009900;">&#91;</span><span style="">'argv'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span><span style="color: #339933;">,</span> <span style="color: #000088;">$arr_config</span><span style="color: #009900;">&#41;</span>;
&nbsp;
&nbsp;
&nbsp;
&nbsp;
<span style="color: #0000ff; font-style: italic;">/**
 * Checks each file in the passed path recursively to see if there are any assets
 * to version.
 *
 * Only file extensions defined in the config are checked and then only assets matching
 * a particular filetype are versioned.
 *
 * If an asset referenced is not found on the filesystem or is not under version control
 * within the working copy, the asset is ignored and nothing is changed.
 *
 * @param str $str_search_path    Path to begin scanning of files
 * @param arr $arr_config         Configuration params determining which files to check, which
 *                                asset extensions to check etc.
 * @return void
 */</span>
<span style="color: #000000; font-weight: bold;">function</span> version_assets<span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_search_path</span><span style="color: #339933;">,</span> <span style="color: #000088;">$arr_config</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #666666; font-style: italic;">// pull in filenames to check</span>
    <span style="color: #000088;">$arr_files</span> <span style="color: #339933;">=</span> get_files_recursive<span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_search_path</span><span style="color: #339933;">,</span> <span style="color: #000088;">$arr_config</span><span style="color: #009900;">&#91;</span><span style="">'file_extensions'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span>;
&nbsp;
    <span style="color: #b1b100;">foreach</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr_files</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$str_file</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
        <span style="color: #666666; font-style: italic;">// load the file into memory</span>
        <span style="color: #000088;">$str_file_content</span> <span style="color: #339933;">=</span> <span style="color: #990000;">file_get_contents</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_file</span><span style="color: #009900;">&#41;</span>;
&nbsp;
        <span style="color: #666666; font-style: italic;">// look for any matching assets in the regex list defined in the config</span>
        <span style="color: #000088;">$arr_matches</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;
&nbsp;
        <span style="color: #b1b100;">foreach</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr_config</span><span style="color: #009900;">&#91;</span><span style="">'regex'</span><span style="color: #009900;">&#93;</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$str_regex</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
            <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">preg_match_all</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_regex</span><span style="color: #339933;">,</span> <span style="color: #000088;">$str_file_content</span><span style="color: #339933;">,</span> <span style="color: #000088;">$arr_m</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #000088;">$arr_matches</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array_merge</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr_matches</span><span style="color: #339933;">,</span> <span style="color: #000088;">$arr_m</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span>;
            <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        <span style="color: #666666; font-style: italic;">// filter out any matches that do not have an extension defined in the asset list</span>
        <span style="color: #000088;">$arr_matches_filtered</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;
&nbsp;
        <span style="color: #b1b100;">foreach</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr_matches</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$str_match</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
            <span style="color: #000088;">$arr_url</span> <span style="color: #339933;">=</span> <span style="color: #990000;">parse_url</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_match</span><span style="color: #009900;">&#41;</span>;
            <span style="color: #000088;">$str_asset</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$arr_url</span><span style="color: #009900;">&#91;</span><span style="">'path'</span><span style="color: #009900;">&#93;</span>;
&nbsp;
            <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">preg_match</span><span style="color: #009900;">&#40;</span><span style="">'/\.('</span> <span style="color: #339933;">.</span> <span style="color: #990000;">implode</span><span style="color: #009900;">&#40;</span><span style="">'|'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$arr_config</span><span style="color: #009900;">&#91;</span><span style="">'asset_extensions'</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">.</span> <span style="">'$)/iU'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$str_asset</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #000088;">$arr_matches_filtered</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$str_asset</span>;
            <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        <span style="color: #666666; font-style: italic;">// if we've found any matches, process them</span>
        <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">count</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr_matches_filtered</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
            <span style="color: #666666; font-style: italic;">// flag to determine if we need to write any changes back once we've processed</span>
            <span style="color: #666666; font-style: italic;">// each match</span>
            <span style="color: #000088;">$boo_modified_file</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">false</span>;
&nbsp;
            <span style="color: #b1b100;">foreach</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr_matches_filtered</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$str_url_asset</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
                <span style="color: #666666; font-style: italic;">// use parse_url to extract just the path</span>
                <span style="color: #000088;">$arr_parsed</span> <span style="color: #339933;">=</span> <span style="color: #990000;">parse_url</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_url_asset</span><span style="color: #009900;">&#41;</span>;
                <span style="color: #000088;">$str_url_path</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$arr_parsed</span><span style="color: #009900;">&#91;</span><span style="">'path'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">.</span> <span style="color: #339933;">@</span><span style="color: #000088;">$arr_parsed</span><span style="color: #009900;">&#91;</span><span style="">'query'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">.</span> <span style="color: #339933;">@</span><span style="color: #000088;">$arr_parsed</span><span style="color: #009900;">&#91;</span><span style="">'fragment'</span><span style="color: #009900;">&#93;</span>;
&nbsp;
                <span style="color: #666666; font-style: italic;">// if this is a relative url (e.g. begininng ../) then work out the filesystem path</span>
                <span style="color: #666666; font-style: italic;">// based on the location of the file containing the asset</span>
                <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">strpos</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_url_path</span><span style="color: #339933;">,</span> <span style="">'../'</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">===</span> <span style="color:#800080;">0</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                    <span style="color: #000088;">$str_fs_path</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$arr_config</span><span style="color: #009900;">&#91;</span><span style="">'webroot'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">.</span> <span style="">'/'</span> <span style="color: #339933;">.</span> <span style="color: #990000;">dirname</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_file</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">.</span> <span style="">'/'</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$str_url_path</span>;
                <span style="color: #009900;">&#125;</span>
                <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
                    <span style="color: #000088;">$str_fs_path</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$arr_config</span><span style="color: #009900;">&#91;</span><span style="">'webroot'</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">.</span> <span style="">'/'</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$str_url_path</span>;
                <span style="color: #009900;">&#125;</span>
&nbsp;
                <span style="color: #666666; font-style: italic;">// normalise path with realpath</span>
                <span style="color: #000088;">$str_fs_path</span> <span style="color: #339933;">=</span> <span style="color: #990000;">realpath</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_fs_path</span><span style="color: #009900;">&#41;</span>;
&nbsp;
                <span style="color: #666666; font-style: italic;">// only proceed if the file exists</span>
                <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_fs_path</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
                    <span style="color: #666666; font-style: italic;">// execute the svn info command to retrieve the change information</span>
                    <span style="color: #000088;">$str_svn_result</span> <span style="color: #339933;">=</span> <span style="color: #339933;">@</span><span style="color: #990000;">shell_exec</span><span style="color: #009900;">&#40;</span><span style="">'svn info '</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$str_fs_path</span><span style="color: #009900;">&#41;</span>;
                    <span style="color: #000088;">$arr_svn_matches</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;
&nbsp;
                    <span style="color: #666666; font-style: italic;">// extract the last changed revision to use as the version</span>
                    <span style="color: #990000;">preg_match</span><span style="color: #009900;">&#40;</span><span style="">'/Last Changed Rev: ([0-9]+)/i'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$str_svn_result</span><span style="color: #339933;">,</span> <span style="color: #000088;">$arr_svn_matches</span><span style="color: #009900;">&#41;</span>;
&nbsp;
                    <span style="color: #666666; font-style: italic;">// only proceed if this file is in version control (e.g. we retrieved a valid match</span>
                    <span style="color: #666666; font-style: italic;">// from the regex above)</span>
                    <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">count</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr_svn_matches</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
                        <span style="color: #000088;">$str_version</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$arr_svn_matches</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span>;
&nbsp;
                        <span style="color: #666666; font-style: italic;">// add version number into the file url (in the form asset.name.VERSION.ext)</span>
                        <span style="color: #000088;">$str_versioned_url</span> <span style="color: #339933;">=</span> <span style="color: #990000;">preg_replace</span><span style="color: #009900;">&#40;</span><span style="">'/(.*)(\.[a-zA-Z0-9]+)$/'</span><span style="color: #339933;">,</span> <span style="">'$1.'</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$str_version</span> <span style="color: #339933;">.</span> <span style="">'$2'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$str_url_asset</span><span style="color: #009900;">&#41;</span>;
                        <span style="color: #000088;">$str_file_content</span> <span style="color: #339933;">=</span> <span style="color: #990000;">str_replace</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_url_asset</span><span style="color: #339933;">,</span> <span style="color: #000088;">$str_versioned_url</span><span style="color: #339933;">,</span> <span style="color: #000088;">$str_file_content</span><span style="color: #009900;">&#41;</span>;
&nbsp;
                        <span style="color: #666666; font-style: italic;">// flag as</span>
                        <span style="color: #000088;">$boo_modified_file</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">true</span>;
&nbsp;
                        <span style="color: #990000;">echo</span> <span style="">'Versioned: ['</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$str_url_asset</span> <span style="color: #339933;">.</span> <span style="">'] referenced in file: ['</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$str_file</span> <span style="color: #339933;">.</span> <span style="">']'</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
                    <span style="color: #009900;">&#125;</span>
                    <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
                        <span style="color: #990000;">echo</span> <span style="">'Ignored: ['</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$str_url_asset</span> <span style="color: #339933;">.</span> <span style="">'] referenced in file: ['</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$str_file</span> <span style="color: #339933;">.</span> <span style="">'] (not versioned)'</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
                    <span style="color: #009900;">&#125;</span>
                <span style="color: #009900;">&#125;</span>
                <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
                    <span style="color: #990000;">echo</span> <span style="">'Ignored: ['</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$str_url_asset</span> <span style="color: #339933;">.</span> <span style="">'] referenced in file: ['</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$str_file</span> <span style="color: #339933;">.</span> <span style="">'] (not on filesystem)'</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
                <span style="color: #009900;">&#125;</span>
            <span style="color: #009900;">&#125;</span>
&nbsp;
            <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$boo_modified_file</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                <span style="color: #990000;">echo</span> <span style="">'-&gt; WRITING: '</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$str_file</span> <span style="color: #339933;">.</span> <span style="color: #0000ff;">&quot;<span style="color: #000099; font-weight: bold;">\n</span>&quot;</span>;
&nbsp;
                <span style="color: #666666; font-style: italic;">// write changes to this file back to the file system</span>
                file_put_contents<span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_file</span><span style="color: #339933;">,</span> <span style="color: #000088;">$str_file_content</span><span style="color: #009900;">&#41;</span>;
            <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span>
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span>
&nbsp;
<span style="color: #0000ff; font-style: italic;">/**
 * Utility method to recursively retrieve all files under a given directory. If
 * an optional array of extensions is passed, only these filetypes will be returned.
 *
 * Ignores any svn directories.
 *
 * @param str $str_path_start  Path to begin searching
 * @param mix $mix_extensions  Array of extensions to match or null to match any
 * @return array
 */</span>
<span style="color: #000000; font-weight: bold;">function</span> get_files_recursive<span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_path_start</span><span style="color: #339933;">,</span> <span style="color: #000088;">$mix_extensions</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">null</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
    <span style="color: #000088;">$arr_files</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;
&nbsp;
    <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$obj_handle</span> <span style="color: #339933;">=</span> <span style="color: #990000;">opendir</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_path_start</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
        <span style="color: #b1b100;">while</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_file</span> <span style="color: #339933;">=</span> <span style="color: #990000;">readdir</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$obj_handle</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
            <span style="color: #666666; font-style: italic;">// ignore meta files and svn directories</span>
            <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #990000;">in_array</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_file</span><span style="color: #339933;">,</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="">'.'</span><span style="color: #339933;">,</span> <span style="">'..'</span><span style="color: #339933;">,</span> <span style="">'.svn'</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
                <span style="color: #666666; font-style: italic;">// construct full path</span>
                <span style="color: #000088;">$str_path</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$str_path_start</span> <span style="color: #339933;">.</span> <span style="">'/'</span> <span style="color: #339933;">.</span> <span style="color: #000088;">$str_file</span>;
&nbsp;
                <span style="color: #666666; font-style: italic;">// if this is a directory, recursively retrieve its children</span>
                <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">is_dir</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_path</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
&nbsp;
                    <span style="color: #000088;">$arr_files</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array_merge</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr_files</span><span style="color: #339933;">,</span> get_files_recursive<span style="color: #009900;">&#40;</span><span style="color: #000088;">$str_path</span><span style="color: #339933;">,</span> <span style="color: #000088;">$mix_extensions</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>;
                <span style="color: #009900;">&#125;</span>
&nbsp;
                <span style="color: #666666; font-style: italic;">// otherwise add to the list</span>
                <span style="color: #b1b100;">else</span> <span style="color: #009900;">&#123;</span>
&nbsp;
                    <span style="color: #666666; font-style: italic;">// only add if it's included in the extension list (if applicable)</span>
                    <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$mix_extensions</span> <span style="color: #339933;">==</span> <span style="color: #000000; font-weight: bold;">null</span> || <span style="color: #990000;">preg_match</span><span style="color: #009900;">&#40;</span><span style="">'/.*\.('</span> <span style="color: #339933;">.</span> <span style="color: #990000;">implode</span><span style="color: #009900;">&#40;</span><span style="">'|'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$mix_extensions</span><span style="color: #009900;">&#41;</span> <span style="color: #339933;">.</span><span style="">')$/Ui'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$str_file</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
                        <span style="color: #000088;">$arr_files</span><span style="color: #009900;">&#91;</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #990000;">str_replace</span><span style="color: #009900;">&#40;</span><span style="">'//'</span><span style="color: #339933;">,</span> <span style="">'/'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$str_path</span><span style="color: #009900;">&#41;</span>;
                    <span style="color: #009900;">&#125;</span>
                <span style="color: #009900;">&#125;</span>
            <span style="color: #009900;">&#125;</span>
        <span style="color: #009900;">&#125;</span>
&nbsp;
        <span style="color: #990000;">closedir</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$obj_handle</span><span style="color: #009900;">&#41;</span>;
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #b1b100;">return</span> <span style="color: #000088;">$arr_files</span>;
<span style="color: #009900;">&#125;</span></pre></div></div>

<p>This is then executed like so:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;">php version_assets.php <span style="color: #ff0000;">&quot;/path/to/project/checkout&quot;</span></pre></div></div>

<h3>The Apache config</h3>

<div class="wp_syntax"><div class="code"><pre class="apache apache" style="font-family:monospace;"><span style="color: #adadad; font-style: italic;">#</span>
<span style="color: #adadad; font-style: italic;"># Rewrite versioned asset urls</span>
<span style="color: #adadad; font-style: italic;">#</span>
<span style="color: #00007f;">RewriteEngine</span> <span style="color: #0000ff;">on</span>
<span style="color: #00007f;">RewriteRule</span> ^(.+)(\.[0-<span style="color: #ff0000;">9</span>]+)\.(js|css|jpg|jpeg|gif|png)$ $1.$<span style="color: #ff0000;">3</span> [L]
&nbsp;
<span style="color: #adadad; font-style: italic;">#</span>
<span style="color: #adadad; font-style: italic;"># Set near indefinite expiry for certain assets</span>
<span style="color: #adadad; font-style: italic;">#</span>
&lt;<span style="color: #000000; font-weight:bold;">filesmatch</span> <span style="color: #7f007f;">&quot;<span style="color: #000099; font-weight: bold;">\.</span>(css|js|jpg|jpeg|png|gif|htc)$&quot;</span>&gt;
    <span style="color: #00007f;">ExpiresActive</span> <span style="color: #0000ff;">On</span>
    <span style="color: #00007f;">ExpiresDefault</span> <span style="color: #7f007f;">&quot;access plus 5 years&quot;</span>
&lt;/<span style="color: #000000; font-weight:bold;">filesmatch</span>&gt;</pre></div></div>

<p>Note: You&#8217;ll need the <strong>rewrite</strong> and <strong>expires</strong> modules enabled in Apache. This is for Apache 2. The syntax above may be somewhat different for Apache 1.3. To enable the modules in Apache 2 you can simply use:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;">a2enmod rewrite
a2enmod expires</pre></div></div>

<p>Done! Now, whenever the site is deployed, only changed assets will be downloaded. Fast, efficient and headache free. Well, unless&#8230;</p>
<h3>Caveats</h3>
<p>The above script is purely to illustrate the process. Your specific needs may well need a slightly different approach. For example, there may be other areas it needs to look for URLs. If you do a lot of dynamic construction of URLs or funky script includes with JavaScript, you may need a secondary deployment script or procedure in order to accommodate such features. Using this technique, you must be careful to add the unique version to <strong>all</strong> the file types looked for in the deployment script, otherwise you&#8217;re telling the browser to cache a file indefinitely without the URL changing on new versions being deployed.</p>
<p>Another area to watch out for would be if you serve assets from different domains. Again, this technique will work in principle but will need some modification. It&#8217;s an exercise left to you, dear reader.</p>
<p>So, there you have it. A reasonably hassle free, efficient and optimised caching policy for your web applications. I hope you find this helpful - good luck.</p>
]]></content:encoded>
			<wfw:commentRss>http://2tap.com/2009/05/18/efficient-caching-of-versioned-javascript-css-and-image-assets-for-fun-and-profit/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Screen scraping sales from Createspace with Zend_Http_Client</title>
		<link>http://2tap.com/2009/04/29/screen-scraping-book-sales-from-createspace-with-zend_http_client/</link>
		<comments>http://2tap.com/2009/04/29/screen-scraping-book-sales-from-createspace-with-zend_http_client/#comments</comments>
		<pubDate>Wed, 29 Apr 2009 22:23:31 +0000</pubDate>
		<dc:creator>Russ</dc:creator>
		
		<category><![CDATA[PHP]]></category>

		<category><![CDATA[Zend Framework]]></category>

		<guid isPermaLink="false">http://2tap.com/?p=73</guid>
		<description><![CDATA[More and more of our data is hidden behind login forms in online apps. When this data updates frequently, and the site provides no API to access the information, keeping on top of it can be a laborious task.
One such example is Createspace. Createspace are a company who provide produce-on-demand manufacturing for products such as [...]]]></description>
			<content:encoded><![CDATA[<p>More and more of our data is hidden behind login forms in online apps. When this data updates frequently, and the site provides no API to access the information, keeping on top of it can be a laborious task.</p>
<p>One such example is <a href="http://www.createspace.com">Createspace</a>. Createspace are a company who provide produce-on-demand manufacturing for products such as books, DVDs and CDs. This allows individuals and smaller publishers to get their products to the market without investing in heavy up front printing costs. Any orders for the product go directly to Createspace, they manufacture and ship the product and finally allocate the profit to the seller.</p>
<p>I have <a href="http://www.amazon.com/gp/product/0956057101">recently been involved in helping to get a book to market</a> and am using Createspace&#8217;s services to produce the book. Keeping track of sales, however, is time consuming due to having to login to Createspace each time and navigate to the relevant area to retrieve the data. The book is also being produced by another company in the UK which have a similar setup meaning now twice the time is required each time I wish to check for sales.</p>
<p>So what to do with no API? No real choice but to screen scrape. Presented here is a quick script I knocked up using PHP and the Zend Framework to scrape sales data from Createspace. Whilst the implementation is Createspace specific, the general process is not and so I hope this will be give some pointers for similar tasks.</p>
<p>To use this we&#8217;re using the Zend_Http_Client from the Zend Framework. This offers similar functionality to the basic PHP cURL extension but in a nicer (IMHO) API. The basic (generic) steps required are:</p>
<ol>
<li>Post required credential details to the application login URL</li>
<li>Store any authentication details (likely a session cookie) sent back from the process</li>
<li>Use the obtained credentials to retrieve the page we wish to scrape the data from</li>
<li>Sprinkle some regex magic on the retrieved HTML to extract the figures we require</li>
</ol>
<p>Here&#8217;s the script:</p>

<div class="wp_syntax"><div class="code"><pre class="php php" style="font-family:monospace;">    <span style="color: #666666; font-style: italic;">//</span>
    <span style="color: #666666; font-style: italic;">// General config</span>
    <span style="color: #666666; font-style: italic;">//</span>
&nbsp;
    <span style="color: #666666; font-style: italic;">// revenue per produce (so we can calculate totals)</span>
    <span style="color: #990000;">define</span><span style="color: #009900;">&#40;</span><span style="">'REVENUE_PER_PRODUCT'</span><span style="color: #339933;">,</span> <span style="color:#800080;">100.00</span><span style="color: #009900;">&#41;</span>;
&nbsp;
    <span style="color: #666666; font-style: italic;">// login url and credentials</span>
    <span style="color: #990000;">define</span><span style="color: #009900;">&#40;</span><span style="">'CREATESPACE_LOGIN_URL'</span><span style="color: #339933;">,</span> <span style="">'https://www.createspace.com/LoginProc.do'</span><span style="color: #009900;">&#41;</span>;
    <span style="color: #990000;">define</span><span style="color: #009900;">&#40;</span><span style="">'CREATESPACE_LOGIN_EMAIL'</span><span style="color: #339933;">,</span> <span style="">'email'</span><span style="color: #009900;">&#41;</span>;
    <span style="color: #990000;">define</span><span style="color: #009900;">&#40;</span><span style="">'CREATESPACE_LOGIN_PASSWORD'</span><span style="color: #339933;">,</span> <span style="">'password'</span><span style="color: #009900;">&#41;</span>;
&nbsp;
    <span style="color: #666666; font-style: italic;">// reports url</span>
    <span style="color: #990000;">define</span><span style="color: #009900;">&#40;</span><span style="">'CREATESPACE_REPORTS_URL'</span><span style="color: #339933;">,</span> <span style="">'https://www.createspace.com/Member/Report/MemberReport.do'</span><span style="color: #009900;">&#41;</span>;
&nbsp;
    <span style="color: #666666; font-style: italic;">//</span>
    <span style="color: #666666; font-style: italic;">// Retrieve CreateSpace sales data</span>
    <span style="color: #666666; font-style: italic;">//</span>
    <span style="color: #000088;">$obj_client</span> <span style="color: #339933;">=</span> <span style="color: #000000; font-weight: bold;">new</span> Zend_Http_Client<span style="color: #009900;">&#40;</span>CREATESPACE_LOGIN_URL<span style="color: #009900;">&#41;</span>;
&nbsp;
    <span style="color: #666666; font-style: italic;">// fake the useragent in the request to make it look more authentic</span>
    <span style="color: #000088;">$obj_client</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">setConfig</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
        <span style="">'useragent'</span> <span style="color: #339933;">=&gt;</span> <span style="">'Mozilla/5.0 (X11; U; Linux i686; en-GB; rv:1.9.0.6) Gecko/2009020911 Ubuntu/8.10 (intrepid) Firefox/3.0.6'</span>
    <span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>;
&nbsp;
    <span style="color: #666666; font-style: italic;">// we want to retrieve any cookies posted back to us to use in the next step</span>
    <span style="color: #000088;">$obj_client</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">setCookieJar</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;
&nbsp;
    <span style="color: #666666; font-style: italic;">// login parameters (entries to the login form fields)</span>
    <span style="color: #000088;">$obj_client</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">setParameterPost</span><span style="color: #009900;">&#40;</span><span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span>
       <span style="">'login'</span> <span style="color: #339933;">=&gt;</span> CREATESPACE_LOGIN_EMAIL<span style="color: #339933;">,</span>
       <span style="">'password'</span> <span style="color: #339933;">=&gt;</span> CREATESPACE_LOGIN_EMAIL<span style="color: #339933;">,</span>
       <span style="">'action'</span> <span style="color: #339933;">=&gt;</span> <span style="">'Log In'</span>
    <span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>;
&nbsp;
    <span style="color: #666666; font-style: italic;">// send the POST data</span>
    <span style="color: #000088;">$obj_client</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">request</span><span style="color: #009900;">&#40;</span><span style="">'POST'</span><span style="color: #009900;">&#41;</span>;
&nbsp;
    <span style="color: #666666; font-style: italic;">// we're now &quot;logged in&quot; so we can retrieve the reports page</span>
    <span style="color: #000088;">$obj_client</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">setUri</span><span style="color: #009900;">&#40;</span>CREATESPACE_REPORTS_URL<span style="color: #009900;">&#41;</span>;
    <span style="color: #000088;">$obj_client</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">request</span><span style="color: #009900;">&#40;</span><span style="">'GET'</span><span style="color: #009900;">&#41;</span>;
&nbsp;
    <span style="color: #666666; font-style: italic;">// extract the content from the request</span>
    <span style="color: #000088;">$str_page</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$obj_client</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getLastResponse</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">-&gt;</span><span style="color: #004000;">getBody</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;
&nbsp;
    <span style="color: #666666; font-style: italic;">//</span>
    <span style="color: #666666; font-style: italic;">// Now we have the raw report HTML, it's simply a case of extracting the sales figures</span>
    <span style="color: #666666; font-style: italic;">//</span>
&nbsp;
    <span style="color: #666666; font-style: italic;">// first grab the table data rows (tbody)</span>
    <span style="color: #990000;">preg_match</span><span style="color: #009900;">&#40;</span><span style="">'/&lt;table .*?id=&quot;units&quot;.*?&lt;tbody&gt;(.*?)&lt; \/tbody&gt;&lt; \/table&gt;/is'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$str_page</span><span style="color: #339933;">,</span> <span style="color: #000088;">$arr_matches</span><span style="color: #009900;">&#41;</span>;
    <span style="color: #000088;">$str_table_body</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$arr_matches</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span>;
&nbsp;
    <span style="color: #666666; font-style: italic;">// then extract each row's data</span>
    <span style="color: #990000;">preg_match_all</span><span style="color: #009900;">&#40;</span><span style="">'/&lt;tr class=&quot;.*?&quot;&gt;.*?&lt;td&gt;(.*?)&lt; \/td&gt;.*?&lt;/td&gt;&lt;td&gt;-&lt; \/td&gt;.*?&lt;/td&gt;&lt;td&gt;-&lt; \/td&gt;.*?&lt;/td&gt;&lt;td&gt;-&lt; \/td&gt;.*?&lt;/td&gt;&lt;td&gt;(.*?)&lt; \/td&gt;/is'</span><span style="color: #339933;">,</span> <span style="color: #000088;">$str_table_body</span><span style="color: #339933;">,</span> <span style="color: #000088;">$arr_matches</span><span style="color: #339933;">,</span> PREG_SET_ORDER<span style="color: #009900;">&#41;</span>;
&nbsp;
    <span style="color: #666666; font-style: italic;">// merge into a more sane array, indexed by date in the form Ym (e.g. 200901 for January, 2009)</span>
    <span style="color: #000088;">$arr_data</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;
&nbsp;
    <span style="color: #b1b100;">foreach</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr_matches</span> <span style="color: #b1b100;">as</span> <span style="color: #000088;">$arr_match</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000088;">$str_date</span> <span style="color: #339933;">=</span> <span style="color: #990000;">date</span><span style="color: #009900;">&#40;</span><span style="">'Ym'</span><span style="color: #339933;">,</span> <span style="color: #990000;">strtotime</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr_match</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">1</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span>;
        <span style="color: #b1b100;">if</span><span style="color: #009900;">&#40;</span><span style="color: #339933;">!</span><span style="color: #990000;">isset</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr_data</span><span style="color: #009900;">&#91;</span><span style="color: #000088;">$str_date</span><span style="color: #009900;">&#93;</span><span style="color: #009900;">&#41;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
            <span style="color: #000088;">$arr_data</span><span style="color: #009900;">&#91;</span><span style="color: #000088;">$str_date</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;
        <span style="color: #009900;">&#125;</span>
&nbsp;
        <span style="color: #000088;">$int_volume</span> <span style="color: #339933;">=</span> <span style="color: #009900;">&#40;</span>int<span style="color: #009900;">&#41;</span><span style="color: #000088;">$arr_match</span><span style="color: #009900;">&#91;</span><span style="color: #cc66cc;">2</span><span style="color: #009900;">&#93;</span>;
&nbsp;
        <span style="color: #000088;">$arr_data</span><span style="color: #009900;">&#91;</span><span style="color: #000088;">$str_date</span><span style="color: #009900;">&#93;</span> <span style="color: #339933;">=</span> <span style="color: #990000;">array</span><span style="color: #009900;">&#40;</span><span style="">'volume'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000088;">$int_volume</span><span style="color: #339933;">,</span> <span style="">'revenue'</span> <span style="color: #339933;">=&gt;</span> <span style="color: #000088;">$int_volume</span> <span style="color: #339933;">*</span> REVENUE_PER_BOOK<span style="color: #009900;">&#41;</span>;
    <span style="color: #009900;">&#125;</span>
&nbsp;
    <span style="color: #666666; font-style: italic;">// $arr_data now contains sales data (volume and revenue) for each month found in the sales table, indexed by</span>
    <span style="color: #666666; font-style: italic;">// the month</span>
    <span style="color: #990000;">print_r</span><span style="color: #009900;">&#40;</span><span style="color: #000088;">$arr_data</span><span style="color: #009900;">&#41;</span>;</pre></div></div>

<p>A few things to note. Firstly, we&#8217;re faking the useragent to a generic &#8220;real looking&#8221; example (as opposed to the default &#8220;Zend_Http_Client&#8221;). Morally we&#8217;re doing nothing wrong here but I suspect &#8220;automated crawling&#8221; is frowned upon in the T&#038;Cs somewhere so best not to make it too obvious.</p>
<p>It should also be mentioned that this method (like all screen scraping) is vulnerable to breaking if Createspace change their login system or HTML structure. There are certainly cleverer parsing methods that can be employed which are more adaptable to change but only up to a point. There&#8217;s not a lot you can do if things dramatically change except for adapting the script to accommodate.</p>
]]></content:encoded>
			<wfw:commentRss>http://2tap.com/2009/04/29/screen-scraping-book-sales-from-createspace-with-zend_http_client/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Extending DOM elements in Mootools ala jQuery</title>
		<link>http://2tap.com/2009/01/11/extending-dom-elements-in-mootools-ala-jquery/</link>
		<comments>http://2tap.com/2009/01/11/extending-dom-elements-in-mootools-ala-jquery/#comments</comments>
		<pubDate>Sat, 10 Jan 2009 23:05:19 +0000</pubDate>
		<dc:creator>Russ</dc:creator>
		
		<category><![CDATA[JavaScript]]></category>

		<category><![CDATA[MooTools]]></category>

		<guid isPermaLink="false">http://2tap.com/?p=64</guid>
		<description><![CDATA[There are a lot of jQuery plugins to add simple enhancements to DOM elements (such as form validation, adding tooltips to links, etc.). In contrast, the Mootools plugin scene is relatively baron which is a shame. The architecture of MooTools is quite different to jQuery and doesn&#8217;t perhaps encourage this kind of plugin to the [...]]]></description>
			<content:encoded><![CDATA[<p>There are a lot of jQuery plugins to add simple enhancements to DOM elements (such as form validation, adding tooltips to links, etc.). In contrast, the Mootools plugin scene is relatively baron which is a shame. The architecture of MooTools is quite different to jQuery and doesn&#8217;t perhaps encourage this kind of plugin to the same extent but, for the common case, plugins can be written in a relatively similar fashion.</p>
<p>As an example, here&#8217;s how you&#8217;d write a method you can call on any DOM <a href="http://mootools.net/docs/Element/Element#Element">Element</a> (or DOM <a href="http://mootools.net/docs/Element/Element#Elements">Elements</a> collection) to set the colour to red. Useless, of course, but it illustrates the method simply.</p>

<div class="wp_syntax"><div class="code"><pre class="javascript javascript" style="font-family:monospace;">Element.<span style="color: #660066;">implement</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#123;</span>
    makered<span style="color: #339933;">:</span> <span style="color: #003366; font-weight: bold;">function</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span> <span style="color: #009900;">&#123;</span>
        <span style="color: #000066; font-weight: bold;">this</span>.<span style="color: #660066;">setStyle</span><span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'color'</span><span style="color: #339933;">,</span> <span style="color: #3366CC;">'red'</span><span style="color: #009900;">&#41;</span>;
        <span style="color: #000066; font-weight: bold;">return</span> <span style="color: #000066; font-weight: bold;">this</span>;
    <span style="color: #009900;">&#125;</span>
<span style="color: #009900;">&#125;</span><span style="color: #009900;">&#41;</span>;</pre></div></div>

<p>And it&#8217;s as easy as that. To use this world changing functionality (make my links red!), just use the standard Mootools selector syntax. This example will set the colour of all anchor elements in the page to red.</p>

<div class="wp_syntax"><div class="code"><pre class="javascript javascript" style="font-family:monospace;">$$<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'a'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">makered</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;</pre></div></div>

<p>Note that our <em>makered()</em> method must (or rather should) return <em>this</em> in order to maintain the Mootools method chaining functionality.</p>
<p>The following example illustrates the chaining. It calls our custom Element method (making all links red) and then fades them all out:</p>

<div class="wp_syntax"><div class="code"><pre class="javascript javascript" style="font-family:monospace;">$$<span style="color: #009900;">&#40;</span><span style="color: #3366CC;">'a'</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">makered</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>.<span style="color: #660066;">fade</span><span style="color: #009900;">&#40;</span><span style="color: #009900;">&#41;</span>;</pre></div></div>

<p>Extend away!</p>
]]></content:encoded>
			<wfw:commentRss>http://2tap.com/2009/01/11/extending-dom-elements-in-mootools-ala-jquery/feed/</wfw:commentRss>
		</item>
		<item>
		<title>JSFractal - JavaScript Fractal Explorer</title>
		<link>http://2tap.com/2008/12/18/jsfractal-javascript-fractal-explorer/</link>
		<comments>http://2tap.com/2008/12/18/jsfractal-javascript-fractal-explorer/#comments</comments>
		<pubDate>Thu, 18 Dec 2008 00:20:29 +0000</pubDate>
		<dc:creator>Russ</dc:creator>
		
		<category><![CDATA[JavaScript]]></category>

		<category><![CDATA[MooTools]]></category>

		<category><![CDATA[Projects]]></category>

		<guid isPermaLink="false">http://2tap.com/?p=48</guid>
		<description><![CDATA[

Have a play
You can try out JSFractal here.
What is it?
JSFractal is a web-based tool (written entirely client side in JavaScript) to allow you to explore fractals. Currently, only the Mandelbrot set is implemented but I hope to add support for switching to Julia sets and other types in the future.
You can drag-select on the fractal [...]]]></description>
			<content:encoded><![CDATA[<p></p>
<div><a href="http://2tap.com/jsfractal/"><img class="size-full wp-image-33" title="JSFractal" src="http://2tap.com/blog/wp-content/uploads/2008/12/jsfractal1.png" alt="JSFractal Screenshot" width="400" height="221" /></a></div>
<h3>Have a play</h3>
<p>You can <a href="http://2tap.com/jsfractal/">try out JSFractal here</a>.</p>
<h3>What is it?</h3>
<p>JSFractal is a web-based tool (written entirely client side in JavaScript) to allow you to explore fractals. Currently, only the <a href="http://en.wikipedia.org/wiki/Mandelbrot_set">Mandelbrot set</a> is implemented but I hope to add support for switching to <a href="http://en.wikipedia.org/wiki/Julia_set">Julia sets</a> and other types in the future.</p>
<p>You can drag-select on the fractal to choose an area to zoom in to. As you progress, you can see a <em>timeline</em> of previous points of the fractal, allowing you to switch to any prior state and continue in a different direction.</p>
<p>As well as allowing you to see previous states of the fractal, the timeline also supports a <em>playback</em> feature so you can watch the entire transition you&#8217;ve created zoom from start to finish.</p>
<p>At any point you can change the quality settings and size of the rendered fractal. Please note however, the higher the quality and the larger the fractal size - the longer you&#8217;ll wait! Currently there is only one colour scheme to choose from. </p>
<p>Finally, you can bookmark the page at any point. Returning to the URL will render the fractal that was showing at the point you bookmarked the page.</p>
<p><a href="http://2tap.com/jsfractal/">Have a go!</a>.</p>
<h3>Why?</h3>
<p>With the current JavaScript performance arms race going on between V8, Webkit, Mozilla and Opera, I wanted something fun to write that would really push the browsers to the limits. </p>
<p>After picking up James Gleick&#8217;s <em>Chaos</em> again, my interest with fractals was renewed and hence this project. Obviously this isn&#8217;t the best medium for something as computationally heavy as a fractal exploration tool - but what the hell. It&#8217;s a good experiment!</p>
<p>On a reasonably spec&#8217;d machine and one of the pre-release ultra-mega-crazy-fast JavaScript engines it&#8217;s actually pretty usable. There&#8217;s a few tricks used along the way to boost performance which I&#8217;ll go into later.</p>
<h3>How does it work?</h3>
<h4>Overview</h4>
<p>JSFractal is written entirely in JavaScript with no server side components. It uses MooTools 1.2 and the canvas element. Thus, Internet Explorer need not apply. Sorry! I attempted to patch in support with <a href="http://excanvas.sourceforge.net/">Google&#8217;s ExplorerCanvas</a> but it was so ridiculously slow that I had to drop it.</p>
<h4>Compatibility</h4>
<p>It has been tested and is thought to work in the following browsers:</p>
<ul>
<li>Mozilla Firefox 1.5+</li>
<li>Safari 3+</li>
<li>Opera 9</li>
<li>Google Chrome</li>
</ul>
<p>For reference, best performance is currently with Firefox 3.1 beta (with <a href="http://www.tech-recipes.com/rx/3381/firefox-31-how-to-enable-the-tracemonkey-javascript-engine/">JIT JavaScript enabled</a>) and Google Chrome.</p>
<h4>Rendering techniques</h4>
<p>Every time a fractal is generated, each pixel&#8217;s value needs to be calculated. On top of this, each pixel needs to be drawn individually. This means the key to fast performance is:</p>
<ul>
<li>Efficient calculations for each pixel</li>
<li>Fast drawing to the canvas</li>
</ul>
<p>I refined the fractal calculations as much as possible and had it running pretty fast. However, the actual rendering to the canvas proved to be a little trickier to speed up.</p>
<p>Initially I implemented rendering using the <strong>fillRect</strong> method of the canvas object to draw each pixel as a 1&#215;1 rectangle. This &#8230; is &#8230; unsurprisingly &#8230; slow. There is a lot of overhead in setting up and executing each call (setting fillColor etc.). This meant, particularly on the faster JS engines, the bottleneck was primarily the rendering.</p>
<p>I love <strong>createImage()</strong>. This is a canvas method implemented in Firefox 3+ (and recent Webkit nightlies I believe) which returns an updateable pixel buffer object. This means we can call it once at the beginning of a render to retrieve access to the pixel buffer and write RGBA values directly (it&#8217;s implemented, effectively, as a single dimensional array so access is fast). It can then simply be redrawn back to the canvas at the end. Fast!</p>
<p>The above is great, but not all the current canvas supporting browsers have this method. The spec for it is still somewhat in the air. There&#8217;s debates as to quite what it should return - the number of pixels does not always equal the canvas size etc. (at least, in theory). In effect, this means that Chrome and Opera are both missing this method which means the only option is fillRect.</p>
<p>It seemed a great shame that Chrome, despite having a very fast JS engine, was lagging behind Firefox merely because of the awful fillRect() method of rendering.</p>
<p>After posting a comment to an <a href="http://ajaxian.com/archives/jsspeccy-a-zx-spectrum-emulator-in-javascript#comments">unrelated article on a Spectrum Emulator</a> on <a href="http://ajaxian.com">Ajaxian</a>, <em>cromwellian</em> suggested the possibility of constructing DataURLs manually as an alternative.</p>
<h4>DataURLs?</h4>
<p>Using DataURLs is actually crazy enough to work. And, even better, it&#8217;s pretty fast. It effectively builds up a bitmap image file on the fly, draws it to an Image object and finally draws the loaded Image to the canvas. The great thing about this technique is that it&#8217;s shifting the performance burden from the canvas element to the JavaScript engine. And since the current crop of browsers are getting pretty <em>rapid</em> in this area, it isn&#8217;t too much of a performance hit.</p>
<p>Measurements in Firefox 3.1b showed only about a 40% hit for using the DataURL method of rendering compared to writing directly to the pixel buffer. This sounds a lot but compared to fillRect() it&#8217;s a great improvement.</p>
<p>All three rendering methods are included in JSFractal (the most appropriate is chosen dependent on your browser features). Note: The fillRect() implementation is never actually used since the only browser than I know of that would require this is Internet Explorer.</p>
<h3>Issues</h3>
<p>There are a few issues I&#8217;d like to address which I&#8217;ll go into here.</p>
<h4>Colours</h4>
<p>Currently there is only one colour scheme available. What&#8217;s more, it&#8217;s not the most optimal algorithm for producing the prettiest fractals. I&#8217;d like to improve this - it&#8217;s more evident the deeper into the fractal you go (differentiations in colour become less, leading to large blocks of the same colour). Best results happen when the quality level is whacked up high and the size is set to the maximum. </p>
<h4>Changing settings and the timeline</h4>
<p>Changing the settings (size, quality, colours) half way through a fractal exploration will only update the current fractal. The timeline playback etc. will still function but previous fractal states may be stretched to fit meaning the animation may change in quality during playback.</p>
<p>There&#8217;s not a lot I can do about this, however, since I&#8217;d need to re-render all previous fractals which would be an unacceptable performance hit. I think the current solution of allowing you to change settings mid-way through and doing its best to compensate for the change is the current best compromise.</p>
<h3>Credits</h3>
<ul>
<li>Parts of the bitmap and base64 encoding methods adapted from <a href="http://neil.fraser.name/software/bmp_lib/">Neil Fraser&#8217;s JavaScript BMP library</a>.</li>
<li><em>cromwellian</em> for his DataURL idea.</li>
<li>Graphics heavily inspired by <em>kailoon&#8217;s</em> <a href="http://themetation.com/2008/07/14/how-to-create-wordpress-themes-from-scratch-part-1/">Photoshop tutorial</a>.
</li>
</ul>
<h3>Over and out</h3>
<p>Any feedback, bug reports or ideas for improvement very much appreciated.</p>
]]></content:encoded>
			<wfw:commentRss>http://2tap.com/2008/12/18/jsfractal-javascript-fractal-explorer/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Tap Trap - The year of procrastination</title>
		<link>http://2tap.com/2008/12/10/tap-trap-the-year-of-procrastination/</link>
		<comments>http://2tap.com/2008/12/10/tap-trap-the-year-of-procrastination/#comments</comments>
		<pubDate>Wed, 10 Dec 2008 22:54:29 +0000</pubDate>
		<dc:creator>Russ</dc:creator>
		
		<category><![CDATA[JavaScript]]></category>

		<category><![CDATA[MooTools]]></category>

		<category><![CDATA[Projects]]></category>

		<category><![CDATA[Tap Trap]]></category>

		<category><![CDATA[Ubuntu]]></category>

		<category><![CDATA[Uncategorised]]></category>

		<guid isPermaLink="false">http://2tap.com/?p=16</guid>
		<description><![CDATA[Since launching Tap Trap, there have been a steady stream of players stumbling across the game and, in some cases, getting hooked!
I wrote the game a couple of years ago now (mostly as an experiment with JavaScript) and didn&#8217;t really plan to promote it particularly but it&#8217;s great to see around 15 new players a [...]]]></description>
			<content:encoded><![CDATA[<p>Since launching <a title="Tap Trap" href="http://2tap.com/taptrap">Tap Trap</a>, there have been a steady stream of players stumbling across the game and, in some cases, getting hooked!</p>
<p>I wrote the game a couple of years ago now (mostly as an experiment with JavaScript) and didn&#8217;t really plan to promote it particularly but it&#8217;s great to see around 15 new players a day having a go!</p>
<p>It recently passed the 500,000th game played - around half of those games were actually completed (a score was submitted). From a quick scribble on the back of a Topman receipt (taking a rough average of 2 minutes per game) this means there&#8217;s been about 1 man year of <span style="text-decoration: line-through;">procrastination</span> play so far.</p>
<p>Well done to <span class="page-text"><em>Joe Bloggs 6140 </em>for the current high score of 5,510<em>. </em></span>The average score is <span class="page-text">1981.98 across all the games completed.</span></p>
<p><span class="page-text">The booby prize goes to </span><span class="page-text"><em>Joe Bloggs 2681 </em>for achieving the lowest score of just 152. It&#8217;s actually quite a feat to achieve such an abysmal score - seriously, try it. It&#8217;s not as easy as it looks!</span></p>
]]></content:encoded>
			<wfw:commentRss>http://2tap.com/2008/12/10/tap-trap-the-year-of-procrastination/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Ubuntu on the Asus Eeepc 901/1000/1000h</title>
		<link>http://2tap.com/2008/08/12/ubuntu-on-the-asus-eeepc-90110001000h/</link>
		<comments>http://2tap.com/2008/08/12/ubuntu-on-the-asus-eeepc-90110001000h/#comments</comments>
		<pubDate>Mon, 11 Aug 2008 23:05:14 +0000</pubDate>
		<dc:creator>Russ</dc:creator>
		
		<category><![CDATA[Linux]]></category>

		<category><![CDATA[Ubuntu]]></category>

		<category><![CDATA[eeepc]]></category>

		<guid isPermaLink="false">http://2tap.com/?p=14</guid>
		<description><![CDATA[Useful custom kernel (including working wireless drivers) available from &#8220;adamm&#8221;&#8217;s repository here: http://www.array.org/ubuntu/
At the time of writing not everything&#8217;s fully worked out (issues with the headphone socket etc.) but it&#8217;s a good way to get the stock Hardy 8.04 install functional and on the net.
For the most basic install (to get wireless working at least) [...]]]></description>
			<content:encoded><![CDATA[<p>Useful custom kernel (including working wireless drivers) available from &#8220;adamm&#8221;&#8217;s repository here: <a href="http://www.array.org/ubuntu/">http://www.array.org/ubuntu/</a></p>
<p>At the time of writing not <em>everything&#8217;s </em>fully worked out (issues with the headphone socket etc.) but it&#8217;s a good way to get the stock Hardy 8.04 install functional and on the net.</p>
<p>For the most basic install (to get wireless working at least) you can just <a href="http://www.array.org/ubuntu/setup901.html">copy a couple of debs</a> onto a usb stick and &#8220;dpkg -i *&#8221; install them before getting the rest of the updates via the net repository.</p>
<p>Up to date discussion of progress currently available in <a href="http://forum.eeeuser.com/viewtopic.php?id=32303">this thread</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://2tap.com/2008/08/12/ubuntu-on-the-asus-eeepc-90110001000h/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Parklife</title>
		<link>http://2tap.com/2007/11/03/parklife/</link>
		<comments>http://2tap.com/2007/11/03/parklife/#comments</comments>
		<pubDate>Sat, 03 Nov 2007 17:26:47 +0000</pubDate>
		<dc:creator>Russ</dc:creator>
		
		<category><![CDATA[Stupid Boy Projects]]></category>

		<guid isPermaLink="false">http://2tap.com/2007/11/04/parklife/</guid>
		<description><![CDATA[I&#8217;m not entirely sure why this exists, but here&#8217;s a 3 minute video of a trip between Hammersmith and Piccadilly Circus in London (playing at 20x speed). It&#8217;s roughly 4 miles of urban assault course carnage.

Not a lot of planning went into this one (read: no planning went into this one) so the production qualities [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m not entirely sure why this exists, but here&#8217;s a 3 minute video of a trip between Hammersmith and Piccadilly Circus in London (playing at 20x speed). It&#8217;s roughly 4 miles of urban assault course carnage.</p>
<p><object width="425" height="366"><param name="movie" value="http://www.youtube.com/v/b1xWkOM9jTY&#038;rel=1&#038;border=0"></param><param name="wmode" value="transparent"></param><embed src="http://www.youtube.com/v/b1xWkOM9jTY&#038;rel=1&#038;border=0" type="application/x-shockwave-flash" wmode="transparent" width="425" height="366"></embed></object></p>
<p>Not a lot of planning went into this one (read: no planning went into this one) so the production qualities may, unfortunately, rule this out for a BAFTA. That, and the plagiarised unlicensed soundtrack.</p>
<p>Here&#8217;s a <a href="http://2tap.com/stupidboyprojects/londonwalk/hammersmith_to_piccadilly.avi" title="Hammersmith to Piccadilly Circus in 3 minutes" target="_blank">better quality version</a> encoded in DivX (right click/save as to download - roughly 21mb).</p>
<p>Those with epilepsy should probably give this one a miss!</p>
]]></content:encoded>
			<wfw:commentRss>http://2tap.com/2007/11/03/parklife/feed/</wfw:commentRss>
<enclosure url="http://2tap.com/stupidboyprojects/londonwalk/hammersmith_to_piccadilly.avi" length="22298034" type="video/x-msvideo" />
		</item>
		<item>
		<title>Sharing files between a Windows guest and Ubuntu host using VMware and Samba</title>
		<link>http://2tap.com/2007/04/22/sharing-files-between-a-windows-guest-and-ubuntu-host-using-vmware-and-samba/</link>
		<comments>http://2tap.com/2007/04/22/sharing-files-between-a-windows-guest-and-ubuntu-host-using-vmware-and-samba/#comments</comments>
		<pubDate>Sun, 22 Apr 2007 06:31:15 +0000</pubDate>
		<dc:creator>Russ</dc:creator>
		
		<category><![CDATA[Linux]]></category>

		<category><![CDATA[Ubuntu]]></category>

		<guid isPermaLink="false">http://2tap.com/2007/04/22/sharing-files-between-a-windows-guest-and-ubuntu-host-using-vmware-and-samba/</guid>
		<description><![CDATA[VMware Workstation (and presumably the other enterprise-grade products in the VMware family) come with the handy &#8220;shared folders&#8221; feature which makes sharing files between a host and a virtual appliance nice and simple. The free products (VMware Player and Server) do not, unfortunately, have this ability and so we must find another way.
This quick guide [...]]]></description>
			<content:encoded><![CDATA[<p>VMware Workstation (and presumably the other enterprise-grade products in the VMware family) come with the handy &#8220;shared folders&#8221; feature which makes sharing files between a host and a virtual appliance nice and simple. The free products (VMware Player and Server) do not, unfortunately, have this ability and so we must find another way.</p>
<p>This quick guide shows how to use <a href="http://www.samba.org" title="Samba">Samba</a> to achieve the same aim. It is aimed at Ubuntu users but (the general concepts at least) should work on any modern Linux distribution. It is also written with a Windows XP guest in mind but a similar process should work in Windows Vista, Windows 2000 and other operating systems.</p>
<p>The goal is to set up a network share which both operating systems can transparently read and write to.</p>
<p>For reference, I am using Ubuntu 7.04 (Feisty).</p>
<h3>Which VMware?</h3>
<p>I&#8217;ll presume you have VMWare already installed with a Windows XP guest virtual appliance already set up. This guide is aimed at users of VMware Player and Server editions (I am using VMware Player).</p>
<p>VMware Player is a simple:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #c20cb9; font-weight: bold;">apt-get</span> <span style="color: #c20cb9; font-weight: bold;">install</span> vmware-player</pre></div></div>

<p>away. For the Server edition, you&#8217;ll probably want to <a href="http://ubuntuguide.org/wiki/Ubuntu:Feisty#How_to_install_Windows_Applications_.28VMWare_Server_or_Workstation.29" alt="VMware Server guide">consult the wiki</a>.</p>
<h3>Install Samba</h3>
<p>If you don&#8217;t already have Samba installed, now would be a good time to do it:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #c20cb9; font-weight: bold;">apt-get</span> <span style="color: #c20cb9; font-weight: bold;">install</span> samba</pre></div></div>

<p>In order to keeps things clean and easy to manage, we&#8217;ll set up a new user account to own the share. This account name will be used when connecting to the share from within Windows. For the purposes of illustration, I will be creating a share called <em>sandbox</em> with the username and group also being <em>sandbox</em>.</p>
<p>Create the new group and user account with no login privileges:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> groupadd sandbox
<span style="color: #c20cb9; font-weight: bold;">sudo</span> useradd <span style="color: #660033;">--gid</span> sandbox <span style="color: #660033;">--shell</span> <span style="color: #000000; font-weight: bold;">/</span>bin<span style="color: #000000; font-weight: bold;">/</span><span style="color: #c20cb9; font-weight: bold;">false</span> sandbox</pre></div></div>

<p>To avoid creating a redundant home directory, you can add:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #660033;">--home</span> <span style="color: #000000; font-weight: bold;">/</span>nonexistent</pre></div></div>

<p>to the end of the previous command.</p>
<p>Now you need to add a matching Samba account. You&#8217;ll be prompted to set a password - make note of this as this is what you will use to connect to the share from within Windows.</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> smbpasswd <span style="color: #660033;">-a</span> sandbox</pre></div></div>

<p>Next you&#8217;ll need to create a directory to be used as the share (assuming you don&#8217;t already have one). Create a directory, setting the username to your usual login and group to <em>sandbox</em>. Then <strong>chmod</strong> the directory 775 (assuming you wish both yourself and the virtual appliance to have read/write access). Here is what I entered:</p>

<div class="wp_syntax"><div class="code"><pre class="text text" style="font-family:monospace;">cd $HOME
mkdir sandbox
sudo chown russ:sandbox sandbox
sudo chmod 775 sandbox</pre></div></div>

<p>When you write to the share from within Ubuntu, new files will be created with the default permissions 644 with the username and group being your own user account. When your Windows client connects to the share, it will access it as if it were the local system user <em>sandbox</em> and so the group permissions will apply and you won&#8217;t be able to write to any files created from within Ubuntu.</p>
<p>To get around this problem, we can set the groupid bit for the <em>sandbox</em> directory which means all new files created will inherit the permissions of the parent and so the <em>sandbox</em> user from within Windows will be able to make read and write changes as desired.</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #c20cb9; font-weight: bold;">chmod</span> g+s sandbox</pre></div></div>

<p>If you don&#8217;t understand the above, don&#8217;t worry, just chmod the directory with the command above and all should be well.</p>
<h3>Setting up the Samba share</h3>
<p>Now all that&#8217;s left to do is to tell Samba about our share. Open up <em>/etc/samba/smb.conf</em> in your favourite text editor.</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> gedit <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>samba<span style="color: #000000; font-weight: bold;">/</span>smb.conf</pre></div></div>

<p>Firstly, we need to set the security mechanism to <em>user</em>. Look for the line:</p>

<div class="wp_syntax"><div class="code"><pre class="text text" style="font-family:monospace;">security = user</pre></div></div>

<p>and make sure it is uncommented (remove the preceding semicolon if there is one).</p>
<p>Now, scroll down to the <em>Share Definitions</em> section and add the following:</p>

<div class="wp_syntax"><div class="code"><pre class="text text" style="font-family:monospace;">[sandbox]
path = /home/russ/sandbox
valid users = sandbox
read only = No
create mask = 0777
directory mask = 0777</pre></div></div>

<p>Be sure to set the correct path to your share. Save the file and restart the Samba daemon:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>init.d<span style="color: #000000; font-weight: bold;">/</span>samba restart</pre></div></div>

<p>That should be it. You should now be able to connect to your share from within the Windows guest. At this point you need to know what IP address to connect to from within Windows. This depends on what networking mode you are using for your virtual appliance.</p>
<h4>Bridged Networking</h4>
<p>In this mode, your guest OS has its own IP address and so the address it needs to connect to is your usual host machine&#8217;s address. In this case your address is probably the top line from the output of this command:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">ifconfig</span> | <span style="color: #c20cb9; font-weight: bold;">grep</span> <span style="color: #ff0000;">&quot;inet addr:&quot;</span></pre></div></div>

<h4>NAT networking</h4>
<p>In this mode, your guest OS shares your host&#8217;s address (in terms of other machines on the LAN) and communicates with the host via a private network. In this case, the IP address you need to connect to is most likely the bottom one from the output of this command:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">ifconfig</span> | <span style="color: #c20cb9; font-weight: bold;">grep</span> <span style="color: #ff0000;">&quot;inet addr:&quot;</span></pre></div></div>

<h3>Connecting to the share from within Windows</h3>
<p>If you are unsure as to your host&#8217;s IP address, try and ping it first from within the Windows guest to confirm you have the right one.</p>
<p><img src="http://2tap.com/blog/wp-content/uploads/2007/04/vmware-windows-guest.gif" alt="Windows Map Networking Drive dialog" /></p>
<p>Having worked out what IP address to connect to, you should now be able to connect to your share from within Windows.</p>
<p>The easiest way to do this is:</p>
<ol>
<li>Open up <strong>My Computer</strong></li>
<li>Go to the <em>Tools</em> menu and then <em>Map Network Drive</em></li>
<li>Choose a drive letter to map the network share to</li>
<li>In <em>Folder</em>, enter: \\<strong>HOSTIP</strong>\sandbox (replacing <strong>HOSTIP</strong>)</li>
<li>Click &#8220;Connect using a different user name&#8221; and enter:
<ul>
<li>username: sandbox</li>
<li>password: <strong>yourpassword</strong></li>
</ul>
</li>
<li>Click <em>OK</em> and then the <em>Finish</em> button to connect</li>
</ol>
<p>Hopefully, congratulations are in order. If not, be sure to make sure that any firewalls you have running (host or guest) have the correct rules set to allow communication between the two systems.</p>
<h3>A note on security</h3>
<p>At this point, assuming you have a successful connection, it is worth noting that any other machine on your local network (and potentially the internet if you are not behind a NAT or firewall) can connect to your share (assuming they have the correct credentials).</p>
<p>If you are only using Samba for sharing with VMware (as I am), you may wish to restrict access to VMware only. This is quite easy to do since VMware creates virtual network interfaces for communication between hosts and guests. This means we can set Samba up to ignore any communications that do not originate from these interfaces.</p>
<p>To do this, open up your Samba configuration file again:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> gedit <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>samba<span style="color: #000000; font-weight: bold;">/</span>smb.conf</pre></div></div>

<p>Make sure you have a:</p>

<div class="wp_syntax"><div class="code"><pre class="text text" style="font-family:monospace;">bind interfaces only = true</pre></div></div>

<p>line and that it is uncommented (remove any preceding semicolons). Just above this should be an <em>interfaces</em> line (most likely commented out). Add the following just below this:</p>

<div class="wp_syntax"><div class="code"><pre class="text text" style="font-family:monospace;">interfaces = vmnet0 vmnet1 vmnet8</pre></div></div>

<p>These are the virtual interfaces VMware uses for each type of virtual networking: bridged, host only and NAT respectively.</p>
<p>After making the changes, you will need to restart Samba again:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>init.d<span style="color: #000000; font-weight: bold;">/</span>samba restart</pre></div></div>

<p>and possibly shutdown your VMware session and restart the VMware service:</p>
<p>VMware Player:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>init.d<span style="color: #000000; font-weight: bold;">/</span>vmware-player restart</pre></div></div>

<p>VMware Server:</p>

<div class="wp_syntax"><div class="code"><pre class="bash bash" style="font-family:monospace;"><span style="color: #c20cb9; font-weight: bold;">sudo</span> <span style="color: #000000; font-weight: bold;">/</span>etc<span style="color: #000000; font-weight: bold;">/</span>init.d<span style="color: #000000; font-weight: bold;">/</span>vmware restart</pre></div></div>

<h3>Finished</h3>
<p>You should now have a Samba share configured which is only accessible from your VMware guest appliances. Good luck!</p>
]]></content:encoded>
			<wfw:commentRss>http://2tap.com/2007/04/22/sharing-files-between-a-windows-guest-and-ubuntu-host-using-vmware-and-samba/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Tap Trap 1.1 - New features</title>
		<link>http://2tap.com/2006/10/10/tap-trap-11-new-features/</link>
		<comments>http://2tap.com/2006/10/10/tap-trap-11-new-features/#comments</comments>
		<pubDate>Tue, 10 Oct 2006 22:26:08 +0000</pubDate>
		<dc:creator>Russ</dc:creator>
		
		<category><![CDATA[Projects]]></category>

		<category><![CDATA[Tap Trap]]></category>

		<guid isPermaLink="false">http://2tap.com/2006/10/10/tap-trap-11-new-features/</guid>
		<description><![CDATA[The latest version of &#8220;Tap Trap&#8221; is now live.
New features include: 

More high scores shown (top 30 now rather than the original top 5)
&#8216;Replay&#8217; feature - watch any game currently in the top 30
New theme &#8220;alternate&#8221; - it&#8217;s ugly, but it might help people who have trouble with red/blue/green

]]></description>
			<content:encoded><![CDATA[<p>The latest version of <a href="http://2tap.com/taptrap" title="Tap Trap game">&#8220;Tap Trap&#8221;</a> is now live.</p>
<p><strong>New features include: </strong></p>
<ul>
<li>More high scores shown (top 30 now rather than the original top 5)</li>
<li>&#8216;Replay&#8217; feature - watch any game currently in the top 30</li>
<li>New theme &#8220;alternate&#8221; - it&#8217;s ugly, but it might help people who have trouble with red/blue/green</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://2tap.com/2006/10/10/tap-trap-11-new-features/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
