<?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/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Weevil Genius</title>
	<atom:link href="http://weevilgenius.net/feed/" rel="self" type="application/rss+xml" />
	<link>http://weevilgenius.net</link>
	<description>I&#039;m *your* sort of raving lunatic</description>
	<lastBuildDate>Tue, 13 Dec 2011 18:23:31 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Daily Work Log in OS X</title>
		<link>http://weevilgenius.net/2011/12/daily-work-log-in-os-x/</link>
		<comments>http://weevilgenius.net/2011/12/daily-work-log-in-os-x/#comments</comments>
		<pubDate>Mon, 12 Dec 2011 22:46:27 +0000</pubDate>
		<dc:creator>Barry Stump</dc:creator>
				<category><![CDATA[OS X]]></category>
		<category><![CDATA[vim]]></category>
		<category><![CDATA[apple]]></category>
		<category><![CDATA[command line]]></category>

		<guid isPermaLink="false">http://weevilgenius.net/?p=49</guid>
		<description><![CDATA[Recent juggling of multiple projects has created a need to track what I&#8217;ve been doing day by day, as well as keep some notes on what I want to be doing. I have thus revived a system I created long ago to edit a weekly log file in my favorite editor (vim) via a keyboard [...]]]></description>
			<content:encoded><![CDATA[<p>Recent juggling of multiple projects has created a need to track what I&#8217;ve been
doing day by day, as well as keep some notes on what I want to be doing. I have
thus revived a system I created long ago to edit a weekly log file in my
favorite editor (vim) via a keyboard shortcut.  As I&#8217;m currently working mostly
in OS X, this required some modifications from the Linux version I used in the
past. The desired action is relatively simple.  I press a hotkey combination and a
text editor window opens with the current week&#8217;s log file, which has been
created on the fly if it didn&#8217;t already exist.</p>

<span id="more-49"></span>

<p><strong>Update:</strong> I updated the script with <code>--remote-silent and --servername</code> based
on a suggestion in the comments.</p>

<h1>Basic Script</h1>

<p>The first thing to decide is location and naming convention for the files.  As
there is going to be one file per week, more or less indefinitely, I&#8217;m going
to need some form of date in the file name so that when listed alphabetically,
the files will also be sorted chronologically. For location, I&#8217;m using a folder
inside <a href="http://www.dropbox.com/">Dropbox</a> which is an easy way for the notes to be automatically backed
up and synced to my various computers.</p>

<p>I would like to name each file <code>log_yyyy-mm-dd.txt</code> where <code>yyyy-mm-dd</code> is the
date of the first weekday (Monday) for that week, but the difficulty is that
the script needs to be able to create a new, properly named file when run on
any day of the week, not just on Monday.  OS X ships with the BSD version of
the <code>date</code> command, which allows date manipulation via the <code>-v</code> parameter.</p>

<pre class="brush: plain; light: true; title: ; notranslate">
date -v-monday +log_%Y-%m-%d.txt
</pre>

<p>This will take the current date and rewind to the most recent Monday
(<code>-v-monday</code>), then reformat that date as a string suitable for a file name.
I put the year first so that earlier dates will sort correctly before later
ones. You should enclose the format parameter (<code>+log_%Y-%m-%d.txt</code>) in quotes
if your format string includes spaces or any characters such as * or ? which the
shell would interpret incorrectly.</p>

<p>This is enough to write a basic launcher script:</p>

<pre class="brush: bash; title: ; notranslate">
#!/bin/bash -e

LOG_DIR=$HOME/Dropbox/work/worklog
LOG_FILE=$(date -v-monday '+log_%Y-%m-%d.txt')

gvim $LOG_DIR/$LOG_FILE</pre>

<p>If for some reason your favorite text editor is not MacVim, you can replace the
last line as necessary.  I save the file to <code>~/bin/edit_worklog.sh</code> and make it
executable. Upon running the script, I get a new MacVim window with the empty
file <code>~/Dropbox/work/worklog/log_2011-12-05.txt</code> open in it.  So far so good.</p>

<h1>Improvements</h1>

<p>Now I&#8217;d like to make some improvements. When I make my log entries, I like to
separate each day with a header line that includes the date, like this:</p>

<pre class="brush: plain; light: true; title: ; notranslate">
Monday, 12/05

 * did
 * some
 * stuff

Tuesday, 12/06

 * continued work on stuff
 * meeting with Mr. X

... etc
</pre>

<p>It would be nice if the launcher script could create the 5 weekday headers for
me when creating the new log file.  I already have the date for Monday for the
name of the file, but now I need dates for Tuesday through Friday.  These are
simply offset by one or more days from the Monday value. For this I can use
<code>-v+1d</code> to add days as needed.  If you are using GNU date, you can use something
like <code>-d'monday+1 days'</code>.  Here&#8217;s the whole week:</p>

<pre class="brush: bash; light: true; title: ; notranslate">
# prints Monday, 12/05
date -v-monday '+%A, %m/%d'

# prints Tuesday, 12/06
date -v-monday -v+1d '+%A, %m/%d'

# prints Wednesday, 12/07
date -v-monday -v+2d '+%A, %m/%d'

# prints Thursday, 12/08
date -v-monday -v+3d '+%A, %m/%d'

# prints Friday, 12/09
date -v-monday -v+4d '+%A, %m/%d'</pre>

<p>Now it would be nice to have the script open the file with the cursor position
at the end and to control the size of the editing window.  These can be
accomplished by adding <code>+</code> and <code>--cmd 'set lines=25 columns=60'</code> (respectively)
to the gvim command.</p>

<p>Lastly, if I run the script while the file is already open for editing, I&#8217;ll get
the usual vim warning about the swap file being already in use. For this
project, I would rather bring the existing editor window to the foreground.  For
this we can use vim&#8217;s remote editor capabilities. By adding <code>--remote-silent</code> we
ask vim to edit the file in an existing window (the remote part) and create a
new editor window if one doesn&#8217;t already exist (the silent part). We should also
add <code>--servername worklog</code> to give the remote window a name so that it doesn&#8217;t
replace any existing remote sessions.</p>

<p>Here&#8217;s my full script with all improvements and some handy comments.</p>

<pre class="brush: bash; title: ; notranslate">
#!/bin/bash
#
# edit_worklog.sh - script to edit (and create if necessary)
#                   a rotating weekly work log file
#
# Author: Barry Stump

# this is the path to the weekly log folder
LOG_DIR=&quot;$HOME/Dropbox/work/worklog&quot;

# the files are named by the first day of the week (most recent Monday)
LOG_FILE=&quot;$LOG_DIR/&quot;$(date -v-monday '+log_%Y-%m-%d.txt')

# does today's file not exist yet?
if [ ! -e $LOG_FILE ]; then

    # create new log file with a line for each weekday...
    # (this version is for the BSD date command that
    # comes with OS X. For GNU date, use something like
    # -d'monday+2 days')

    # Monday
    date -v-mon '+%A, %m/%d' &gt; $LOG_FILE
    echo &gt;&gt; $LOG_FILE
    # Tuesday
    date -v-mon -v+1d '+%A, %m/%d' &gt;&gt; $LOG_FILE
    echo &gt;&gt; $LOG_FILE
    # Wednesday
    date -v-mon -v+2d '+%A, %m/%d' &gt;&gt; $LOG_FILE
    echo &gt;&gt; $LOG_FILE
    # Thursday
    date -v-mon -v+3d '+%A, %m/%d' &gt;&gt; $LOG_FILE
    echo &gt;&gt; $LOG_FILE
    # Friday
    date -v-mon -v+4d '+%A, %m/%d' &gt;&gt; $LOG_FILE
    echo &gt;&gt; $LOG_FILE

fi

# open up the file
# --cmd &quot;set lines=25 columns=60&quot;
#       (resize window to 60x25)
#
# +     (move cursor to end of file)
#
# --servername worklog --remote-silent
#       (bring window to foreground if already open)
#
gvim --cmd &quot;set lines=25 columns=60&quot; + \
     --servername worklog --remote-silent $LOG_FILE</pre>

<h1>Keyboard Shortcut</h1>

<p>Now that I have a handy script to launch my work log, I would like to bind it
to a hotkey combination.  In OS X, I accomplished this by using the Automator
utility to create a service workflow and then binding a hotkey to it.  These
directions are for OS X 10.6 Snow Leopard.  Older or newer versions of OS X
may require some tweaks.</p>

<p>First, open Automator (from the Applications folder) and choose the Service
template in the dialog that appears.</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2011/12/daily_worklog_service.png" alt="Service Template" title="Service Template" /></p>

<p>For &#8220;Service receives selected&#8221; choose &#8220;no input&#8221;.  Now drag &#8220;Run Shell Script&#8221;
from the library listing on the left to the empty section on the right.  Replace
the default command <code>cat</code> with the path and name of the saved script from above.
Here I&#8217;m using <code>$HOME/bin/edit_worklog.sh</code>.</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2011/12/daily_worklog_service_2.png" alt="Run Shell Script" title="Run Shell Script" /></p>

<p>When done, save the workflow with ⌘S and enter a name in the popup. If you ever
need to edit the workflow again later, it is located at ~/Library/Services. I
called my workflow &#8220;View Worklog&#8221;. This adds an entry &#8220;View Worklog&#8221; to the
Services menu of every application. The final step is to associate a system-wide
keyboard shortcut to this service.  Open System Preferences, choose Keyboard,
and then click the Keyboard Shortcuts pane.  Click &#8220;Services&#8221; from the category
list on the left, then scroll down to &#8220;General&#8221;.  Double click to the right of
your newly created service name to assign a keyboard shortcut.  I used ⇧⌘&#96;
which appears to be unused on my system.</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2011/12/daily_worklog_keyboard_shortcut.png" alt="Keyboard Shortcut" title="Keyboard Shortcut" /></p>

<p>Close System Preferences to save the settings, and you should be able to pull up
the weekly log window just by using the new keyboard shortcut combination.</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2011/12/daily_worklog_screenshot.png" alt="Work Log Screenshot" title="Work Log Screenshot" /></p>
]]></content:encoded>
			<wfw:commentRss>http://weevilgenius.net/2011/12/daily-work-log-in-os-x/feed/</wfw:commentRss>
		<slash:comments>4</slash:comments>
		</item>
		<item>
		<title>HTML 5 &#8211; Websocket Chat Demo</title>
		<link>http://weevilgenius.net/2010/10/html-5-websocket-chat-demo/</link>
		<comments>http://weevilgenius.net/2010/10/html-5-websocket-chat-demo/#comments</comments>
		<pubDate>Tue, 26 Oct 2010 05:12:51 +0000</pubDate>
		<dc:creator>Barry Stump</dc:creator>
				<category><![CDATA[HTML 5]]></category>
		<category><![CDATA[How To]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[Javascript]]></category>
		<category><![CDATA[PHP]]></category>

		<guid isPermaLink="false">http://weevilgenius.net/?p=48</guid>
		<description><![CDATA[The HTML 5 initiative contains a draft spec for WebSockets which allow a browser to establish a full-duplex, bi-directional communication channel over a single TCP socket. This allows web developers to establish real time two way communications with a server using simple javascript without resorting to Flash, Java, ajax long polling, comet, forever iframe, or [...]]]></description>
			<content:encoded><![CDATA[<p>The HTML 5 initiative contains a draft spec for WebSockets which allow a browser
to establish a full-duplex, bi-directional communication channel over a single
TCP socket.  This allows web developers to establish real time two way
communications with a server using simple javascript without resorting to Flash,
Java, ajax long polling, comet, forever iframe, or other current workarounds.
Here I&#8217;ve written a simple browser chat using WebSockets for the client and
PHP for the server.</p>

<span id="more-48"></span>

<h1>WebSocket Handshake</h1>

<p>To establish a WebSocket connection, the browser makes a HTTP GET request to the
server with an offer to upgrade to WebSocket.  In the current
<a href="http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76">websocket spec</a>, that request looks like this:</p>

<p><strong>Client Request</strong></p>

<pre class="brush: plain; light: true; title: ; notranslate">
GET /resource HTTP/1.1
Upgrade: WebSocket
Connection: Upgrade
Host: example.com:8080
Origin: http://www.example.com
Sec-WebSocket-Key1: W  1529l9 6i4b{{a 9 z55
Sec-WebSocket-Key2: F13 O05   q  q6 - 4k7D   97

-#&amp;nD[fU
</pre>

<p>The first line is a HTTP/1.1 GET request.  The next two lines offer to upgrade
the connection to the WebSocket protocol.  The Host header lists the server name
and port the client wishes to connect to (necessary when the server hosts
multiple domains on the same IP address).  The Origin header indicates where the
request originates from, not unlike the HTTP Referer header.  The last two
headers and the 8 bytes at the end are random tokens which the server will use
to construct a challenge response.</p>

<p>If the server accepts the upgrade offer (based on the resource, host, and
origin), it returns a response like this:</p>

<p><strong>Server Response</strong></p>

<pre class="brush: plain; light: true; title: ; notranslate">
HTTP/1.1 101 Web Socket Protocol Handshake
Upgrade: WebSocket
Connection: Upgrade
Sec-WebSocket-Origin: http://www.example.com
Sec-WebSocket-Location: ws://example.com:8080/resource

8jKS'y:G*Co,Wxa-
</pre>

<p>The first three lines indicate that the server is upgrading the connection to
the WebSocket protocol.  The Sec-WebSocket-Origin header echoes the value the
client sent, and the Sec-WebSocket-Location header returns the full URI for the
websocket connection.  The 16 bytes at the end form the challenge response.  It
is calculated by extracting the numeric digits from the client's first
Sec-WebSocket-Key header, concatenating them to form a number, and dividing by
the number of spaces.  The same process is done with the second client key, the
two numbers are then concatenated together along with the final 8 bytes of the
client request, and the MD5 sum of the result becomes the server challenge
response.  Whew.</p>

<p>Here's a snippet from the server code which calculates the security response:</p>

<pre class="brush: php; first-line: 137; title: ; notranslate">
private function handleSecurityKey($key) {
    // extract the numeric digits to form a number
    preg_match_all('/[0-9]/', $key, $number);
    // extract the number of spaces
    preg_match_all('/ /', $key, $space);
    if ($number &amp;&amp; $space) {
        // perform the calculation per the websocket spec
        return implode('', $number[0]) / count($space[0]);
    }
    return '';
}

private function getHandshakeSecurityKey($key1, $key2, $code) {
    return md5(
        pack('N', $this-&gt;handleSecurityKey($key1)).
        pack('N', $this-&gt;handleSecurityKey($key2)).
        $code,
        true
    );
}</pre>

<p>At any point during this handshake process, if the client or the server does not
receive the expected response, it will refuse the upgrade simply by closing the
connection.</p>

<h1>WebSocket API</h1>

<p>Inside a compatible browser, you create and interact with WebSockets via a
WebSocket object.  According to the <a href="http://dev.w3.org/html5/websockets/">WebSocket API</a>, creating a new WebSocket
object causes the browser to attempt a connection to the server.  The server
address is specified in the same manner as a HTTP URI except &#8220;ws&#8221; replaces
&#8220;http&#8221; and &#8220;wss&#8221; replaces &#8220;https&#8221;.  Once created, the WebSocket object exposes
four callback functions for handling events, a function for sending messages,
another for closing the connection and a <code>readyState</code> property for determining
the status of the connection.</p>

<pre class="brush: jscript; light: true; title: ; notranslate">
// connect to server
var websocket = new WebSocket(&quot;ws://example.com:8080/resource&quot;);

// called when the connection is established
websocket.onopen = function(evt) { ... };

// called when a message is received from the server
websocket.onmessage = function(evt) { ... };

// called when an error occurs
websocket.onerror = function(evt) { ... };

// called when the connection is closed (by either side)
websocket.onclose = function() { ... };

// send a message to the server
websocket.send(data);

// close the connection
websocket.close();

// status of the connection
// 0 == CONNECTING
// 1 == OPEN
// 2 == CLOSING
// 3 == CLOSED
var state = websocket.readyState;</pre>

<h1>Data Transport</h1>

<p>Once the connection between client and server has been established successfully,
it is held open and becomes a two way TCP socket, allowing either side to send
messages at will.</p>

<p>Each message consists of a <code>0x00</code> byte followed by UTF-8 data followed by
a <code>0xFF</code> byte.  Any deviation from this, for example missing start or end byte
markers or the data is not UTF-8, results in an error and the termination of the
connection.  The <a href="http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76">WebSocket protocol</a> currently does not support binary
data streams.</p>

<p>On the client side, each message received triggers the <code>onmessage</code> handler
function, allowing client code to react appropriately.</p>

<h1>The Client</h1>

<p>Now it&#8217;s time to dive into the client code.  The html is really simple:</p>

<pre class="brush: xml; title: ; notranslate">
&lt;html&gt;
&lt;head&gt;
  &lt;title&gt;HTML5 Chat Demo&lt;/title&gt;
&lt;body&gt;

&lt;h3&gt;HTML 5 Chat Demo&lt;/h3&gt;

&lt;h4 id=&quot;error&quot; style=&quot;color:#ff0000&quot;&gt;&lt;/h4&gt;
&lt;div id=&quot;console&quot; style=&quot;height:300px; overflow:scroll; border:1px solid black&quot;&gt;&lt;/div&gt;
&lt;div style=&quot;padding-top:10px&quot;&gt;
    &lt;label&gt;Handle:&lt;/label&gt;&lt;input id=&quot;handle&quot; size=&quot;25&quot; value=&quot;&quot;&gt;
    &lt;button id=&quot;connect&quot;&gt;Connect&lt;/button&gt;
    &lt;button id=&quot;disconnect&quot;&gt;Disconnect&lt;/button&gt;&lt;br&gt;
    &lt;label&gt;Message:&lt;/label&gt;&lt;input id=&quot;message&quot; size=&quot;25&quot; value=&quot;&quot;&gt;
    &lt;button id=&quot;send&quot;&gt;Send&lt;/button&gt;
&lt;/div&gt;


&lt;/body&gt;
&lt;/html&gt;</pre>

<p>We have a large div to represent the chat window.  Below it is a text box to
hold the user&#8217;s chat handle, two buttons for connecting and disconnecting to the
server, followed by another text box for the user&#8217;s current message, and a send
button.  The actual work is done via javascript.</p>

<p>First we need to see if the browser supports WebSockets, if not, we display an
error message:</p>

<pre class="brush: jscript; gutter: false; title: ; notranslate">
// test for websocket support
if (&quot;WebSocket&quot; in window) {

    // websockets supported!

} else { // no websocket support
    $('error').innerHTML = &quot;Your browser does not appear to support WebSockets&quot;;
}</pre>

<p>Once compatibility has been determined, we can attempt to make a connection and
hook up the handler functions.</p>

<pre class="brush: jscript; gutter: false; title: ; notranslate">
// creating a WebSocket object causes it
// to connect to the server
websocket = new WebSocket(server);

// called once the connection is established
websocket.onopen = function(evt) {
    $('console').innerHTML += &quot;CONNECTED&lt;br&gt;&quot;;
};

// called upon receipt of a message
websocket.onmessage = function(evt) {
    $('console').innerHTML += &quot;RECEIVED: &quot;+evt.data+&quot;&lt;br&gt;&quot;;
};

// called when an error occurs
websocket.onerror = function(evt) {
    $('console').innerHTML += &quot;ERROR: &quot;+evt.data +&quot;&lt;br&gt;&quot;;
};

// called when the connection is closed (by either side)
websocket.onclose = function() {
    $('console').innerHTML += &quot;CLOSED&lt;br&gt;&quot;;
};</pre>

<h1>The Server</h1>

<p>The server application needs to listen on a given port for incoming connections,
offer to upgrade them from HTTP to WebSockets, perform the handshake correctly,
and finally perform any logic specific to your application.  In this case, it
forwards messages between connected clients.  In PHP, this requires a number of
calls to the various <code>socket_*</code> functions.  This example is pretty light on
proper error handling, and uses code adapted from
<a href="http://code.google.com/p/phpwebsocket/">here</a> and
<a href="http://bohuco.net/dev/websocket/?source=WebSocketServer.php">here</a></p>

<p>First we need to create a socket to listen on.  This initial socket needs to be
saved as it is required for getting the socket objects for any clients that
connect.</p>

<pre class="brush: php; first-line: 36; title: ; notranslate">
protected function connectMaster() {
    // create a server socket.  AF_INET = IPv4, SOCK_STREAM + SOL_TCP = TCP/IP
    $this-&gt;master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP) or die(&quot;socket_create() failed&quot;);
    // reuse local addresses
    socket_set_option($this-&gt;master, SOL_SOCKET, SO_REUSEADDR, 1) or die(&quot;socket_option() failed&quot;);
    // bind the IP address to the socket
    socket_bind($this-&gt;master, $this-&gt;address, $this-&gt;port) or die(&quot;socket_bind() failed&quot;);
    // listen for incoming connections (max of 20 pending)
    socket_listen($this-&gt;master, 20) or die(&quot;socket_listen() failed&quot;);
    // save the socket object
    $this-&gt;sockets[] = $this-&gt;master;

    $this-&gt;log(&quot;Websocket server started : &quot;. date('Y-m-d H:i:s'));
    $this-&gt;log(&quot;Listening on             : &quot;. $this-&gt;address .&quot; port &quot;. $this-&gt;port);
    $this-&gt;log(&quot;Master socket            : &quot;. $this-&gt;master);
    $this-&gt;log(&quot;CTRL-C to quit&quot;);

    return $this-&gt;master;
}</pre>

<p>Now we enter the main server run loop.  It waits around for any of its open
sockets to have data.  If it&#8217;s the master socket, it means we have a new
connection to process.  If it&#8217;s any other socket, we send the handshake if
necessary, otherwise, we pass the message to our callback function.</p>

<pre class="brush: php; first-line: 56; title: ; notranslate">
public function run() {
    // main run loop
    while(true){
        $changed = $this-&gt;sockets;
        // wait for any of the sockets to change (have data to read)
        // see http://www.php.net/manual/en/function.socket-select.php
        if (false === socket_select($changed, $write=NULL, $except=NULL,NULL)) {
            die(&quot;socket_select() failed: &quot;. socket_strerror(socket_last_error()));
        }
        foreach($changed as $socket){
            if ($socket == $this-&gt;master) {
                // new incoming connection, time to accept
                $this-&gt;log(&quot;Incoming connection request&quot;);
                $client = socket_accept($this-&gt;master);
                if($client === false){ $this-&gt;log(&quot;socket_accept() failed: &quot;. socket_strerror(socket_last_error())); continue; }
                else{ $this-&gt;connect($client); }
            } else {
                // existing connection has data to read
                $bytes = @socket_recv($socket, $buffer, 2048, 0);
                // close the connection if there was an error or no data to read
                if($bytes == 0) {
                    $this-&gt;disconnect($socket);
                } else {
                    $user = $this-&gt;getUserBySocket($socket);
                    // perform the WebSocket upgrade HTTP handshake if needed
                    if(! $user-&gt;handshake) {
                        $this-&gt;doHandshake($buffer, $socket);
                        $user-&gt;handshake = true;
                    } else {
                        $this-&gt;log(&quot;&lt; &quot;. $this-&gt;unwrap($buffer));
                        $user-&gt;lastAction = time();
                        // call the callback function
                        if ($this-&gt;callback) {
                            call_user_func($this-&gt;callback, $user, $this-&gt;unwrap($buffer), $this);
                        }
                    }
                }
            }
        }
    }
}</pre>

<p>Here&#8217;s the simple callback function which passes the message along to the other
clients so that they can display our chat messages:</p>

<pre class="brush: php; first-line: 23; title: ; notranslate">
// callback function
function process($sourceUser, $msg, $server){

    // echo the data back to all connected users
    foreach($server-&gt;getUsers() as $user){
        $server-&gt;send($user-&gt;socket, $msg);
    }
}</pre>

<p>Because PHP is not normally used as an infinitely running process, we have to
adjust the time limit config as well as turn off the buffering subsystem to get
this to work smoothly without timing out.</p>

<pre class="brush: php; first-line: 13; title: ; notranslate">
// show us all errors
error_reporting(E_ALL | E_NOTICE);
// keep running until script aborts or we're killed
set_time_limit(0);
// flush output buffering
ob_implicit_flush();</pre>

<p>And there you have the basics of a simple websocket server and client.</p>

<h1>Limitations of WebSockets</h1>

<p>Currently, browsers based on recent versions of webkit (Safari &amp; Chrome but not
iPhone) support <a href="http://tools.ietf.org/html/draft-hixie-thewebsocketprotocol-76">draft76</a>[the latest draft] of the WebSocket spec, but as a
draft, it&#8217;s a bit of a moving target.</p>

<p>As <a href="http://blogs.webtide.com/gregw/entry/websocket_chat">Greg Wilkins discusses</a>,
there are some fundamental difficulties writing a robust application based on
the current WebSocket draft specification.  In particular, since any problem
encountered by client or server results in the connection closing, it is
impossible for the client to distinguish errors from failed connections from
time outs or transient network issues as all result in a single call to
<code>onclose()</code>.</p>

<p>Also, as <a href="http://www.ietf.org/mail-archive/web/hybi/current/msg02149.html">discussed here</a>,
even when using port 80, non-WebSocket aware proxies often terminate WebSocket
handshakes or connections preventing clients behind the proxy from communicating
with the WebSocket server.</p>

<p>Hopefully, future drafts of the spec will address these issues.</p>

<h1>Putting It All Together</h1>

<p>To see the chat client in action, <a href="http://chat.dev.weevilgenius.net/">click here</a>.</p>

<p>The full client and server source code is available as a download:</p>

<p><a href="http://weevilgenius.net/wp-content/uploads/2010/10/phpwebsocket.zip">phpwebsocket.zip</a>[5.4k]</p>

<p>To launch the server, use something like this:</p>

<pre class="brush: plain; light: true; title: ; notranslate">
php server/server.php 127.0.0.1 8080
</pre>

<p>Edit the server address in the client/index.html code to match your own server.</p>

<pre class="brush: jscript; first-line: 17; highlight: [24]; title: ; toolbar: false; notranslate">
// the websocket object
var websocket;

// our chat handle
var handle = &quot;&quot;;

// the address of the websocket server
var server = &quot;ws://chat.dev.weevilgenius.net:1212/chat_demo&quot;;


function init()
{</pre>
]]></content:encoded>
			<wfw:commentRss>http://weevilgenius.net/2010/10/html-5-websocket-chat-demo/feed/</wfw:commentRss>
		<slash:comments>16</slash:comments>
		</item>
		<item>
		<title>HTML 5 Mobile &#8211; Week 3 Summary</title>
		<link>http://weevilgenius.net/2010/10/html-5-mobile-week-3-summary/</link>
		<comments>http://weevilgenius.net/2010/10/html-5-mobile-week-3-summary/#comments</comments>
		<pubDate>Thu, 21 Oct 2010 03:47:54 +0000</pubDate>
		<dc:creator>Barry Stump</dc:creator>
				<category><![CDATA[HTML 5]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[apple]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[mobile]]></category>

		<guid isPermaLink="false">http://weevilgenius.net/?p=45</guid>
		<description><![CDATA[This is another weekly summary of the live video class HTML 5 Mobile Web Development. I have already written a summary of week 2. This week we are going to use ajax to request live Twitter updates, use some CSS3 transitions, and explore web fonts. Setup This week you will need to host your files [...]]]></description>
			<content:encoded><![CDATA[<p>This is another weekly summary of the live video class
<a href="http://training.oreilly.com/html5mobile/">HTML 5 Mobile Web Development</a>.  I have already written a
<a href="http://weevilgenius.net/2010/10/html-5-mobile-week-2-summary/">summary of week 2</a>.  This week we are going to use ajax to request live
<a href="http://twitter.com/">Twitter</a> updates, use some CSS3 transitions, and explore web fonts.</p>

<span id="more-45"></span>

<h1>Setup</h1>

<p>This week you will need to host your files on a PHP server with <a href="http://www.php.net/manual/en/intro.curl.php">curl</a> support
enabled.  We will be using the following PHP script to act as an ajax proxy when
communicating with Twitter.  This is done to get around the same origin browser
security policy which prevents scripts from accessing a server other than that
which hosts the page containing the script.  There are other techniques for this
which were discussed in some detail in the chat room.</p>

<p><strong>curl.php</strong></p>

<pre class="brush: php; collapse: true; light: false; title: ; toolbar: true; notranslate">
&lt;?php
$apiCall = isset($_GET['apiCall']) ? $_GET['apiCall'] : false;
if (!$apiCall) {
    header(&quot;HTTP/1.0 400 Bad Request&quot;);
    echo &quot;Failed because apiCall parameter is missing&quot;;
    exit();
}

$url = urldecode($apiCall);

// Open the Curl session
$session = curl_init($url);
// Don't return HTTP headers. Do return the contents of the call
curl_setopt($session, CURLOPT_HEADER, false);
curl_setopt($session, CURLOPT_RETURNTRANSFER, true);

// Make the call
$json = curl_exec($session);

// The web service returns JSON
header(&quot;Content-Type:  application/json&quot;);

echo $json;
curl_close($session);</pre>

<p>You can determine whether your server supports the <a href="http://www.php.net/manual/en/intro.curl.php">curl</a> extension by looking
at the output of this PHP snippet:</p>

<pre class="brush: php; light: true; title: ; notranslate">
&lt;?php phpinfo();</pre>

<p>You should see a section titled &#8220;curl&#8221;.</p>

<h1>Section 1</h1>

<p>Time to add the javascript necessary to fetch tweets asynchronously.  This is
standard stuff for current websites and the browser specific details would
normally be handled for you by a javascript framework such as jQuery.  None of
this code is specific to HTML 5.</p>

<pre class="brush: jscript; first-line: 18; title: ; notranslate">
function pageLoaded() {
    //showProfile('JakeCarter');
    getTweets();
}

function getTweets() {
    //console.log('getTweets()');

    var client = new XMLHttpRequest();
    client.onreadystatechange = function() {
        if (this.readyState == XMLHttpRequest.prototype.DONE &amp;&amp; this.status == 200) {
            console.log(&quot;Got tweets? Yup!&quot;);
            // So far so good.
            // TODO: Add code to convert JSON to JS Object

            // TODO: Loop through results and Create List Items for our list.
        }
    };
    var apiCall = 'http://search.twitter.com/search.json?q=OReillyMedia';
    var requestString = '../../../shared/scripts/curl.php?apiCall=' + escape(apiCall);

    client.open('GET', requestString, true);
    client.send();
}</pre>

<p>To view the javascript console, use <a href="http://getfirebug.com/">Firebug</a> in Firefox, or the
<a href="http://trac.webkit.org/wiki/WebInspector">Web Inspector</a> in webkit browsers (Safari &amp; Chrome/Chromium).  Here&#8217;s what
the page looks like in Chromium.</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2010/10/week3_01.png" alt="Screenshot 01" /></p>

<h1>Section 2</h1>

<p>Now we need to actually parse the data returning from Twitter so that we can
display it appropriately on our page.  Because a JSON string is valid javascipt,
we can use the javascript built-in <code>eval()</code> function to convert it directly to
the equivalent javascript objects.  In any kind of production system, you should
not use <code>eval()</code> and instead use a proper JSON parser as otherwise you have a
security risk of running untrusted or unintentional code.  The rest of the new
code simply creates some new DOM elements for displaying the twitter data and
adds them to the page.</p>

<pre class="brush: jscript; first-line: 23; title: ; notranslate">
function getTweets() {
    //console.log('getTweets()');

    var client = new XMLHttpRequest();
    client.onreadystatechange = function() {
        if (this.readyState == XMLHttpRequest.prototype.DONE &amp;&amp; this.status == 200) {
            // So far so good.
            var tweetsList = document.getElementById('tweetsList');
            var response = eval('(' + this.responseText + ')');
            for (var resultId in response.results) {
                var result = response.results[resultId];

                // Get info we'll use.
                var name = result.from_user;
                var text = result.text;
                var imageUrl = result.profile_image_url;

                // Create Image Element
                var imageElement = document.createElement('img');
                imageElement.setAttribute('src', imageUrl);
                imageElement.setAttribute('alt', name)
                imageElement.setAttribute('class', 'user-image');
                // imageElement.setAttribute('onClick', &quot;showProfile('&quot; + name + &quot;')&quot;);

                // Create username span
                var usernameSpan = document.createElement('span');
                usernameSpan.setAttribute('class', 'username');
                usernameSpan.innerText = name;

                // Create message span
                var messageSpan = document.createElement('span');
                messageSpan.setAttribute('class', 'message');
                messageSpan.innerText = text;

                // Create container div
                var div = document.createElement('div');
                div.appendChild(imageElement);
                div.appendChild(usernameSpan);
                div.appendChild(messageSpan);

                // Create List Item
                var li = document.createElement('li');
                li.appendChild(div);

                // Add List Item to List
                tweetsList.insertBefore(li, tweetsList.firstChild);
            }
        }
    };</pre>

<p>Here&#8217;s what it looks like on an Android device:</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2010/10/week3_02.jpg" alt="Screenshot 02" /></p>

<h1>Section 3</h1>

<p>Now that we&#8217;re getting back real tweets, let&#8217;s add a click handler to display
detailed profile information.  It will make another call to Twitter.</p>

<pre class="brush: jscript; first-line: 40; highlight: [45]; title: ; notranslate">
// Create Image Element
var imageElement = document.createElement('img');
imageElement.setAttribute('src', imageUrl);
imageElement.setAttribute('alt', name)
imageElement.setAttribute('class', 'user-image');
imageElement.setAttribute('onClick', &quot;showProfile('&quot; + name + &quot;')&quot;);
</pre>

<pre class="brush: jscript; first-line: 79; title: ; notranslate">
function getProfileForScreenName(screenName) {
    var client = new XMLHttpRequest();
    client.onreadystatechange = function() {
        if (this.readyState == XMLHttpRequest.prototype.DONE &amp;&amp; this.status == 200) {
            var profile = eval('(' + this.responseText + ')');

            // Get HTML Elements.
            var profileImage = document.getElementById('profileImage');
            profileImage.setAttribute('src', profile.profile_image_url);
            profileImage.setAttribute('alt', profile.name);

            var name = document.getElementById('name');
            name.innerText = profile.name;

            var screenName = document.getElementById('screenName');
            screenName.innerText = &quot;@&quot; + profile.screen_name;

            var location = document.getElementById('location');
            location.innerText = profile.location;

            var statusText = document.getElementById('statusText');
            statusText.innerText = profile.status.text;

            var followersCount = document.getElementById('followersCount');
            followersCount.innerText = &quot;Followers: &quot; + profile.followers_count;

            var friendsCount = document.getElementById('friendsCount');
            friendsCount.innerText = &quot;Following: &quot; + profile.friends_count;

            switchToSectionWithId('profile');
        }
    };
    var apiCall = 'http://api.twitter.com/1/users/show.json?screen_name=' + screenName;
    var requestString = '../../../shared/scripts/curl.php?apiCall=' + escape(apiCall);

    client.open('GET', requestString, true);
    client.send();
}
</pre>

<pre class="brush: jscript; first-line: 164; title: ; notranslate">
function showProfile(username) {
    clearAllNavItems();
    getProfileForScreenName(username);
}
</pre>

<p>While we&#8217;re at it, we can use a CSS3 transition to animate the display of the
profile view.  This transition will apply to the opacity of the section
elements.</p>

<pre class="brush: css; first-line: 155; highlight: [156,157,158,159]; title: ; notranslate">
section {
    -webkit-transition-property: opacity;
    -webkit-transition-delay: 0.5s;
    -webkit-transition-duration: 0.5s;
    -webkit-transition-timing-function: ease;
    z-index: 0;
    position: absolute;
    top: 88px;
    width: 100%;
    background-color: #ffffff;
    display: block;
    opacity: 0;
}
section.selected {
    z-index: 100;
    opacity: 1;
}</pre>

<p>Here&#8217;s what the profile view looks like on the device.</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2010/10/week3_03.jpg" alt="Screenshot 03" /></p>

<h1>Final Section</h1>

<p>Now let&#8217;s use a webfont for a custom look.  Before webfonts, you could only
specify CSS fonts which you hoped the user had installed. Now we can specify a
custom font and provide the file for the browser to use.  At the moment, this
only works in the Android browser, not the iPhone.</p>

<pre class="brush: css; first-line: 7; highlight: [7,8,9,10,25]; title: ; notranslate">
@font-face {
    font-family: Chunk;
    src: url('../../../shared/fonts/chunk/Chunk.otf') format('opentype');
}
/*
    Header
*/
header {
    width: 100%;
    height: 44px;
    background-image: -webkit-gradient(
        linear,
        0% 0%,
        0% 100%,
        from(#666666),
        to(#666666),
        color-stop(.5,#333333)
    );
    font-family: Chunk, Helvetica;

    -webkit-box-sizing: border-box;
</pre>

<p>Here&#8217;s the page at the end of week 3 with the custom font.</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2010/10/week3_04.jpg" alt="Screenshot 04" /></p>

<h1>Useful Links</h1>

<p>Class source code download: <a href="http://examples.oreilly.com/0636920014225">http://examples.oreilly.com/0636920014225</a><br />
Class forum: <a href="http://forums.oreilly.com/category/104/HTML5-Mobile-Web-Development/">http://forums.oreilly.com/category/104/HTML5-Mobile-Web-Development/</a></p>

<p>These came up in the chat channel during this week&#8217;s session</p>

<ul>
<li><a href="http://rumpetroll.com/">http://rumpetroll.com/</a></li>
<li><a href="http://html5demos.com/">http://html5demos.com/</a></li>
<li><a href="http://jquery-howto.blogspot.com/2009/04/twitter-jsonjsonp-api-url.html">JSONP on Twitter</a></li>
<li><a href="http://www.json.org/json2.js">JSON parser</a></li>
<li>Google&#8217;s <a href="http://closure-compiler.appspot.com/home">closure minifier</a></li>
<li><a href="http://jquerymobile.com/">jQuery Mobile</a></li>
<li><a href="http://jqtouch.com/">jqTouch</a></li>
<li><a href="http://www.theleagueofmoveabletype.com/">League of Moveable Type</a></li>
<li><a href="http://www.fontsquirrel.com/">Font Squirrel</a></li>
<li><a href="http://paulirish.com/2009/bulletproof-font-face-implementation-syntax/">http://paulirish.com/2009/bulletproof-font-face-implementation-syntax/</a></li>
<li><a href="http://github.com/madrobby/zepto">http://github.com/madrobby/zepto</a></li>
</ul>
]]></content:encoded>
			<wfw:commentRss>http://weevilgenius.net/2010/10/html-5-mobile-week-3-summary/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Squashfs &#8211; Compressed Filesystem Backup</title>
		<link>http://weevilgenius.net/2010/10/squashfs-compressed-filesystem-backup/</link>
		<comments>http://weevilgenius.net/2010/10/squashfs-compressed-filesystem-backup/#comments</comments>
		<pubDate>Mon, 18 Oct 2010 22:58:24 +0000</pubDate>
		<dc:creator>Barry Stump</dc:creator>
				<category><![CDATA[Linux]]></category>
		<category><![CDATA[How To]]></category>

		<guid isPermaLink="false">http://weevilgenius.net/?p=40</guid>
		<description><![CDATA[I am way overdue upgrading my primary work machine to the latest Ubuntu release. The biggest thing that has been holding me back (besides finding the time) is the fear of losing some file or system config which will take ages to recreate. Often when I upgrade a system like this, I simply install a [...]]]></description>
			<content:encoded><![CDATA[<p>I am way overdue upgrading my primary work machine to the latest Ubuntu release.
The biggest thing that has been holding me back (besides finding the time) is
the fear of losing some file or system config which will take ages to recreate.
Often when I upgrade a system like this, I simply install a new hard drive and
keep the old drive around in case I need to dig up some file I don&#8217;t have handy
in any other location.  But this is a laptop and I am already nearly out of free
space.  What I&#8217;m going to do this time is use <a href="http://squashfs.sourceforge.net/">Squashfs</a> to create a
compressed, read-only  filesystem inside a file which can be placed on an
external drive.</p>

<span id="more-40"></span>

<h1>Considerations</h1>

<p>There are a number of different things I could do to save a copy of my files:</p>

<ul>
<li>Use rsync to create a remote directory copy</li>
<li>Use cp to create a remote directory copy</li>
<li>Use tar to create a remote file copy</li>
<li>Use tar + gzip (or bzip2) to create a remote compressed file copy</li>
<li>Create a ext2/3/4 filesystem inside a remote file using the loopback device</li>
<li>Use one of the many backup utilities available for linux</li>
</ul>

<p>Why did I choose Squashfs?  Well, since I&#8217;m going to be copying the majority of
a running filesystem, any of the methods which create a remote directory copy
will be filled with thousands of files and directories.  I don&#8217;t really like
that kind of mess unless the external disk is dedicated.  Tar+gzip gets me a
single file and compression too, but it&#8217;s a pain to search as the whole thing
would need to be untarred before use.  Creating a read/write filesystem in a
file is really useful, but I&#8217;m happy to trade write ability for compression.  I
don&#8217;t really need a backup utility since this is a one time operation.</p>

<p><a href="http://squashfs.sourceforge.net/">Squashfs</a> is in widespread use as the primary filesystem for many live CD
linux distributions, and the linux kernel has included support since version
2.6.29.  On Ubuntu, the user level tools can be installed via the <em>squashfs-tools</em>
package.</p>

<h1>Squash It</h1>

<p>Enough talk, time to put this into practice.  First, we need to verify that our
kernel has the squashfs module available:</p>

<pre class="brush: plain; light: true; title: ; notranslate">
$ modinfo squashfs
filename:       /lib/modules/2.6.28-19-generic/kernel/ubuntu/squashfs/squashfs.ko
license:        GPL
author:         Phillip Lougher &lt;phillip@lougher.demon.co.uk&gt;
description:    squashfs 3.2-r2-CVS, a compressed read-only filesystem
srcversion:     7626F567E1FBB5EDCFF9F67
depends:
vermagic:       2.6.28-19-generic SMP mod_unload modversions 586
</pre>

<p>Good, we&#8217;ve got the kernel module.  Now we need to install the userland tools.</p>

<pre class="brush: plain; light: true; title: ; notranslate">
$ sudo apt-get install squashfs-tools
</pre>

<p>Now let&#8217;s create a little squashfs file to verify that everything works.  I&#8217;m
going to use one the linux kernel source directories as a test.</p>

<pre class="brush: plain; light: true; title: ; notranslate">
$ mksquashfs /usr/src/linux-headers-2.6.28-19 test.sqsh
Parallel mksquashfs: Using 2 processors
Creating little endian 3.1 filesystem on test.sqsh, block size 131072.
[============================================================================================] 8848/8848 100%
Exportable Little endian filesystem, data block size 131072, compressed data, compressed metadata, compressed fragments, duplicates are removed
Filesystem size 8196.05 Kbytes (8.00 Mbytes)
        23.39% of uncompressed filesystem size (35034.82 Kbytes)
Inode table size 94740 bytes (92.52 Kbytes)
        29.11% of uncompressed inode table size (325410 bytes)
Directory table size 89099 bytes (87.01 Kbytes)
        57.22% of uncompressed directory table size (155701 bytes)
Number of duplicate files found 202
Number of inodes 10353
Number of files 8842
Number of fragments 284
Number of symbolic links  0
Number of device nodes 0
Number of fifo nodes 0
Number of socket nodes 0
Number of directories 1511
Number of uids 1
        root (0)
Number of gids 0
</pre>

<p>That was easy.  Now let&#8217;s mount the file.</p>

<pre class="brush: plain; light: true; title: ; notranslate">
$ mkdir ~/squash_test
$ sudo mount -t squashfs -o loop test.sqsh ~/squash_test
</pre>

<p>Let&#8217;s compare the contents to the original source:</p>

<pre class="brush: plain; light: true; title: ; notranslate">
$ ls -l ~/squash_test
total 59
drwxr-xr-x 24 root root   358 2010-09-20 09:20 arch/
drwxr-xr-x  2 root root    57 2010-09-20 09:20 block/
drwxr-xr-x  3 root root    50 2010-09-20 09:20 crypto/
drwxr-xr-x 19 root root   223 2010-09-20 09:20 Documentation/
drwxr-xr-x 83 root root   889 2010-09-20 09:20 drivers/
drwxr-xr-x  2 root root    25 2010-09-20 09:20 firmware/
drwxr-xr-x 62 root root   653 2010-09-20 09:20 fs/
drwxr-xr-x 27 root root   351 2010-09-20 09:20 include/
drwxr-xr-x  2 root root    37 2010-09-20 09:20 init/
drwxr-xr-x  2 root root    25 2010-09-20 09:20 ipc/
-rw-r--r--  1 root root  2430 2008-12-24 15:26 Kbuild
drwxr-xr-x  6 root root   134 2010-09-20 09:20 kernel/
drwxr-xr-x  6 root root   131 2010-09-20 09:20 lib/
-rw-r--r--  1 root root 57730 2010-09-16 07:13 Makefile
drwxr-xr-x  2 root root    37 2010-09-20 09:20 mm/
drwxr-xr-x 45 root root   477 2010-09-20 09:20 net/
drwxr-xr-x  6 root root    89 2010-09-20 09:20 samples/
drwxr-xr-x 10 root root  1057 2010-09-20 09:20 scripts/
drwxr-xr-x  6 root root    81 2010-09-20 09:20 security/
drwxr-xr-x 20 root root   205 2010-09-20 09:20 sound/
drwxr-xr-x 21 root root   257 2010-09-20 09:20 ubuntu/
drwxr-xr-x  2 root root    37 2010-09-20 09:20 usr/

$ ls -l /usr/src/linux-headers-2.6.28-19
total 148
drwxr-xr-x 24 root root  4096 2010-09-20 09:20 arch/
drwxr-xr-x  2 root root  4096 2010-09-20 09:20 block/
drwxr-xr-x  3 root root  4096 2010-09-20 09:20 crypto/
drwxr-xr-x 19 root root  4096 2010-09-20 09:20 Documentation/
drwxr-xr-x 83 root root  4096 2010-09-20 09:20 drivers/
drwxr-xr-x  2 root root  4096 2010-09-20 09:20 firmware/
drwxr-xr-x 62 root root  4096 2010-09-20 09:20 fs/
drwxr-xr-x 27 root root  4096 2010-09-20 09:20 include/
drwxr-xr-x  2 root root  4096 2010-09-20 09:20 init/
drwxr-xr-x  2 root root  4096 2010-09-20 09:20 ipc/
-rw-r--r--  1 root root  2430 2008-12-24 15:26 Kbuild
drwxr-xr-x  6 root root  4096 2010-09-20 09:20 kernel/
drwxr-xr-x  6 root root  4096 2010-09-20 09:20 lib/
-rw-r--r--  1 root root 57730 2010-09-16 07:13 Makefile
drwxr-xr-x  2 root root  4096 2010-09-20 09:20 mm/
drwxr-xr-x 45 root root  4096 2010-09-20 09:20 net/
drwxr-xr-x  6 root root  4096 2010-09-20 09:20 samples/
drwxr-xr-x 10 root root  4096 2010-09-20 09:20 scripts/
drwxr-xr-x  6 root root  4096 2010-09-20 09:20 security/
drwxr-xr-x 20 root root  4096 2010-09-20 09:20 sound/
drwxr-xr-x 21 root root  4096 2010-09-20 09:20 ubuntu/
drwxr-xr-x  2 root root  4096 2010-09-20 09:20 usr/
</pre>

<p>Perfect!  How much space did we save?</p>

<pre class="brush: plain; light: true; title: ; notranslate">
$ ls -lh ~/test.sqsh
-rwx------ 1 bstump bstump 8.1M 2010-10-18 15:34 /home/bstump/test.sqsh*

$ du -hs /usr/src/linux-headers-2.6.28-19
64M     /usr/src/linux-headers-2.6.28-19
</pre>

<p>In this example, the source directory is about 64 MB versus only 8 MB for the
compressed version.  That&#8217;s 8:1 compression.  However, the linux kernel is
highly compressible source code.  Typical compression for mixed files is about
2:1.</p>

<p>Unmount the file when we&#8217;re done.</p>

<pre class="brush: plain; light: true; title: ; notranslate">
$ sudo umount ~/squash_test
</pre>

<p>Now I&#8217;m ready to back up my primary file system to a single archive file in
preparation for a system update.</p>]]></content:encoded>
			<wfw:commentRss>http://weevilgenius.net/2010/10/squashfs-compressed-filesystem-backup/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>HTML 5 Mobile &#8211; Week 2 Summary</title>
		<link>http://weevilgenius.net/2010/10/html-5-mobile-week-2-summary/</link>
		<comments>http://weevilgenius.net/2010/10/html-5-mobile-week-2-summary/#comments</comments>
		<pubDate>Fri, 15 Oct 2010 00:11:11 +0000</pubDate>
		<dc:creator>Barry Stump</dc:creator>
				<category><![CDATA[HTML 5]]></category>
		<category><![CDATA[Android]]></category>
		<category><![CDATA[apple]]></category>
		<category><![CDATA[html5]]></category>
		<category><![CDATA[mobile]]></category>

		<guid isPermaLink="false">http://weevilgenius.net/?p=29</guid>
		<description><![CDATA[I am currently attending a live video class HTML 5 Mobile Web Development, and am summarizing the second lesson here in order to digest the material better and help some friends who got a late start. If you want to follow along, go to the class download page linked at the end and download the [...]]]></description>
			<content:encoded><![CDATA[<p>I am currently attending a live video class <a href="http://training.oreilly.com/html5mobile/">HTML 5 Mobile Web Development</a>,
and am summarizing the second lesson here in order to digest the material better
and help some friends who got a late start.  If you want to follow along, go to
the class download page linked at the end and download the code.</p>

<p>This week starts our construction of &#8220;Tweetstr&#8221;, a mobile web twitter client.
We create and edit the basic HTML and CSS files to display the home screen while
exploring some new HTML 5 and CSS 3 tags as well as some settings specific to
Apple devices such as the iPhone.</p>

<span id="more-29"></span>

<h1>Section 1</h1>

<p>We start with some basic html and css files.</p>

<p><strong>index.html</strong></p>

<pre class="brush: jscript; collapse: true; html-script: true; light: false; title: ; toolbar: true; notranslate">
&lt;!DOCTYPE html&gt;
&lt;html lang=&quot;en&quot;&gt;
    &lt;head&gt;
        &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot; /&gt;
        &lt;meta http-equiv=&quot;Content-Language&quot; content=&quot;en&quot;/&gt;

        &lt;!-- TODO: Set Viewport --&gt;

        &lt;title&gt;Tweetstr&lt;/title&gt;

        &lt;!-- TODO: Add CSS Reset --&gt;

        &lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot; type=&quot;text/css&quot; media=&quot;screen&quot; charset=&quot;utf-8&quot;&gt;

        &lt;script type=&quot;text/javascript&quot; charset=&quot;utf-8&quot;&gt;
            function navSelected(navItem) {
                // First we need to clear all other nav items.
                clearAllNavItems();

                // Now give navItem the selected css class.
                event.target.setAttribute('class', 'selected');
            }

            function clearAllNavItems() {
                var navList = document.getElementById('navList');

                for (var i = 0; i &lt; navList.children.length; i++) {
                    var li = navList.children[i];
                    li.setAttribute('class', '');
                }
            }
        &lt;/script&gt;
    &lt;/head&gt;
    &lt;body&gt;
        &lt;header&gt;
            Tweetstr
        &lt;/header&gt;
        &lt;nav&gt;
            &lt;ul id='navList'&gt;
                &lt;li id='liHome' onclick='navSelected()' class='selected' &gt;&lt;/li&gt;
                &lt;li id='liMentions' onclick='navSelected()'&gt;&lt;/li&gt;
                &lt;li id='liFaves' onclick='navSelected()'&gt;&lt;/li&gt;
                &lt;li id='liMessages' onclick='navSelected()'&gt;&lt;/li&gt;
                &lt;li id='liSearch' onclick='navSelected()'&gt;&lt;/li&gt;
            &lt;/ul&gt;
        &lt;/nav&gt;
        &lt;section id='home'&gt;
            &lt;ul class='tweets'&gt;
                &lt;li&gt;
                    &lt;div&gt;
                        &lt;img class='user-image' alt='JakeCarter'
                             src='user-image.png' /&gt;
                        &lt;span class='username'&gt;JakeCarter&lt;/span&gt;
                        &lt;span class='message'&gt;I love twitter!&lt;/span&gt;
                    &lt;/div&gt;
                &lt;/li&gt;
                &lt;li&gt;&lt;/li&gt;
                &lt;li&gt;&lt;/li&gt;
                &lt;li&gt;&lt;/li&gt;
                &lt;li&gt;&lt;/li&gt;
                &lt;li&gt;&lt;/li&gt;
                &lt;li&gt;&lt;/li&gt;
                &lt;li&gt;&lt;/li&gt;
                &lt;li&gt;&lt;/li&gt;
                &lt;li&gt;&lt;/li&gt;
        &lt;/section&gt;
    &lt;/body&gt;
&lt;/html&gt;
</pre>

<p><strong>style.css</strong></p>

<pre class="brush: css; collapse: true; light: false; title: ; toolbar: true; notranslate">
body {
    font-family: &quot;Helvetica&quot;;
}
/*
    Header
*/
header {
    width: 100%;
    height: 44px;

    text-align: center;
    padding-top: 10px;

    font-weight: bold;
    font-size: 1.5em;
}

/*
    Nav
*/
nav {
    height: 44px;
    width: 100%;
}
nav ul {
    height: 100%;
    width: 320px;
    margin-left: auto;
    margin-right: auto;
}
nav ul li {
    display: inline-block;
    float: left;
    width: 64px;
    height: 100%;
    border: 1px solid #999999;

    background-repeat: no-repeat;
    background-position: center center;
}

/*
    List
*/
section#home ul.tweets li {
    min-height: 52px;
}

section#home ul.tweets li div {
    padding: 2px;
}

section#home ul.tweets li div img {
    float: left;
    padding-right: 5px;
}
</pre>

<p>Notice how simple the doctype tag is for HTML 5:</p>

<pre class="brush: xml; light: true; title: ; notranslate">
&lt;!-- HTML 4 --&gt;
&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.01//EN&quot; &quot;http://www.w3.org/TR/html4/strict.dtd&quot;&gt;

&lt;!-- HTML 5 --&gt;
&lt;!DOCTYPE html&gt;</pre>

<p>Also, we&#8217;re using some new HTML 5 semantic tags, <code>&lt;header&gt;</code>, <code>&lt;nav&gt;</code>, and
<code>&lt;section&gt;</code> instead of generic <code>&lt;div&gt;</code> tags as with HTML 4.</p>

<pre class="brush: xml; first-line: 34; highlight: [35,38,47]; title: ; notranslate">
&lt;body&gt;
    &lt;header&gt;
        Tweetstr
    &lt;/header&gt;
    &lt;nav&gt;
        &lt;ul id='navList'&gt;
            &lt;li id='liHome' onclick='navSelected()' class='selected' &gt;&lt;/li&gt;
            &lt;li id='liMentions' onclick='navSelected()'&gt;&lt;/li&gt;
            &lt;li id='liFaves' onclick='navSelected()'&gt;&lt;/li&gt;
            &lt;li id='liMessages' onclick='navSelected()'&gt;&lt;/li&gt;
            &lt;li id='liSearch' onclick='navSelected()'&gt;&lt;/li&gt;
        &lt;/ul&gt;
    &lt;/nav&gt;
    &lt;section id='home'&gt;</pre>

<p>By the end of section 1, the application looks like this:</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2010/10/week2_01.jpg" alt="Screenshot 01" /></p>

<p>The browser zooms way out, the nav items don&#8217;t fit on a single line,
and we don&#8217;t have much styling yet.</p>

<h1>Section 2</h1>

<p>Let&#8217;s correct that zoom problem by setting the viewport.</p>

<pre class="brush: xml; first-line: 3; highlight: [7]; title: ; notranslate">
&lt;head&gt;
    &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot; /&gt;
    &lt;meta http-equiv=&quot;Content-Language&quot; content=&quot;en&quot;/&gt;

    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0, user-scalable=no&quot; /&gt;

    &lt;title&gt;Tweetstr&lt;/title&gt;</pre>

<p>The viewport is a meta tag which Apple introduced for <a href="http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html">mobile Safari</a>
which tells the browser what zoom level (or &#8220;scale&#8221;) should be shown for the
page and whether the user can zoom in or out when viewing that page.  You can
set <code>width</code> to either a fixed number of pixels or <code>device-width</code> (recommended).
Viewport settings are also supported by Android and <a href="http://starkravingfinkle.org/blog/2010/01/perils-of-the-viewport-meta-tag/">mobile Firefox</a>.</p>

<p>We&#8217;ll also add a HTML 5 compatible reset stylesheet.  This &#8220;resets&#8221; various
styles to sane, known values for most of the tags supported by HTML 5.  Jake
has provided the one from <a href="http://html5doctor.com/html-5-reset-stylesheet/">HTML5 doctor</a>.</p>

<pre class="brush: xml; first-line: 3; highlight: [11]; title: ; notranslate">
&lt;head&gt;
    &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot; /&gt;
    &lt;meta http-equiv=&quot;Content-Language&quot; content=&quot;en&quot;/&gt;

    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0, user-scalable=no&quot; /&gt;

    &lt;title&gt;Tweetstr&lt;/title&gt;

    &lt;link rel=&quot;stylesheet&quot; href=&quot;html5reset.css&quot; type=&quot;text/css&quot; media=&quot;screen&quot; charset=&quot;utf-8&quot; /&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot; type=&quot;text/css&quot; media=&quot;screen&quot; charset=&quot;utf-8&quot;&gt;</pre>

<p>It needs to be added before the existing stylesheet as they are processed in
order from top to bottom.</p>

<p>Here&#8217;s how the page looks by the end of section 2:</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2010/10/week2_02.jpg" alt="Screenshot 02" /></p>

<p>Now the zoom level is looking correct, and we have sane CSS values for our new
HTML 5 tags, but we still have lots of clean up to go.</p>

<h1>Section 3</h1>

<p>Each of the five nav boxes has been set to a width of 64 pixels in the
style.css file.  64 * 5 = 320, which is the width of the basic iPhone screen, so
why do they not all fit on a single row like they&#8217;re supposed to?  It&#8217;s because
browsers by default uses the &#8220;content box&#8221; sizing model, where the given width
and height are applied to the content with borders and padding added after.
This makes our 64 pixel boxes take up more than 64 pixels.</p>

<p>CSS3 adds the ability to specify an alternate box sizing model &#8220;border box&#8221;
where the width applies to the entire visible content, with the content area
shrinking to accommodate the space required for borders and padding.  See
<a href="http://www.quirksmode.org/css/box.html">this</a> for a more lengthy discussion on the topic.</p>

<p>Although CSS3 is not a final specification, we can use browser specific CSS
styles to control the sizing model.</p>

<pre class="brush: css; light: true; title: ; notranslate">
/* CSS3 official */
box-sizing: border-box;
box-sizing: content-box;

/* webkit (Chrome/Safari) */
-webkit-box-sizing: border-box;
-webkit-box-sizing: content-box;

/* Mozilla (Firefox) */
-moz-box-sizing: border-box;
-moz-box-sizing: content-box;

/* IE 8 */
-ms-box-sizing: border-box;
-ms-box-sizing: content-box;</pre>

<p>Let&#8217;s fix up our boxes by requesting the border box sizing model:</p>

<pre class="brush: css; first-line: 7; highlight: [11]; title: ; notranslate">
header {
    width: 100%;
    height: 44px;

    -webkit-box-sizing: border-box;

    text-align: center;
    padding-top: 10px;

    font-weight: bold;
    font-size: 1.5em;
}
</pre>

<pre class="brush: css; first-line: 33; highlight: [40]; title: ; notranslate">
nav ul li {
    display: inline-block;
    float: left;
    width: 64px;
    height: 100%;
    border: 1px solid #999999;

    -webkit-box-sizing: border-box;

    background-repeat: no-repeat;
    background-position: center center;
}
</pre>

<p>Here&#8217;s what the page looks like at the end of section 3.</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2010/10/week2_03.jpg" alt="Screenshot 03" /></p>

<p>That looks better.  All of the nav boxes fit on a single line.</p>

<h1>Section 4</h1>

<p>Before CSS3, using a gradients on the web meant creating image slices with the
required colors and using the CSS <code>background-repeat</code> declaration.  Lots of
work and more importantly, lots of files for the browser to download.  CSS3
allows us to create gradients through pure CSS without involving separate images.</p>

<p>Here Jake adds a gradient to the header.</p>

<pre class="brush: css; first-line: 7; highlight: [10,11,12,13,14,15,16,17]; title: ; notranslate">
header {
    width: 100%;
    height: 44px;
    background-image: -webkit-gradient(
        linear,
        0% 0%,
        0% 100%,
        from(#666666),
        to(#666666),
        color-stop(.5,#333333)
    );

    -webkit-box-sizing: border-box;

    text-align: center;
    color: #ffffff;
    padding-top: 10px;

    font-weight: bold;
    font-size: 1.5em;
}</pre>

<p>And the nav:</p>

<pre class="brush: css; first-line: 32; highlight: [35,36,37,38,39,40,41,42]; title: ; notranslate">
nav {
    height: 44px;
    width: 100%;
    background-image: -webkit-gradient(
            linear,
            0% 0%,
            0% 100%,
            from(#aaaaaa),
            to(#aaaaaa),
            color-stop(.5,#cccccc)
        );
}</pre>

<p>And a contrasting gradient for the selected state on each nav button:</p>

<pre class="brush: css; first-line: 62; title: ; notranslate">
#liHome.selected {
    background-image: -webkit-gradient(
                linear,
                0% 0%,
                0% 100%,
                from(#dddddd),
                to(#dddddd),
                color-stop(.5,#cccccc)
            );
}

#liMentions.selected {
    background-image: -webkit-gradient(
                linear,
                0% 0%,
                0% 100%,
                from(#dddddd),
                to(#dddddd),
                color-stop(.5,#cccccc)
            );
}

#liFaves.selected {
    background-image: -webkit-gradient(
                linear,
                0% 0%,
                0% 100%,
                from(#dddddd),
                to(#dddddd),
                color-stop(.5,#cccccc)
            );
}

#liMessages.selected {
    background-image: -webkit-gradient(
                linear,
                0% 0%,
                0% 100%,
                from(#dddddd),
                to(#dddddd),
                color-stop(.5,#cccccc)
            );
}

#liSearch.selected {
    background-image: -webkit-gradient(
                linear,
                0% 0%,
                0% 100%,
                from(#dddddd),
                to(#dddddd),
                color-stop(.5,#cccccc)
            );
}
</pre>

<p>Here&#8217;s what the page looks like after adding the gradients.</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2010/10/week2_04.jpg" alt="Screenshot 04" /></p>

<h1>Section 5</h1>

<p>Now let&#8217;s add the images which will form the face of our nav buttons.  Notice
how in the selected state, each box has two background images set, one for the
gradient and one for the icon.  The order is important here.</p>

<pre class="brush: css; first-line: 62; title: ; notranslate">
#liHome {
    background-image: url('../../../shared/images/icons/icons/53-house.png');
}
#liHome.selected {
    background-image: url('../../../shared/images/icons/icons/53-house.png'),
        -webkit-gradient(
                linear,
                0% 0%,
                0% 100%,
                from(#dddddd),
                to(#dddddd),
                color-stop(.5,#cccccc)
            );
}

#liMentions {
    background-image: url('../../../shared/images/icons/icons/09-chat2.png');
}
#liMentions.selected {
    background-image: url('../../../shared/images/icons/icons/09-chat2.png'),
        -webkit-gradient(
                linear,
                0% 0%,
                0% 100%,
                from(#dddddd),
                to(#dddddd),
                color-stop(.5,#cccccc)
            );
}

#liFaves {
    background-image: url('../../../shared/images/icons/icons/28-star.png');
}
#liFaves.selected {
    background-image: url('../../../shared/images/icons/icons/28-star.png'),
        -webkit-gradient(
                linear,
                0% 0%,
                0% 100%,
                from(#dddddd),
                to(#dddddd),
                color-stop(.5,#cccccc)
            );
}

#liMessages {
    background-image: url('../../../shared/images/icons/icons/08-chat.png');
}
#liMessages.selected {
    background-image: url('../../../shared/images/icons/icons/08-chat.png'),
        -webkit-gradient(
                linear,
                0% 0%,
                0% 100%,
                from(#dddddd),
                to(#dddddd),
                color-stop(.5,#cccccc)
            );
}

#liSearch {
    background-image: url('../../../shared/images/icons/icons/06-magnifying-glass.png');
}
#liSearch.selected {
    background-image: url('../../../shared/images/icons/icons/06-magnifying-glass.png'),
        -webkit-gradient(
                linear,
                0% 0%,
                0% 100%,
                from(#dddddd),
                to(#dddddd),
                color-stop(.5,#cccccc)
            );
}
</pre>

<p>Here are the nav buttons with their nice icons:</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2010/10/week2_05.jpg" alt="Screenshot 05" /></p>

<h1>Section 7</h1>

<p>Another nifty CSS3 feature is the new <code>nth-child</code> selector.  Without this, if
you wanted to style every nth row of a list or table with a distinctive look,
you were forced to apply different classes to them, typically with javascript or
a server side language like PHP.  CSS3 lets you select and style nth rows
directly from your stylesheet.</p>

<p>Here Jake gives every other (odd) row a gradient.</p>

<pre class="brush: css; first-line: 143; title: ; notranslate">
section#home ul.tweets li:nth-child(odd) {
    background-image:  -webkit-gradient(
        linear,
        0% 0%,
        0% 100%,
        from(#aaaaaa),
        to(#aaaaaa),
        color-stop(.5,#cccccc)
    );
}</pre>

<p>In addition to the shortcuts &#8220;odd&#8221; and &#8220;even&#8221;, you can put in a custom formula.
For example, if you wanted to style every 3rd row, starting from row 2, you
could use this: <code>:nth-child(3n+2)</code>.  Powerful stuff.</p>

<p>Here&#8217;s what it looks like in the browser:</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2010/10/week2_06.jpg" alt="Screenshot 06" /></p>

<h1>Final Section</h1>

<p>The page is starting to look like a proper mobile application.  For the final
section of this week, we&#8217;re going to add some <a href="http://developer.apple.com/safari/library/documentation/AppleApplications/Reference/SafariHTMLRef/Articles/MetaTags.html">Apple-specific</a>
fields to tell iOS devices some additional information about how the application
should behave.</p>

<pre class="brush: xml; first-line: 3; highlight: [9,10,14]; title: ; notranslate">
&lt;head&gt;
    &lt;meta http-equiv=&quot;Content-Type&quot; content=&quot;text/html; charset=utf-8&quot; /&gt;
    &lt;meta http-equiv=&quot;Content-Language&quot; content=&quot;en&quot;/&gt;

    &lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0, user-scalable=no&quot; /&gt;

    &lt;meta name=&quot;apple-mobile-web-app-capable&quot; content=&quot;yes&quot;&gt;
    &lt;meta name=&quot;apple-mobile-web-app-status-bar-style&quot; content=&quot;black&quot;&gt;

    &lt;title&gt;Tweetstr&lt;/title&gt;

    &lt;link rel=&quot;apple-touch-icon&quot; href=&quot;../../../shared/images/tweetstr-icon.png&quot;/&gt;

    &lt;link rel=&quot;stylesheet&quot; href=&quot;html5reset.css&quot; type=&quot;text/css&quot; media=&quot;screen&quot; charset=&quot;utf-8&quot; /&gt;
    &lt;link rel=&quot;stylesheet&quot; href=&quot;style.css&quot; type=&quot;text/css&quot; media=&quot;screen&quot; charset=&quot;utf-8&quot;&gt;</pre>

<p>The first one, <code>apple-mobile-web-app-capable</code> determines whether the page can be
treated as a full screen application (without the Safari URL bar and navigation
buttons) when launched from the home screen.  To get a web page to launch from
the home screen on iOS devices, open it in mobile Safari, click the &#8220;+&#8221; bookmark
button and choose &#8220;Add to Home Screen&#8221;.  A web page saved this way with the meta
tag set will launch full screen.  Without it, you get the normal mobile Safari
UI.  The sugested name of the application short cut on the home screen comes
from the title of the web page.</p>

<p>The next Apple specific tag is <code>apple-mobile-web-app-status-bar-style</code> which
controls the appearance of the status bar for full screen web apps when launched
from the home screen.  It can be set to &#8220;default&#8221;, &#8220;black&#8221; or
&#8220;black-translucent&#8221;.  Here we use black.</p>

<p>The last meta tag we added sets the icon used on the device&#8217;s home screen for
the web app.  Without this tag, iOS will save a miniature screenshot of the web
page as the application icon.  This version of the tag allows iOS to add rounded
corners and that Apple glossy highlight to the icon.  If you don&#8217;t want this (or
your icon already has these effect applied), use <code>apple-touch-icon-precomposed</code>
instead.</p>

<p>One additional Apple tag which Jake didn&#8217;t cover is <code>apple-touch-startup-image</code>
which allows you to specify an image to display as a splash screen while your
web application is loading.  Because of the varying screen resolutions even
across Apple devices, this is not as useful as the other tags.</p>

<p>Here is the Add to Home screen for Tweetstr:</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2010/10/week2_07.jpg" alt="Screenshot 07" /></p>

<p>And here is the icon on the home screen:</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2010/10/week2_08.jpg" alt="Screenshot 08" /></p>

<p>And here it has been launched as a full screen web application.  Notice there is
no Safari UI and the title bar is now black.</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2010/10/week2_09.jpg" alt="Screenshot 09" /></p>

<p>That&#8217;s all until next week.</p>

<h1>Useful Links</h1>

<p>Class source code download: <a href="http://examples.oreilly.com/0636920014225">http://examples.oreilly.com/0636920014225</a><br />
Class forum: <a href="http://forums.oreilly.com/category/104/HTML5-Mobile-Web-Development/">http://forums.oreilly.com/category/104/HTML5-Mobile-Web-Development/</a></p>

<p>These came up in the chat channel during this week&#8217;s session</p>

<ul>
<li><a href="http://code.google.com/p/mobile-bookmark-bubble/">mobile bookmark bubble</a> -
opensource code created by Google to remind users to add your web app to the
home screen</li>
<li><a href="http://blog.iwalt.com/2010/06/targeting-the-iphone-4-retina-display-with-css3-media-queries.html">retina display</a> -
how to target the iPhone 4 retina display with a different CSS stylesheet</li>
<li><a href="http://mobile.tutsplus.com/tutorials/iphone/iphone-web-app-meta-tags/">Apple web app meta tags</a> -
Apple specific meta tags for mobile apps</li>
<li><a href="http://openappmkt.com/">Open App Market</a> &#8211; marketplace for mobile apps</li>
</ul>]]></content:encoded>
			<wfw:commentRss>http://weevilgenius.net/2010/10/html-5-mobile-week-2-summary/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Vim Tip &#8211; Relocating vim swapfiles</title>
		<link>http://weevilgenius.net/2010/10/vim-tip-relocating-vim-swapfiles/</link>
		<comments>http://weevilgenius.net/2010/10/vim-tip-relocating-vim-swapfiles/#comments</comments>
		<pubDate>Mon, 11 Oct 2010 18:35:36 +0000</pubDate>
		<dc:creator>Barry Stump</dc:creator>
				<category><![CDATA[vim]]></category>
		<category><![CDATA[dropbox]]></category>

		<guid isPermaLink="false">http://weevilgenius.net/?p=26</guid>
		<description><![CDATA[Vim is my editor of choice for any file that doesn&#8217;t need a full IDE, and I generally have multiple files open in various windows at any point in time. In its default configuration, for each file you have open, vim creates a hidden swap file in the same directory with the extension &#8220;.swp&#8221;. Unfortunately, [...]]]></description>
			<content:encoded><![CDATA[<p>Vim is my editor of choice for any file that doesn&#8217;t need a full IDE, and I
generally have multiple files open in various windows at any point in time.  In
its default configuration, for each file you have open, vim creates a hidden
swap file in the same directory with the extension &#8220;.swp&#8221;.  Unfortunately, if
any of those files reside inside my <a href="http://www.dropbox.com/">Dropbox</a> folder, the Dropbox program
notices the swap files and syncs them up to the cloud.  Read on to see how to
change that behavior without losing the advantages that swap files give.</p>

<span id="more-26"></span>

<p>An easy solution for one or two files is simply to invoke vim with the <code>-n</code>
option which tells vim to not use any swap files for that session.</p>

<pre><code>vim -n foo.txt
</code></pre>

<p>But what I want is a more permanent solution that doesn&#8217;t eliminate swap files
but gets them out of the directories of the files I&#8217;m editing.  To do this,
first create an empty directory; I used $HOME/.vim_swap so that it would be
in my home directory but hidden from normal directory listings.  Open up your
.vimrc file and add the following line, adjusting to match the directory you
created.</p>

<pre><code>set directory^=$HOME/.vim_swap//   "put all swap files together in one place
</code></pre>

<p>The <code>directory</code> setting is a comma separated list of directories where vim
should place swapfiles.  Each path is tried in order and the first one that
doesn&#8217;t fail (because it doesn&#8217;t exist or for permissions reasons) is where vim
will create swapfiles.  By default on unix systems, it it set to
&#8220;<code>.,~/tmp,/var/tmp,/tmp</code>&#8221; which means it first tries &#8220;.&#8221; or the same directory
as the file being edited.  By using ^=, we tell vim to prepend the given value
to those already set (the defaults).  This allows vim to fall back to other
directories should the specified one fail for some reason (directory doesn&#8217;t
exist or disk is full for example).  The double slash at the end tells vim to
form swap file names using the full path of the edited file, replacing / with %,
avoiding name conflicts.  See <code>:help directory</code> for more details.</p>

<p>Save the .vimrc file and you&#8217;re done.  All new vim sessions will now attempt to
use the new directory for swap files.  You can see the new value by typing
<code>:set directory?</code> in a new vim session and you can verify that swap files are
being put in the right place by doing a directory listing of the new folder.</p>

<pre><code>$ ls ~/.vim_swap
%home%bstump%Dropbox%blog%vim_swapfiles.txt.swp
%home%bstump%.vimrc.swp
</code></pre>

<p>Now you have full swap file support in vim without cluttering up source
directories and confusing sync programs like Dropbox.</p>]]></content:encoded>
			<wfw:commentRss>http://weevilgenius.net/2010/10/vim-tip-relocating-vim-swapfiles/feed/</wfw:commentRss>
		<slash:comments>3</slash:comments>
		</item>
		<item>
		<title>MySQL Explain &#8211; Reference</title>
		<link>http://weevilgenius.net/2010/09/mysql-explain-reference/</link>
		<comments>http://weevilgenius.net/2010/09/mysql-explain-reference/#comments</comments>
		<pubDate>Tue, 07 Sep 2010 23:33:00 +0000</pubDate>
		<dc:creator>Barry Stump</dc:creator>
				<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://weevilgenius.net/?p=24</guid>
		<description><![CDATA[The EXPLAIN command in MySQL is an essential diagnostic tool for any database developer. With it you can learn the details of how the server processes any query and therefore where to spend your time optimizing the query and/or the server indexes. It should be the first thing you check when facing a slow query. [...]]]></description>
			<content:encoded><![CDATA[<p>The EXPLAIN command in MySQL is an essential diagnostic tool for any database
developer.  With it you can learn the details of how the server processes any
query and therefore where to spend your time optimizing the query and/or the
server indexes.  It should be the first thing you check when facing a slow
query.</p>

<span id="more-24"></span>

<h1>Sample Data</h1>

<p>For the examples below, I&#8217;m using the &#8220;world&#8221; database which you can download
from <a href="http://dev.mysql.com/doc/index-other.html">the MySQL developer website</a>.  It contains city, country, and language
data for many parts of the world, and is simple to understand, while having
enough data for the query plans to be realistic.</p>

<p>The database is organized into three tables, City, Country, and CountryLanguage
which look like this:</p>

<p><strong>City Table</strong> (4079 rows)</p>

<pre><code>CREATE TABLE City
(
  ID            INT(11)  NOT NULL AUTO_INCREMENT,
  Name          CHAR(35) NOT NULL,
  CountryCode   CHAR(3)  NOT NULL,
  District      CHAR(20) NOT NULL,
  Population    INT(11)  NOT NULL default '0',

  PRIMARY KEY (ID)
) ENGINE=MyISAM CHARSET=latin1
</code></pre>

<p><strong>Country Table</strong> (239 rows)</p>

<pre><code>CREATE TABLE `Country` (
  Code              CHAR(3)     NOT NULL,
  Name              CHAR(52)    NOT NULL,
  Continent         ENUM('Asia',
                         'Europe',
                         'North America',
                         'Africa',
                         'Oceania',
                         'Antarctica',
                         'South America'
                    )           NOT NULL DEFAULT 'Asia',
  Region            CHAR(26)    NOT NULL,
  SurfaceArea       FLOAT(10,2) NOT NULL DEFAULT '0.00',
  IndepYear         SMALLINT(6)     NULL,
  Population        INT(11)     NOT NULL DEFAULT '0',
  LifeExpectancy    FLOAT(3,1)      NULL,
  GNP               FLOAT(10,2)     NULL,
  GNPOld            FLOAT(10,2)     NULL,
  LocalName         CHAR(45)    NOT NULL,
  GovernmentForm    CHAR(45)    NOT NULL,
  HeadOfState       CHAR(60)        NULL,
  Capital           INT(11)         NULL,
  Code2             CHAR(2)     NOT NULL,

  PRIMARY KEY (Code)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
</code></pre>

<p><strong>CountryLanguage Table</strong> (984 rows)</p>

<pre><code>CREATE TABLE CountryLanguage (
  CountryCode CHAR(3)       NOT NULL,
  Language    CHAR(30)      NOT NULL,
  IsOfficial  ENUM('T','F') NOT NULL DEFAULT 'F',
  Percentage  FLOAT(4,1)    NOT NULL DEFAULT '0.0',

  PRIMARY KEY  (CountryCode, Language)
) ENGINE=MyISAM DEFAULT CHARSET=latin1
</code></pre>

<h1>Using EXPLAIN</h1>

<p>Type &#8220;EXPLAIN&#8221; followed by any SELECT type query and the server will return a
number of useful pieces of information for each table referenced in the query.</p>

<ul>
<li><strong>id</strong> &#8211; server assigned integer (starting from 1) indicating the order
         in which the query optimizer decided to process the tables. You can
         force your own order by using the <code>STRAIGHT_JOIN</code> query hint.</li>
<li><strong>select_type</strong> &#8211; The type of query.  Will be one of the following:

<ul>
<li><code>SIMPLE</code> &#8211; Vanilla query without unions or subqueries</li>
<li><code>PRIMARY</code> &#8211; Outermost SELECT query</li>
<li><code>UNION</code> &#8211; Second or later SELECT query of a union</li>
<li><code>DEPENDENT UNION</code> &#8211; Second or later SELECT query of a union which is
                  dependent on an outer query</li>
<li><code>SUBQUERY</code> &#8211; First SELECT query in a subquery</li>
<li><code>DEPENDENT SUBQUERY</code> &#8211; First SELECT query in a subquery, dependent on
                     an outer query</li>
<li><code>DERIVED</code> &#8211; SELECT query used as a table in a FROM clause</li>
</ul></li>
<li><strong>table</strong> &#8211; The table name or alias</li>
<li><strong>type</strong> &#8211; The access type (sometimes called the join type).  <em>This is the
           single most important piece of information given</em>, as it indicates
           how efficiently the server was able to retrieve the requested
           rows of data.  I cover each of the access types in detail below.</li>
<li><strong>possible_keys</strong> &#8211; Indexes which the server could possibly use to retrieve
                    the requested rows from this table.  Prior to version
                    5.0, MySQL was only able to use a single index per table
                    in a query.  5.0 added the index_merge access type which
                    allows multiple indexes to be used in certain cases.</li>
<li><strong>key</strong> &#8211; The index selected (if any) for this table.  The index used is
          chosen based on the estimated amount of work the query optimizer
          thinks using each index will require.  The current query optimizer
          attempts to minimize disk reads.  A NULL value here means no index
          will be used, indicating a full table scan.  You can use the
          <code>FORCE INDEX</code>, <code>USE INDEX</code>, and <code>IGNORE INDEX</code> query hints to
          guide the optimizer.</li>
<li><strong>key_len</strong> &#8211; The number of bytes of the chosen index that the server will
              actually use.  Mostly useful for determining how many parts
              of a multi-part index the server used.</li>
<li><strong>rows</strong> &#8211; Estimated number of rows the optimizer thinks the server will
           have to examine in order to retrieve the requested rows.  This
           number is based on table statistics and index selectivity.</li>
<li><strong>Extra</strong> &#8211; Additional information pertaining to this table.

<ul>
<li><code>Distinct</code> &#8211; Server is able to perform an optimization where it will stop
           searching for more rows for the current row combination after
           it has found the first matching row</li>
<li><code>Not exists</code> &#8211; Server is able to do a LEFT JOIN optimization where it will
             not examine more rows in this table for the current row
             combination after it finds one row that matches the JOIN
             criteria.</li>
<li><code>range checked for each record</code> &#8211; Server found no good index to use, but
            checks indexes for each row.  Slow, but faster than a full
            table scan.</li>
<li><code>Using filesort</code> &#8211; Server will need to do an extra pass to sort the rows.</li>
<li><code>Using index</code> &#8211; Server is able to return rows directly from the index
            instead of doing an additional seek to read the actual rows.
            Generally good news.</li>
<li><code>Using temporary</code> &#8211; Server will need to create a temporary table to hold
            results.  Often occurs with differing GROUP BY and ORDER BY
            columns.</li>
<li><code>Using where</code> &#8211; Server is using values in the WHERE clause to limit
            returned rows.</li>
</ul></li>
</ul>

<h1>Access Types</h1>

<p>Here in order of fastest to slowest are all of the possible access types that
MySQL can use when retrieving rows from a table.</p>

<h2>system</h2>

<p>This is a special case of the <em>const</em> access type where the data source is a
system table.</p>

<h2>const</h2>

<p>This access type is used when the table has a primary key (or unique index)
where each part is compared to a fixed value.  Only a single row (at most) can
match, and therefore the result values can be treated as constants in the rest
of the query.  This is extremely fast.</p>

<pre><code>EXPLAIN SELECT * FROM City WHERE ID = 1
</code></pre>

<table>
<thead>
<tr>
  <th>id</th>
  <th>select_type</th>
  <th>table</th>
  <th>type</th>
  <th>possible_keys</th>
  <th>key</th>
  <th>key_len</th>
  <th>ref</th>
  <th>rows</th>
  <th>Extra</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>SIMPLE</td>
  <td>City</td>
  <td><strong>const</strong></td>
  <td>PRIMARY</td>
  <td>PRIMARY</td>
  <td>4</td>
  <td>const</td>
  <td>1</td>
  <td></td>
</tr>
</tbody>
</table>

<h2>eq_ref</h2>

<p>This access type is used when a join uses all parts of a unique, non-nullable
index.  A single row (at most) is read from the table for each combination of
rows returned from the other joins.  This is the most efficient access type for
most joins.</p>

<pre><code>EXPLAIN SELECT City.Name, Country.Name AS Country
        FROM City
        INNER JOIN Country ON Country.Code = City.CountryCode
        WHERE City.Name LIKE 'S%';
</code></pre>

<table>
<thead>
<tr>
  <th>id</th>
  <th>select_type</th>
  <th>table</th>
  <th>type</th>
  <th>possible_keys</th>
  <th>key</th>
  <th>key_len</th>
  <th>ref</th>
  <th>rows</th>
  <th>Extra</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>SIMPLE</td>
  <td>City</td>
  <td>ALL</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>4079</td>
  <td>Using where</td>
</tr>
<tr>
  <td>1</td>
  <td>SIMPLE</td>
  <td>Country</td>
  <td><strong>eq_ref</strong></td>
  <td>PRIMARY</td>
  <td>PRIMARY</td>
  <td>3</td>
  <td>world.City.CountryCode</td>
  <td>1</td>
  <td></td>
</tr>
</tbody>
</table>

<p>In this example, the server is able to locate a single row from the Country
table via its primary key for each City row returned.</p>

<h2>ref</h2>

<p>Identical to <em>eq_ref</em> except that one or more rows are read from the table
instead of a single row.  This happens when either the join condition uses only
the left most part of a multicolumn index or the index is not unique but does
not contain NULL values.</p>

<pre><code>EXPLAIN SELECT C.Name, L.Language
        FROM Country C
        INNER JOIN CountryLanguage L ON L.CountryCode = C.Code
        WHERE C.Name LIKE 'P%';
</code></pre>

<table>
<thead>
<tr>
  <th>id</th>
  <th>select_type</th>
  <th>table</th>
  <th>type</th>
  <th>possible_keys</th>
  <th>key</th>
  <th>key_len</th>
  <th>ref</th>
  <th>rows</th>
  <th>Extra</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>SIMPLE</td>
  <td>C</td>
  <td>ALL</td>
  <td>PRIMARY</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>239</td>
  <td>Using where</td>
</tr>
<tr>
  <td>1</td>
  <td>SIMPLE</td>
  <td>L</td>
  <td><strong>ref</strong></td>
  <td>PRIMARY</td>
  <td>PRIMARY</td>
  <td>3</td>
  <td>world.C.Code</td>
  <td>9</td>
  <td>Using index</td>
</tr>
</tbody>
</table>

<p>In this example, the CountryLanguage table has a two part primary key on
(CountryCode, Language), but we are only matching on CountryCode, so multiple
rows can be returned for each matching Country.</p>

<h2>fulltext</h2>

<p>The join is performed using a FULLTEXT index match.  Note that FULLTEXT indexes
and operations are not available in all MySQL database engines.</p>

<h2>ref_or_null</h2>

<p>Identical to <em>ref</em> access type but with an extra search for nullability.</p>

<pre><code>ALTER TABLE Country ADD INDEX ix_Country_IndepYear (IndepYear);
EXPLAIN SELECT Name, IndepYear
        FROM Country
        WHERE IndepYear = 1975
          OR IndepYear IS NULL;
ALTER TABLE Country DROP INDEX ix_Country_IndepYear;
</code></pre>

<table>
<thead>
<tr>
  <th>id</th>
  <th>select_type</th>
  <th>table</th>
  <th>type</th>
  <th>possible_keys</th>
  <th>key</th>
  <th>key_len</th>
  <th>ref</th>
  <th>rows</th>
  <th>Extra</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>SIMPLE</td>
  <td>Country</td>
  <td><strong>ref_or_null</strong></td>
  <td>ix_Country_IndepYear</td>
  <td>ix_Country_IndepYear</td>
  <td>3</td>
  <td>const</td>
  <td>35</td>
  <td>Using where</td>
</tr>
</tbody>
</table>

<p>For this example, we have to add an index to a nullable column.  Pretty much all
queries using this access type have a characteristic <code>OR [COLUMN] IS NULL</code>
clause in them.</p>

<h2>index_merge</h2>

<p>Multiple indexes on the same table are used to resolve multiple range
conditions, then the results are merged together.  Ordinarily, MySQL will use
at most one index per table in a query.  Using table and index statistics, it
selects a single index from those available which it estimates will help the
most with the given table and conditions.  MySQL 5 added a new access type
which can use multiple indexes under certain conditions.</p>

<pre><code>ALTER TABLE Country ADD INDEX ix_Country_Region (Region);
ALTER TABLE Country ADD INDEX ix_Country_Continent (Continent);
EXPLAIN SELECT Name, Continent, Region
        FROM Country
        WHERE Continent = 'Europe'
          OR Region = 'Nordic Countries';
ALTER TABLE Country DROP INDEX ix_Country_Region;
ALTER TABLE Country DROP INDEX ix_Country_Continent;
</code></pre>

<table>
<thead>
<tr>
  <th>id</th>
  <th>select_type</th>
  <th>table</th>
  <th>type</th>
  <th>possible_keys</th>
  <th>key</th>
  <th>key_len</th>
  <th>ref</th>
  <th>rows</th>
  <th>Extra</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>SIMPLE</td>
  <td>Country</td>
  <td><strong>index_merge</strong></td>
  <td>ix_Country_Region,ix_Country_Continent</td>
  <td>ix_Country_Continent,ix_Country_Region</td>
  <td>1,26</td>
  <td>NULL</td>
  <td>44</td>
  <td>Using union(ix_Country_Continent,ix_Country_Region); Using where</td>
</tr>
</tbody>
</table>

<p>In this example, we add an index on Continent and another on Region.  Before
version 5, the server would have been forced to select only one of the indexes
to pull rows,  resorting to scanning the results to eliminate rows not matching
the other clause.  Here, however, MySQL is able to use both indexes and merge
(union in this case) the results.</p>

<h2>unique_subquery</h2>

<p>Similar to <em>ref</em> but for subqueries of the type <code>IN (SELECT ... FROM ... WHERE ...)</code>
with a unique, non-nullable index.  The subquery is completely replaced with an
index lookup function.</p>

<pre><code>EXPLAIN SELECT Name
        FROM City
        WHERE CountryCode IN (SELECT Code FROM Country
                              WHERE Continent='North America');
</code></pre>

<table>
<thead>
<tr>
  <th>id</th>
  <th>select_type</th>
  <th>table</th>
  <th>type</th>
  <th>possible_keys</th>
  <th>key</th>
  <th>key_len</th>
  <th>ref</th>
  <th>rows</th>
  <th>Extra</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>PRIMARY</td>
  <td>City</td>
  <td>ALL</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>4079</td>
  <td>Using where</td>
</tr>
<tr>
  <td>2</td>
  <td>DEPENDENT SUBQUERY</td>
  <td>Country</td>
  <td><strong>unique_subquery</strong></td>
  <td>PRIMARY</td>
  <td>PRIMARY</td>
  <td>3</td>
  <td>func</td>
  <td>1</td>
  <td>Using where</td>
</tr>
</tbody>
</table>

<p>In this example, we use a subquery to gather all the countries in North America
in order to limit the cities.  While this produces the desired result, it&#8217;s
more efficiently rewritten as an INNER JOIN like this:</p>

<pre><code>EXPLAIN SELECT Ci.Name
        FROM City Ci
        INNER JOIN Country Co ON Co.Code = Ci.CountryCode
          AND Co.Continent='North America';
</code></pre>

<table>
<thead>
<tr>
  <th>id</th>
  <th>select_type</th>
  <th>table</th>
  <th>type</th>
  <th>possible_keys</th>
  <th>key</th>
  <th>key_len</th>
  <th>ref</th>
  <th>rows</th>
  <th>Extra</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>SIMPLE</td>
  <td>Ci</td>
  <td>ALL</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>4079</td>
  <td></td>
</tr>
<tr>
  <td>1</td>
  <td>SIMPLE</td>
  <td>Co</td>
  <td>eq_ref</td>
  <td>PRIMARY</td>
  <td>PRIMARY</td>
  <td>3</td>
  <td>world.Ci.CountryCode</td>
  <td>1</td>
  <td>Using where</td>
</tr>
</tbody>
</table>

<p>This produces the same result but uses the <em>eq_ref</em> access type which is
generally faster.</p>

<h2>index_subquery</h2>

<p>Similar to <em>unique_subquery</em> except that the index is not unique.</p>

<pre><code>ALTER TABLE Country ADD INDEX ix_Country_Code (Code);
EXPLAIN SELECT Name
        FROM City WHERE CountryCode IN (SELECT Code
                                        FROM Country IGNORE INDEX (PRIMARY)
                                        WHERE Continent = 'North America');
ALTER TABLE Country DROP INDEX ix_Country_Code;
</code></pre>

<table>
<thead>
<tr>
  <th>id</th>
  <th>select_type</th>
  <th>table</th>
  <th>type</th>
  <th>possible_keys</th>
  <th>key</th>
  <th>key_len</th>
  <th>ref</th>
  <th>rows</th>
  <th>Extra</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>PRIMARY</td>
  <td>City</td>
  <td>ALL</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>4079</td>
  <td>Using where</td>
</tr>
<tr>
  <td>2</td>
  <td>DEPENDENT SUBQUERY</td>
  <td>Country</td>
  <td><strong>index_subquery</strong></td>
  <td>ix_Country_Code</td>
  <td>ix_Country_Code</td>
  <td>3</td>
  <td>func</td>
  <td>1</td>
  <td>Using where</td>
</tr>
</tbody>
</table>

<p>Here we have to add a non-unique index to Country.Code and force the query
optimizer to ignore the primary key in order to get the <em>index_subquery</em> access
type.</p>

<h2>range</h2>

<p>This access type is used when an index exists for the given column(s) and a
range of values is requested.  Possible operators include =, &lt;>, >, >=, &lt;, &lt;=,
IS NULL, &lt;=>, BETWEEN, LIKE, and IN.  Note that <em>range</em> only occurs with LIKE
when the first character is not a wildcard.</p>

<pre><code>EXPLAIN SELECT Name FROM City WHERE ID BETWEEN 10 AND 100;
EXPLAIN SELECT Name FROM City WHERE ID &lt; 10;
EXPLAIN SELECT Name FROM City WHERE ID IN (10,20,30,40);
</code></pre>

<table>
<thead>
<tr>
  <th>id</th>
  <th>select_type</th>
  <th>table</th>
  <th>type</th>
  <th>possible_keys</th>
  <th>key</th>
  <th>key_len</th>
  <th>ref</th>
  <th>rows</th>
  <th>Extra</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>SIMPLE</td>
  <td>City</td>
  <td><strong>range</strong></td>
  <td>PRIMARY</td>
  <td>PRIMARY</td>
  <td>4</td>
  <td>NULL</td>
  <td>91</td>
  <td>Using where</td>
</tr>
</tbody>
</table>

<h2>index</h2>

<p>A bit of a misnomer, <em>index</em> access type should probably be called <em>index_scan</em>
as it results in a full scan of the index tree.  This is usually better than a
full table scan because the size of the index is usually smaller than the raw
data.  It occurs when each of the requested columns is contained in a single
index, but none of the other more efficient access types apply.  It always
results in the Extra column of the EXPLAIN output showing &#8220;using index&#8221;.</p>

<pre><code>EXPLAIN SELECT CountryCode
        FROM CountryLanguage
        WHERE Language = 'English';
</code></pre>

<table>
<thead>
<tr>
  <th>id</th>
  <th>select_type</th>
  <th>table</th>
  <th>type</th>
  <th>possible_keys</th>
  <th>key</th>
  <th>key_len</th>
  <th>ref</th>
  <th>rows</th>
  <th>Extra</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>SIMPLE</td>
  <td>CountryLanguage</td>
  <td><strong>index</strong></td>
  <td>NULL</td>
  <td>PRIMARY</td>
  <td>33</td>
  <td>NULL</td>
  <td>984</td>
  <td>Using where; Using index</td>
</tr>
</tbody>
</table>

<p>Here, even though we only reference columns in the primary key for the
CountryLanguage table, it lists CountryCode before Language, so <em>eq_ref</em> is not
available, and the engine has to scan the full index to get the values we&#8217;ve
requested.</p>

<h2>ALL</h2>

<p>A full table scan is performed for each combination of rows returned from
previous tables.  This can occur because no index is available for the
conditions given, or no WHERE clause is given, or simply because the query
optimizer estimates that a full table scan would be less costly than one
of the other access methods above (usually only when the number of rows is small
or index selectivity is poor).  Typically, seeing the <em>ALL</em> access type
indicates poor performance, and you can improve performance by adding
appropriate indexes or rewriting the query.</p>

<pre><code>EXPLAIN SELECT Code
        FROM Country
        WHERE Continent = 'Asia';
</code></pre>

<table>
<thead>
<tr>
  <th>id</th>
  <th>select_type</th>
  <th>table</th>
  <th>type</th>
  <th>possible_keys</th>
  <th>key</th>
  <th>key_len</th>
  <th>ref</th>
  <th>rows</th>
  <th>Extra</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>SIMPLE</td>
  <td>Country</td>
  <td><strong>ALL</strong></td>
  <td>NULL</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>NULL</td>
  <td>239</td>
  <td>Using where</td>
</tr>
</tbody>
</table>

<p>Here we see the <em>ALL</em> access type because there is no index available on the
Country.Continent column, and the server is forced to scan each data record
looking for matches.  If we add an index on the Continent column, we can bump
the access type all the way up to <em>ref</em>.</p>

<pre><code>ALTER TABLE Country ADD INDEX ix_Country_Continent (Continent);
EXPLAIN SELECT Code
        FROM Country
        WHERE Continent = 'Asia';
ALTER TABLE Country DROP INDEX ix_Country_Continent;
</code></pre>

<table>
<thead>
<tr>
  <th>id</th>
  <th>select_type</th>
  <th>table</th>
  <th>type</th>
  <th>possible_keys</th>
  <th>key</th>
  <th>key_len</th>
  <th>ref</th>
  <th>rows</th>
  <th>Extra</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>SIMPLE</td>
  <td>Country</td>
  <td>ref</td>
  <td>ix_Country_Continent</td>
  <td>ix_Country_Continent</td>
  <td>1</td>
  <td>const</td>
  <td>42</td>
  <td>Using where</td>
</tr>
</tbody>
</table>]]></content:encoded>
			<wfw:commentRss>http://weevilgenius.net/2010/09/mysql-explain-reference/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>Vim Tip &#8211; Highlighting long rows</title>
		<link>http://weevilgenius.net/2010/07/vim-tip-highlighting-long-rows/</link>
		<comments>http://weevilgenius.net/2010/07/vim-tip-highlighting-long-rows/#comments</comments>
		<pubDate>Tue, 20 Jul 2010 19:26:18 +0000</pubDate>
		<dc:creator>Barry Stump</dc:creator>
				<category><![CDATA[vim]]></category>

		<guid isPermaLink="false">http://weevilgenius.net/?p=21</guid>
		<description><![CDATA[Occasionally when using vim to edit large amounts of text, such as writing a blog post, or formatting a large code comment, I wish to hard wrap the lines at a certain number of columns for ease of reading. Here is a simple vim function which uses the built in syntax highlighting capabilities of vim [...]]]></description>
			<content:encoded><![CDATA[<p>Occasionally when using vim to edit large amounts of text, such as writing a
blog post, or formatting a large code comment, I wish to hard wrap the lines at
a certain number of columns for ease of reading.  Here is a simple vim function
which uses the built in syntax highlighting capabilities of vim to highlight any
line which extends beyond 80 columns of text, allowing me to add line breaks as
I see fit, while leaving long lines of code alone.  As an added bonus, it looks
really nice in the gui version of vim while still remaining usable in the
console version.</p>

<span id="more-21"></span>

<p>Here&#8217;s a sample of text with the long row highlighting turned off:</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2010/07/vim_overlength_before.png" alt="long row highlighting disabled" title="long row highlighting disabled" /></p>

<p>And here&#8217;s what it looks like when turned on:</p>

<p><img src="http://weevilgenius.net/wp-content/uploads/2010/07/vim_overlength_after.png" alt="long row highlighting enabled" title="long row highlighting enabled" /></p>

<p><strong>Function</strong></p>

<pre><code>" Highlight rows longer than 80 characters
function ToggleOverLengthHi()
    if exists("b:overlengthhi") &amp;&amp; b:overlengthhi
        highlight clear OverLength
        let b:overlengthhi = 0
        echo "overlength hilight off"
    else
        " adjust colors/styles as desired
        highlight OverLength ctermbg=darkred gui=undercurl guisp=blue
        " change '81' to be 1+(number of columns)
        match OverLength /\%81v.\+/
        let b:overlengthhi = 1
        echo "overlength hilight on"
    endif
endfunction
</code></pre>

<p>Now you can bind (map in vim lingo) the function to an unused key to toggle the
highlight.  In this example, I&#8217;ve used F1 which is normally mapped to :help</p>

<p><strong>Keybind</strong></p>

<pre><code>map &lt;silent&gt; &lt;F1&gt; &lt;Esc&gt;:call ToggleOverLengthHi()&lt;CR&gt;
</code></pre>

<p>To use this in your own vim sessions, add the function and the key binding line
to your .vimrc file.</p>

<p>A couple of notes: The highlighting only works if you have the syntax
highlighting system turned on, for example with <code>syntax on</code> in your .vimrc file.
The long row highlighting is off by default, and the toggle is set per buffer,
so if you have multiple files open in the same vim session, you can toggle each
one separately.</p>]]></content:encoded>
			<wfw:commentRss>http://weevilgenius.net/2010/07/vim-tip-highlighting-long-rows/feed/</wfw:commentRss>
		<slash:comments>5</slash:comments>
		</item>
		<item>
		<title>MySQL Proxy</title>
		<link>http://weevilgenius.net/2010/07/mysql-proxy/</link>
		<comments>http://weevilgenius.net/2010/07/mysql-proxy/#comments</comments>
		<pubDate>Thu, 08 Jul 2010 21:11:00 +0000</pubDate>
		<dc:creator>Barry Stump</dc:creator>
				<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://weevilgenius.net/?p=17</guid>
		<description><![CDATA[MySQL Proxy is a handy little utility for MySQL 5.0 and above which can be used to troubleshoot the database parts of an existing application without modifying the code to add lots of debugging statements. It speaks the MySQL binary protocol and can be placed between an application and its database, allowing you to inspect, [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://dev.mysql.com/downloads/mysql-proxy/">MySQL Proxy</a> is a handy little utility for MySQL 5.0 and above which can be
used to troubleshoot the database parts of an existing application without
modifying the code to add lots of debugging statements.  It speaks the MySQL
binary protocol and can be placed between an application and its database,
allowing you to inspect, log, and even manipulate the proxied queries and
results.</p>

<span id="more-17"></span>

<h1>Making the Connection</h1>

<p>Suppose you have a web application and you wish to know how much database
activity is generated by requesting a single page.  Perhaps page response times
are slow, or the application uses an ORM layer which you suspect is overly
chatty, or you want to see how well object caching works in different scenarios.
Time to set up the proxy.</p>

<p>In this example, I&#8217;m going to use the mysql command line as the client, and my
server is running locally at 127.0.0.1:3306</p>

<p>Download the latest version of the proxy from the <a href="http://dev.mysql.com/downloads/mysql-proxy/">MySQL website</a>
and unpack it on a machine which can be reached by network from the application
you want to test.  Now you have to tell it where to listen for incoming
connections, via the <code>--proxy-address=host:port</code> parameter.  You can omit the
host part to bind to all available ip addresses.  Since my local server is
using the default port of 3306, I&#8217;ll use <code>:3307</code> for the proxy.</p>

<p>Next, you need to tell the proxy where the real server is.  You do this via the
<code>--proxy-backend-addresses=host:port</code> parameter.  The proxy can support multiple
back end hosts, just repeat the parameter and change the host:port values.  If
you use multiple back end hosts, the proxy will connect to each one in round
robin fashion, becoming a simple load balancer.  In my case, I&#8217;ll just point to
my local instance of <code>127.0.0.1:3306</code>.</p>

<p>Time to test the basic proxy.  In one window, I launch the proxy, and leave it
running (you can terminate the proxy by typing CTRL-C).</p>

<pre><code>mysql-proxy --proxy-address=:3307 --proxy-backend-addresses=127.0.0.1:3306
</code></pre>

<p>I can now open another command window to test the proxy with the mysql client:</p>

<pre><code>$ mysql -h 127.0.0.1 -P 3307 -u root -p -A mysql
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 9
Server version: 5.0.75-0ubuntu10.5 (Ubuntu)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql&gt; SHOW TABLES;
+---------------------------+
| Tables_in_mysql           |
+---------------------------+
| columns_priv              |
| db                        |
| func                      |
| help_category             |
| help_keyword              |
| help_relation             |
| help_topic                |
| host                      |
| proc                      |
| procs_priv                |
| tables_priv               |
| time_zone                 |
| time_zone_leap_second     |
| time_zone_name            |
| time_zone_transition      |
| time_zone_transition_type |
| user                      |
+---------------------------+
17 rows in set (0.00 sec)
</code></pre>

<p>Note that I had to specify 127.0.0.1 as the host in order to get the client to
make a network connection to the server (the proxy, in this case), instead of
using the system mysql socket.  MySQL proxy only works with a network
connection.</p>

<h1>Basic Logging</h1>

<p>So far, my example is pretty useless.  Sure, I can connect to the proxy which
then relays commands on to the server, but I can&#8217;t do anything useful with it
yet.  The real power of the proxy is the ability to write scripts to inspect,
log, redirect, and even manipulate the queries and responses passing through.
You invoke this by writing your script using the Lua programming language, and
then telling the proxy to run it with the <code>--proxy-lua-script=/path/to/script.lua</code>
parameter.</p>

<p>Here is a script which simply prints out each query sent to the server.</p>

<pre><code>-- dump_queries.lua
-- Prints a line for each query received from the client

function read_query(packet)
    if packet:byte() == proxy.COM_QUERY then
        print("Query: " .. packet:sub(2))
    end
end
</code></pre>

<p>I save the file as dump_queries.lua, and restart the proxy:</p>

<pre><code>mysql-proxy --proxy-address=:3307 --proxy-backend-addresses=127.0.0.1:3306 --proxy-lua-script=/home/bstump/mysql_proxy/dump_queries.lua
</code></pre>

<p>Now I can connect to the proxy and make some queries:</p>

<pre><code>$ mysql -h 127.0.0.1 -P 3307 -u root -p -A mysql
Enter password:
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 13
Server version: 5.0.75-0ubuntu10.5 (Ubuntu)

Type 'help;' or '\h' for help. Type '\c' to clear the buffer.

mysql&gt; SELECT NOW();
+---------------------+
| NOW()               |
+---------------------+
| 2010-07-08 11:07:43 |
+---------------------+
1 row in set (0.00 sec)

mysql&gt; SELECT COUNT(*) FROM user;
+----------+
| COUNT(*) |
+----------+
|        6 |
+----------+
1 row in set (0.00 sec)
</code></pre>

<p>In the proxy window, you can see all of the queries, including one at the top
which the client sent behind the scenes when connecting to the server.</p>

<pre><code>Query: select @@version_comment limit 1
Query: SELECT NOW()
Query: SELECT COUNT(*) FROM user
</code></pre>

<h1>Advanced Logging</h1>

<p>Here is a more advanced script which prints the query time and the number of
rows returned as well as the query.  It is a simplified version of a more robust
logger script available <a href="http://forge.mysql.com/tools/tool.php?id=81">here</a>.</p>

<pre><code>-- dump_queries_with_times.lua
-- Prints a line for each query received from the client along with times

function read_query(packet)
    if packet:byte() == proxy.COM_QUERY then
        -- inject the query so that we can inspect the results and get the times
        proxy.queries:append(1, packet, { resultset_is_needed = true })

        return proxy.PROXY_SEND_QUERY
    end
end

-- read_query_result() is only called for queries injected via read_query()
function read_query_result(inj)
    local row_count = 0
    local res = assert(inj.resultset)
    local num_cols = string.byte(res.raw, 1)
    if num_cols &gt; 0 and num_cols &lt; 255 then
        for row in inj.resultset.rows do
            row_count = row_count + 1
        end
    end

    local log_string = string.format("Query [%.3f ms]: %s [%d rows]",
        (inj.query_time / 1000),
        inj.query,
        row_count)

    print(log_string)
end
</code></pre>

<p>The output looks like this:</p>

<pre><code>Query [0.423 ms]: select @@version_comment limit 1 [1 rows]
Query [0.496 ms]: SELECT DATABASE() [1 rows]
Query [149.016 ms]: SELECT data_source_id, COUNT(*) FROM event GROUP BY data_source_id [5 rows]
Query [94.104 ms]: SELECT artist_id FROM artist [17065 rows]
</code></pre>

<h1>Other Uses</h1>

<p>Because of the ability to write your own scripts, there are lots of interesting
uses for MySQL proxy.  In addition to the types of logging covered above, people
have written scripts to:</p>

<ul>
<li>Rewrite queries from one schema to another</li>
<li>Perform cross tabulations</li>
<li>Return information and perform operations normally only available to mysqladmin</li>
<li>Load balance/server fallback/pool connections</li>
<li>Rewrite/obfuscate connection credentials</li>
<li>Gather query statistics</li>
<li>Automatically log warnings and errors if generated</li>
<li>Split read and write queries to separate servers</li>
<li>Perform QA operations on database connections</li>
<li>Measure bandwidth by user or session</li>
<li>Selectively filter or block certain queries</li>
<li>Provide some server admin capabilities via a SQL interface</li>
</ul>

<h1>Useful Links</h1>

<ul>
<li><a href="http://dev.mysql.com/downloads/mysql-proxy/">MySQL Proxy Download</a></li>
<li><a href="http://dev.mysql.com/doc/refman/5.1/en/mysql-proxy.html">MySQL Proxy Documentation</a></li>
<li><a href="http://forge.mysql.com/wiki/MySQL_Proxy">MySQL Proxy Wiki</a></li>
<li><a href="https://launchpad.net/mysql-proxy">MySQL Proxy Launchpad</a></li>
<li><a href="http://forge.mysql.com/tools/search.php?t=tag&amp;k=mysqlproxy">MySQL Proxy scripts</a></li>
</ul>]]></content:encoded>
			<wfw:commentRss>http://weevilgenius.net/2010/07/mysql-proxy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>MySQL Unions</title>
		<link>http://weevilgenius.net/2010/05/mysql-unions/</link>
		<comments>http://weevilgenius.net/2010/05/mysql-unions/#comments</comments>
		<pubDate>Tue, 25 May 2010 19:00:54 +0000</pubDate>
		<dc:creator>Barry Stump</dc:creator>
				<category><![CDATA[MySQL]]></category>

		<guid isPermaLink="false">http://weevilgenius.net/?p=15</guid>
		<description><![CDATA[A union is a type of join which allows you to glue two or more separate SELECT queries into a single result set. The structure of each query needs to be the same, meaning that the number of columns and their data types should match for all queries used in the union, and the column [...]]]></description>
			<content:encoded><![CDATA[<p>A union is a type of join which allows you to glue two or more separate SELECT
queries into a single result set.  The structure of each query needs to be the
same, meaning that the number of columns and their data types should match for
all queries used in the union, and the column names or aliases from the first
query determine the names of the final result columns.</p>

<span id="more-15"></span>

<p>In all of the examples below, I will use the sample data from the
<a href="http://weevilgenius.net/2010/04/mysql-basics-joins/">join post</a></p>

<p><strong>actor Table:</strong></p>

<table>
<thead>
<tr>
  <th>actor_id</th>
  <th>actor</th>
  <th>gender</th>
  <th>is_alive</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>Angelina Jolie</td>
  <td>F</td>
  <td>1</td>
</tr>
<tr>
  <td>2</td>
  <td>Béla Lugosi</td>
  <td>M</td>
  <td>0</td>
</tr>
<tr>
  <td>3</td>
  <td>Carrie Fisher</td>
  <td>F</td>
  <td>1</td>
</tr>
<tr>
  <td>4</td>
  <td>Daniel Craig</td>
  <td>M</td>
  <td>1</td>
</tr>
</tbody>
</table>

<p><strong>movie Table:</strong></p>

<table>
<thead>
<tr>
  <th>movie_id</th>
  <th>actor_id</th>
  <th>title</th>
  <th>year</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1</td>
  <td>1</td>
  <td>Mr. &amp; Mrs. Smith</td>
  <td>2005</td>
</tr>
<tr>
  <td>2</td>
  <td>1</td>
  <td>Lara Croft: Tomb Raider</td>
  <td>2001</td>
</tr>
<tr>
  <td>3</td>
  <td>2</td>
  <td>Dracula</td>
  <td>1931</td>
</tr>
<tr>
  <td>4</td>
  <td>3</td>
  <td>Star Wars: A New Hope</td>
  <td>1977</td>
</tr>
<tr>
  <td>5</td>
  <td>3</td>
  <td>Star Wars: The Empire Strikes Back</td>
  <td>1980</td>
</tr>
<tr>
  <td>6</td>
  <td>3</td>
  <td>Star Wars: Return of the Jedi</td>
  <td>1983</td>
</tr>
<tr>
  <td>7</td>
  <td>NULL</td>
  <td>Lion King</td>
  <td>1994</td>
</tr>
<tr>
  <td>8</td>
  <td>NULL</td>
  <td>Time Bandits</td>
  <td>1981</td>
</tr>
</tbody>
</table>

<p>The general structure of a union query looks like this (the parentheses are
optional but help separate the queries visually):</p>

<pre><code>(SELECT query1)
UNION [ALL | DISTINCT]
(SELECT query2)
...
</code></pre>

<p>Using our sample data, suppose you wanted a list of movies in which either Béla
Lugosi or Carrie Fisher appeared.  You could easily write this using a phrase
like <code>WHERE A.actor='Béla Lugosi' OR A.actor='Carrie Fisher'</code>, but we&#8217;re going
to use a UNION here:</p>

<pre class="brush:sql">(
  SELECT
    M.title,
    M.year
  FROM movie M
  INNER JOIN actor A
    ON A.actor_id = M.actor_id
    AND A.actor = 'Carrie Fisher'
)
UNION
(
  SELECT
    M.title,
    M.year
  FROM movie M
  INNER JOIN actor A
    ON A.actor_id = M.actor_id
    AND A.actor = 'Béla Lugosi'
);</pre>

<p><strong>results:</strong></p>

<table>
<thead>
<tr>
  <th>title</th>
  <th>year</th>
</tr>
</thead>
<tbody>
<tr>
  <td>Star Wars: A New Hope</td>
  <td>1977</td>
</tr>
<tr>
  <td>Star Wars: The Empire Strikes Back</td>
  <td>1980</td>
</tr>
<tr>
  <td>Star Wars: Return of the Jedi</td>
  <td>1983</td>
</tr>
<tr>
  <td>Dracula</td>
  <td>1931</td>
</tr>
</tbody>
</table>

<p>As you can see, the results are just the individual query results glued together.
How is this better than the single query version?  It isn&#8217;t in this example, but
the union allows us to do things that would be difficult or impossible to do
with a single query.  For example, you can sort the results within each query
separately (add an <code>ORDER BY</code> clause <em>inside</em> the parentheses) or sort the
entire combined result (add an <code>ORDER BY</code> clause <em>outside</em> the parentheses).</p>

<p>For a more involved example, suppose we wanted to see the most represented
actors in the 1980&#8242;s versus the 2000&#8242;s.  Splitting each decade into a separate
query yields the following union:</p>

<pre class="brush:sql">(
  SELECT
    '2000''s' AS decade,
    A.actor,
    COUNT(*) AS movies
  FROM actor A
  INNER JOIN movie M ON M.actor_id = A.actor_id
  WHERE M.year BETWEEN 2000 AND 2009
  GROUP BY A.actor
)
UNION
(
  SELECT
    '1980''s' AS decade,
    A.actor,
    COUNT(*) AS movies
  FROM actor A
  INNER JOIN movie M ON M.actor_id = A.actor_id
  WHERE M.year BETWEEN 1980 AND 1989
  GROUP BY A.actor
)
ORDER BY decade, movies DESC;</pre>

<p><strong>results:</strong></p>

<table>
<thead>
<tr>
  <th>decade</th>
  <th>actor</th>
  <th>movies</th>
</tr>
</thead>
<tbody>
<tr>
  <td>1980&#8242;s</td>
  <td>Carrie Fisher</td>
  <td>2</td>
</tr>
<tr>
  <td>2000&#8242;s</td>
  <td>Angelina Jolie</td>
  <td>2</td>
</tr>
</tbody>
</table>

<p>Although our sparse data set does not yield very interesting results, the UNION
provides a nice way to organize the queries and control the sorting of the
various parts.</p>

<h4>UNION ALL vs. UNION DISTINCT</h4>

<p>By default, MySQL removes duplicate entries from the merged result set before
returning the results.  This is equivalent to using <code>UNION DISTINCT</code>.  If you
need all results returned, whether or not they are duplicates, use <code>UNION ALL</code>.</p>

<h4>Using UNION to optimize OR conditions</h4>

<p>Before version 5.0, MySQL was unable to use more than one index per table in a
single query.  One side effect of this is that trying to use an OR expression on
two different columns, such as <code>WHERE col1='foo' OR col2='bar'</code> could perform
very poorly because MySQL could only utilize an index to limit results on one or
the other column, but not both.  By converting the query to a UNION, it is
possible to get the server to use two indexes and then merge the results.  MySQL
5 added the <em>index_merge</em> query plan which basically does this internally.</p>]]></content:encoded>
			<wfw:commentRss>http://weevilgenius.net/2010/05/mysql-unions/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
	</channel>
</rss>

