<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">

  <title><![CDATA[Trey Hunner]]></title>
  <link href="http://treyhunner.com/atom.xml" rel="self"/>
  <link href="http://treyhunner.com/"/>
  <updated>2012-03-03T22:02:21-08:00</updated>
  <id>http://treyhunner.com/</id>
  <author>
    <name><![CDATA[Trey Hunner]]></name>
    
  </author>
  <generator uri="http://octopress.org/">Octopress</generator>

  
  <entry>
    <title type="html"><![CDATA[Maintaining consistent coding conventions with EditorConfig]]></title>
    <link href="http://treyhunner.com/2012/02/editorconfig/"/>
    <updated>2012-02-17T00:00:00-08:00</updated>
    <id>http://treyhunner.com/2012/02/editorconfig</id>
    <content type="html"><![CDATA[<p>I often have indentation issues when coding because I work with many open
source (and closed source) projects that have different identation styles.  For
example, my <a href="https://github.com/treyhunner/jquery-formrestrict">jQuery formrestrict</a> uses 4 spaces for indentation,
<a href="https://github.com/kswedberg/jquery-expander">jQuery expander</a> uses 2 spaces, and <a href="https://github.com/gbirke/jquery_pagination">jQuery pagination</a> uses hard tabs.
I don&#8217;t want to change my text editor&#8217;s indentation settings every time I open
one of these files and using a plugin to auto-detect indentation is only a
partial solution.  To solve this problem I started a project I call
EditorConfig.</p>

<p><a href="http://editorconfig.org">EditorConfig</a> defines coding conventions for groups of files and instructs
text editors to adhere to these conventions.  To solve my problem, I just
create a file called <code>.editorconfig</code> that sets indentation for my files and
with my EditorConfig plugin installed Vim takes care of the rest.  Here&#8217;s an
example <code>.editorconfig</code> file I could add to my project that uses
<a href="https://github.com/kswedberg/jquery-expander">jQuery expander</a> and <a href="https://github.com/gbirke/jquery_pagination">jQuery pagination</a>:</p>

<figure class='code'><figcaption><span>.editorconfig</span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='ini'><span class='line'><span class="k">[*.js]</span>
</span><span class='line'><span class="na">indent_style</span> <span class="o">=</span> <span class="s">space</span>
</span><span class='line'><span class="na">indent_size</span> <span class="o">=</span> <span class="s">4</span>
</span><span class='line'>
</span><span class='line'><span class="k">[jquery.expander.js]</span>
</span><span class='line'><span class="na">indent_style</span> <span class="o">=</span> <span class="s">space</span>
</span><span class='line'><span class="na">indent_size</span> <span class="o">=</span> <span class="s">2</span>
</span><span class='line'>
</span><span class='line'><span class="k">[jquery.pagination.js]</span>
</span><span class='line'><span class="na">indent_style</span> <span class="o">=</span> <span class="s">tab</span>
</span><span class='line'><span class="na">indent_size</span> <span class="o">=</span> <span class="s">4</span>
</span></code></pre></td></tr></table></div></figure>


<p>With this <code>.editorconfig</code> file, all JavaScript files use 4 spaces for
indentation except for <code>jquery.expander.js</code> which uses 2 spaces and
<code>jquery.pagination.js</code> which uses hard tabs with a column width of 4.  If I put
my <code>.editorconfig</code> file under version control, other developers working on my
project can see the coding conventions I defined and if their text editor has
an EditorConfig plugin installed their editor will automatically use the
correct indentation as well.</p>

<p>Example EditorConfig files can be seen in my own projects (such as
<a href="https://github.com/treyhunner/dotfiles/blob/master/.editorconfig">in my dotfiles repo</a>) and in the
<a href="https://github.com/treyhunner/dotfiles/blob/master/.editorconfig">various EditorConfig plugin codebases</a>.  More
information on EditorConfig can be found at the
<a href="http://editorconfig.org">EditorConfig website</a>.</p>

<p>EditorConfig plugins are <a href="http://editorconfig.org/#download">available for 6 different editors</a>
now.  If you like the idea, <a href="http://editorconfig.org">try out EditorConfig</a> for your
favorite editor and <a href="https://groups.google.com/forum/?fromgroups#!forum/editorconfig">tell us what you think</a>.  If you don&#8217;t like
the idea or have a problem with EditorConfig please <a href="https://github.com/editorconfig/editorconfig/issues">submit an issue</a>,
add a thread to <a href="https://groups.google.com/forum/?fromgroups#!forum/editorconfig">the mailing list</a>, or send me an email voicing
your concern.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Migrating from Subversion to Git]]></title>
    <link href="http://treyhunner.com/2011/11/migrating-from-subversion-to-git/"/>
    <updated>2011-11-17T00:00:00-08:00</updated>
    <id>http://treyhunner.com/2011/11/migrating-from-subversion-to-git</id>
    <content type="html"><![CDATA[<p>I recently migrated multiple Subversion repositories to Git.  I found
<a href="http://blog.woobling.org/2009/06/git-svn-abandon.html" title="Migrating from Subversion to Git">this blog post</a> very helpful during the process.  Here are
some tips I found helpful that were not mentioned in that post.</p>

<h3>Generating an authors file</h3>

<p>Subversion denotes authors by usernames while Git uses name and email.  An
authors file can be used to map subversion usernames to Git authors when
creating the Git repository, like so:</p>

<pre><code>git-svn clone --stdlayout --authors-file=authors.txt http://your/svn/repo/url
</code></pre>

<p>The trickest part about <a href="http://triptico.com/notes/8d4510bb.html">making an authors file</a> was finding
all the authors.  I found this command useful for finding usernames of all
Subversion committers:</p>

<pre><code>svn log | awk '($0 ~ /^r/) {print $3}' | sort -u
</code></pre>

<p>A similar method of creating an authors file is
<a href="http://technicalpickles.com/posts/creating-a-svn-authorsfile-when-migrating-from-subversion-to-git/">presented here</a>.</p>

<h3>Removing git-svn-id messages</h3>

<p>When migrating from Subversion, every commit messages has a <code>git-svn-id</code> line
appended to it like this one:</p>

<blockquote><p>git-svn-id: http://svn/repo/url/trunk@9837 1eab27b1-3bc6-4acd-4026-59d9a2a3569e</p></blockquote>

<p>If you are planning on migrating away from your old Subversion repository
entirely, there&#8217;s no need to keep these.  The following command (taken from the
<a href="http://linux.die.net/man/1/git-filter-branch">git filter-branch man page</a>) removes these <code>git-svn-id</code>
lines from all commit messages in the current branch:</p>

<pre><code>git filter-branch -f --msg-filter 'sed -e "/git-svn-id:/d"'
</code></pre>

<h3>Removing empty commit messages</h3>

<p>Subversion allows empty commit messages, but Git does not.  Any empty commit
messages in your newly migrated git repository should be replaced so commands
like <code>git rebase</code> will work on these commits.</p>

<p>This command will replace all empty commit messages with <nobr>
&#8220;&lt;empty commit message&gt;&#8221;</nobr>:</p>

<pre><code>git filter-branch -f --msg-filter '
read msg
if [ -n "$msg" ] ; then
    echo "$msg"
else
    echo "&lt;empty commit message&gt;"
fi'
</code></pre>

<h3>Poking around</h3>

<p>After you&#8217;ve cleaned up your new <em>master</em> branch, you should cleanup other
branches you plan to keep.  Just <code>git checkout</code> each branch and repeat the same
steps.  To find the remote subversion branches available for checkout use
<code>git branch -a</code>.</p>

<p>Migrating between version control systems may be a good time to permanently
cleanup commit history with a <code>git rebase</code> or eliminate large files
that were never used with a <code>git filter-branch</code>.  Just remember to make backups of
previous working versions of branches before changing them, just in case.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Django and model history]]></title>
    <link href="http://treyhunner.com/2011/09/django-and-model-history/"/>
    <updated>2011-09-29T00:00:00-07:00</updated>
    <id>http://treyhunner.com/2011/09/django-and-model-history</id>
    <content type="html"><![CDATA[<p>Recently I had a need to store a snapshot of every state of particular model instance in a Django application.  I basically needed version control for the rows in my database tables.  When searching for applications that provided this feature, which I call <strong>model history</strong>, I found <a href="http://djangopackages.com/grids/g/model-audit/">many different approaches</a> but few good comparisons of them.  In an attempt to fill that void, I&#8217;m going to detail some of my findings here.</p>

<h3>django-reversion</h3>

<p>The <a href="http://stdbrouw.github.com/django-revisions/">django-reversion</a> application was started in 2008 by Dave Hall.  Reversion uses only one table to store data for all version-tracked models.  Each version of a model adds a new row to this table, using a JSON object representing the model state.  Models can be reverted to previous versions from the admin interface.  This single-table version structure makes django-reversion very easy to install and to uninstall, but it also creates <a href="http://groups.google.com/group/django-reversion/browse_thread/thread/922b4e42d9577e0b">problems when model fields are changed</a>.</p>

<h3>django-revisions</h3>

<p>The <a href="https://github.com/etianen/django-reversion">django-revisions</a> application was created by Stijn Debrouwere in 2010 because the existing Django model history applications at the time were abandoned or suffered from fundamental design problems.  Revisions uses a model history method called same-table versioning (<a href="http://stdbrouw.github.com/django-revisions/design.html">design details outlined here</a>).  Same-table versioning adds a few fields to each version-tracked model which allows it to record the most recent version of each model as well as old versions in the original model table.  Model changes are simplified because they change all versions at once and no new tables need to be added to use revisions (just new fields on existing tables).  The only problem I found with revisions was that it does not currently support database-level uniqueness constraints.  Adding <code>unique=True</code> to a model field or a <code>unique_together</code> Meta attribute will result in an error.  Currently uniqueness constraints must be specified in a separate way for Revisions to honor them when saving models.</p>

<h3>django-simple-history</h3>

<p>The <a href="https://bitbucket.org/q/django-simple-history/">django-simple-history</a> application was based on code originally written by Marty Alchin, author of Pro Django.  Marty Alchin posted <a href="https://code.djangoproject.com/wiki/AuditTrail">AuditTrail</a> on the Django trac wiki in 2007 and later revised and republished the code in his book Pro Django in 2008, renaming it to HistoricalRecords.  Corey Bertram created django-simple-history from this code and put it online in 2010.</p>

<p>Simple History works by creating a separate &#8220;historical&#8221; model for each model that requires an audit trail and storing a snapshot of each changed model instance in that historical model.  For example, a Book model would have a HistoricalBook created from it that would store a new HistoricalBook instance every time a Book instance was changed.  Collisions are avoided by disabling uniqueness constraints and model schema changes are accepted by automatically changing historical models as well.  This method comes at the cost of creating an extra table in the database for each model that needs history.</p>

<h3>My conclusions</h3>

<p>When testing these three applications myself, I immediately eliminated django-reversion because I needed to allow easy model schema changes for my project.  I found that both django-revisions and django-simple-history worked well with schema migrations through <a href="http://south.aeracode.org/docs/about.html">South</a> (which I use on everything).  Django-revisions worked better for data migrations in South (due to only needing to change one model), but the uniqueness constraint problems with django-revisions would have been problematic for some of my models.  So eventually I settled on <a href="https://bitbucket.org/q/django-simple-history/">django-simple-history</a>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Sharing Screenshots in Linux]]></title>
    <link href="http://treyhunner.com/2011/04/sharing-screenshots-in-linux/"/>
    <updated>2011-04-03T00:00:00-07:00</updated>
    <id>http://treyhunner.com/2011/04/sharing-screenshots-in-linux</id>
    <content type="html"><![CDATA[<p>I have been using Github Issues recently and loving its simplicity.  Unfortunately, I&#8217;ve found that I often need to upload screenshots to demonstrate bugs and Issues does not support file uploads.  There are <a href="http://wiki.dropbox.com/DropboxAddons/DropboxScreenGrabber">Windows</a> and <a href="http://www.getcloudapp.com/">Mac</a> applications that solve this problem by capturing a screenshot, uploading it, and copying a URL to access the screenshot to the clipboard.</p>

<p>I did not find any Linux applications that will capture/upload a screenshot and copy the URL but I discovered <a href="http://forums.dropbox.com/topic.php?id=21735">a thread in the Dropbox forums</a> with a script that does just that.  I added comments to the script, changed the variable names, removed the need for a temporary file, and added a <code>notify-send</code> call as a visual cue (should work on Ubuntu).  I have the script mapped to <kbd>Ctrl-PrtScrn</kbd> in Ubuntu.</p>

<div><script src='https://gist.github.com/892492.js?file='></script>
<noscript><pre><code>#!/bin/sh
# Ubuntu-specific modification of http://wiki.dropbox.com/TipsAndTricks/ShareScreenshots

# Change these
DB_USER_ID=YOURDBUSERID
BITLY_USERNAME=YOURBITLYUSERNAME
BITLY_API_KEY=YOURBITLYKEYHERE
DROPBOX_PUBLIC_DIR=~/Dropbox/Public
SCREENSHOT_DIR=screenshots

CAPTURE_DELAY=0
PICTURE_QUALITY=50
FILE_EXTENSION=png
TIME=$(date +%Y%m%d%H%M%S)
FILENAME=$TIME.$FILE_EXTENSION

# Move to the directory where screenshots will be stored
mkdir -p $DROPBOX_PUBLIC_DIR/$SCREENSHOT_DIR
cd $DROPBOX_PUBLIC_DIR/$SCREENSHOT_DIR

# Take screenshot and save in screenshot directory
scrot -d $CAPTURE_DELAY -q $PICTURE_QUALITY $FILENAME

# Get Dropbox public URL for screenshot
DB_URL=&quot;http://dl.dropbox.com/u/$DB_USER_ID/$SCREENSHOT_DIR/$FILENAME&quot;

# Get bit.ly shortened URL for Dropbox URL
ESCAPED_DB_URL=&quot;$(echo $DB_URL | sed 's,:,%3A,g;s,/,%2F,g')&quot;
BITLY_API_CALL=&quot;http://api.bit.ly/v3/shorten?login=$BITLY_USERNAME&amp;apiKey=$BITLY_API_KEY&amp;longUrl=$ESCAPED_DB_URL&amp;format=txt&quot;
SHORT_URL=$(curl -s -S $BITLY_API_CALL)

# Copy shortened URL to clipboard
echo $SHORT_URL | xclip -sel clip

# Display message to user (requires libnotify)
notify-send &quot;Screenshot added&quot; &quot;Screenshot link copied to clipboard: $SHORT_URL&quot;
</code></pre></noscript></div>



]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Encrypted private keys in Django]]></title>
    <link href="http://treyhunner.com/2010/12/encrypted-private-keys-in-django/"/>
    <updated>2010-12-11T00:00:00-08:00</updated>
    <id>http://treyhunner.com/2010/12/encrypted-private-keys-in-django</id>
    <content type="html"><![CDATA[<p>Uniquely identifiable URLs are necessary for many web applications.  For example, a website that provides book reviews may identify the URL of a specific book like this: <strong>www.example.com/books/8839/</strong>.  The easiest way to identify entities in Django is to use the unique primary key of each object, which by default is an auto-incremented positive integer.</p>

<p>Revealing the primary key of an entity is often not desirable.  An astute visitor of the website mentioned above may be able to guess information from the URL such as how many book reviews are available on the website or how old specific reviews are.</p>

<p>The code snippet below demonstrates one way to use a unique but cryptic identifier for an object without needing to change the way primary keys are generated.  There are two notable extensions to the basic Django Model in the below code:</p>

<ol>
<li>The encrypted_pk and encrypted_id model properties return an AES-encrypted version of the primary key as a 13 character base-36 string.</li>
<li>The get method of the default manager can be queried with an encrypted primary key by using the keyword argument encrypted_pk.</li>
</ol>


<p>Feel free to use this code however you want.</p>

<div><script src='https://gist.github.com/735861.js?file='></script>
<noscript><pre><code># This code is under the MIT license.
# Inspired by this StackOverflow question:
http://stackoverflow.com/questions/3295405/creating-django-objects-with-a-random-primary-key

import struct
from Crypto.Cipher import DES
from django.db import models


def base36encode(number):
    &quot;&quot;&quot;Encode number to string of alphanumeric characters (0 to z). (Code taken from Wikipedia).&quot;&quot;&quot;
    if not isinstance(number, (int, long)):
        raise TypeError('number must be an integer')
    if number &lt; 0:
        raise ValueError('number must be positive')

    alphabet = '0123456789abcdefghijklmnopqrstuvwxyz'
    base36 = ''
    while number:
        number, i = divmod(number, 36)
        base36 = alphabet[i] + base36

    return base36 or alphabet[0]


def base36decode(numstr):
    &quot;&quot;&quot;Convert a base-36 string (made of alphanumeric characters) to its numeric value.&quot;&quot;&quot;
    return int(numstr,36)


class EncryptedPKModelManager(models.Manager):
    &quot;&quot;&quot;This manager allows models to be identified based on their encrypted_pk value.&quot;&quot;&quot;
    def get(self, *args, **kwargs):
        encrypted_pk = kwargs.pop('encrypted_pk', None)
        if encrypted_pk:
            # If found, decrypt encrypted_pk argument and set pk argument to the appropriate value
            kwargs['pk'] = struct.unpack('&lt;Q', self.model.encryption_obj.decrypt(
                struct.pack('&lt;Q', base36decode(encrypted_pk))
            ))[0]
        return super(EncryptedPKModelManager, self).get(*args, **kwargs)


class EncryptedPKModel(models.Model):
    &quot;&quot;&quot;Adds encrypted_pk property to children which returns the encrypted value of the primary key.&quot;&quot;&quot;
    encryption_obj = DES.new('8charkey') # This 8 character secret key should be changed!

    def __init__(self, *args, **kwargs):
        super(EncryptedPKModel, self).__init__(*args, **kwargs)
        setattr(
            self.__class__,
            &quot;encrypted_%s&quot; % (self._meta.pk.name,),
            property(self.__class__._encrypted_pk)
        )

    def _encrypted_pk(self):
        return base36encode(struct.unpack('&lt;Q', self.encryption_obj.encrypt(
            str(struct.pack('&lt;Q', self.pk))
        ))[0])

    encrypted_pk = property(_encrypted_pk)

    class Meta:
        abstract = True


class ExampleModelManager(EncryptedPKModelManager):
    pass


class ExampleModel(EncryptedPKModel):
    objects = ExampleModelManager()
    example_field = models.CharField(max_length=32)


# Example usage:
# example_instance = ExampleModel.objects.get(pk=1)
# url_pk = example_instance.encrypted_pk
# ExampleModel.objects.get(encrypted_pk=url_pk)
</code></pre></noscript></div>



]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Replacement for jQuery AlphaNumeric plugin]]></title>
    <link href="http://treyhunner.com/2010/10/replacement-for-jquery-alphanumeric-plugin/"/>
    <updated>2010-10-18T00:00:00-07:00</updated>
    <id>http://treyhunner.com/2010/10/replacement-for-jquery-alphanumeric-plugin</id>
    <content type="html"><![CDATA[<p>I recently inherited a codebase that used the <a href="http://plugins.jquery.com/project/aphanumeric">jQuery AlphaNumeric plugin</a> extensively.  This plugin can be used to restrict users from entering certain characters into a form field.   The functions included for this plugin (<strong>alphanumeric</strong>, <strong>alpha</strong>, and <strong>numeric</strong>) claim to allow only alphabetic and/or numeric characters to be entered in the form field being acted on.</p>

<p>Unfortunately, this plugin is ineffectual.  I have witnessed unexpected behaviors of varying significance associated with this plugin:</p>

<ol>
<li>Forbidden characters can be input by pasting with CTRL+V in Chrome</li>
<li>Forbidden characters can be input by selecting Edit-&gt;Paste in Firefox and IE</li>
<li>Forbidden characters can be input by middle-click pasting in Linux</li>
<li>The arrow keys, Home button, End button, and Delete button do not work in input fields using this plugin&#8217;s functions in Firefox</li>
<li>The context menu is disabled (right clicking on an input field does nothing)</li>
<li>And most importantly, instead of using a list of allowed characters, a list of disallowed characters is used so these are the only characters that are actually forbidden:

<blockquote><p>!@#$%<sup>&amp;*()+=[]&#8217;;,/{}|&#8221;:?~`.-</sup></p></blockquote></li>
</ol>


<p>The code is so brief that patching the current plugin would be pointless, so instead I wrote <a href="http://github.com/treyhunner/jquery-formrestrict">a replacement</a> that acts similar enough that I did not have to change any of our pre-existing code that depended on the AlphaNumeric plugin.</p>

<p>First I created <strong>restrict</strong>, a very basic modifier function that takes a sanitizer function as an argument.  The sanitizer function should manipulate the string to be valid input (if it was not already) and return the valid version of this input.  The <strong>restrict</strong> function is triggered whenever the input field is altered and will immediately replace the text in the field with sanitized text.</p>

<p>Most of the restricts I would want to use on an input field can be represented by a regular expression, so I created the <strong>regexRestrict</strong> function that takes a regular expression as input and uses <strong>restrict</strong> to replace matches to this regular expression found in the string.</p>

<p>The <strong>restrict</strong> and <strong>regexRestrict</strong> functions provide every feature that the AlphaNumeric plugin promises, but they don&#8217;t use the same syntax as the AlphaNumeric plugin.  To be able to drop this plugin into a codebase that currently uses the AlphaNumeric plugin, we&#8217;d need an equivalent to the <strong>alphanumeric</strong>, <strong>alpha</strong>, and <strong>numeric</strong> functions with all of their <a href="http://itgroup.com.ph/alphanumeric/">stated features</a>.  To allow the code that relied on the AlphaNumeric plugin to continue functioning, I created replacements for all three of these functions.  These functions take the same inputs as their AlphaNumeric plugin equivalents.</p>

<p>The restrict and regexRestrict functions and the alphanumeric plugin replacement that uses these functions can be <a href="http://github.com/treyhunner/jquery-formrestrict">found on github</a>.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Multiple Monitors with Multiple Workspaces]]></title>
    <link href="http://treyhunner.com/2009/06/multiple-monitors-with-multiple-workspaces/"/>
    <updated>2009-06-15T00:00:00-07:00</updated>
    <id>http://treyhunner.com/2009/06/multiple-monitors-with-multiple-workspaces</id>
    <content type="html"><![CDATA[<p>In most window managers (WMs) that allow for multiple workspaces, additional monitors simply increase the size of each workspace. Since January I have been using a window manager that handles multiple monitors very differently, <a href="http://www.xmonad.org/">xmonad</a>.  Instead of increasing the workspace size to fit onto two monitors, each monitor displays a separate workspace, so the number of visible workspaces is increased.</p>

<p><em>Paradigm difference between these two WM styles:</em></p>

<ol>
<li>Each additional monitor extends the workspace size

<ul>
<li>One large workspace is visible at a time (ex: workspace 1 spans across all monitors)</li>
<li>When the workspace changes, both monitors change</li>
<li>When removing a monitor, workspaces must shrink in size, bunching windows together</li>
</ul>
</li>
<li>Each additional monitor allows another workspace to be visible

<ul>
<li>Each monitor displays one workspace at a time (ex: monitor 1 currently showing workspace 3 and monitor 2 currently showing workspace 1)</li>
<li>When the workspace on one monitor changes the workspace on the other monitor does not need to change</li>
<li>When removing a monitor, one less workspace will be displayed</li>
</ul>
</li>
</ol>


<p>There are many times when I want to be able to keep one monitor static while changing the other monitor.  For example I may want a video to stay on one monitor while I work on the other monitor.  In most window managers this restricts me to one workspace because changing workspaces would change both monitors.  In xmonad, the workspace that contains the video can be placed on one monitor and the visible workspace on the other monitor can changed freely without interfering with the first monitor.</p>

<p>Since using xmonad, I have found &#8220;1 workspace per monitor&#8221; window management much more productive and comfortable.  I wish more window managers would at least make this kind of workspace/monitor handling an option.  I have had problems with xmonad recently and I have been trying to switch back to a more popular window manager with free-floating windows like Gnome, KDE, Xfce, or Openbox.</p>

<p>So far my biggest problem with switching to other window managers has been the lack of &#8220;1 workspace per monitor&#8221; support.  Xmonad has greatly increased my productivity with two monitors and it&#8217;s hard for me to switch away from it for this reason.  Hopefully I will find out how to effectively emulate this behavior in other window managers.</p>
]]></content>
  </entry>
  
  <entry>
    <title type="html"><![CDATA[Ubuntu Now Boots in 10 Seconds]]></title>
    <link href="http://treyhunner.com/2009/05/ubuntu-now-boots-in-10-seconds/"/>
    <updated>2009-05-01T00:00:00-07:00</updated>
    <id>http://treyhunner.com/2009/05/ubuntu-now-boots-in-10-seconds</id>
    <content type="html"><![CDATA[<p>I upgraded my Thinkpad to Ubuntu 9.04 (Jaunty Jackalope) recently.  My laptop has a solid-state drive and I had tweaked the boot process previously so it was down to 15 seconds.  I had heard that Jaunty had drastically decrease the boot time, but I figured my computer could not boot much faster than it already had since I had modified the boot process drastically.</p>

<p>I was wrong.  The first time I rebooted I was amazed at the speed of the boot.  My boot logger recorded the boot as taking 8 seconds.  I have rebooted once more since I installed Jaunty and that boot only took 7 seconds.</p>

<p>The main reason the boot time is so fast is due to my solid state drive.  That is how I got my boot down to 15 seconds before.  However, 7 seconds is a ridiculously fast boot time in my opinion.  Especially since it seems like it takes only about 3 seconds until my login manager is loaded and waiting for me.</p>
]]></content>
  </entry>
  
</feed>

