<?xml version="1.0" encoding="UTF-8"?>
<?xml-stylesheet type="text/xsl" media="screen" href="/~d/styles/rss2full.xsl"?><?xml-stylesheet type="text/css" media="screen" href="http://feeds.feedburner.com/~d/styles/itemcontent.css"?><rss 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/" version="2.0">

<channel>
	<title>Perplexed Labs</title>
	
	<link>http://blog.perplexedlabs.com</link>
	<description>web development war stories from the frontlines to the backend</description>
	<lastBuildDate>Mon, 05 Oct 2009 16:54:20 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.6</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="self" href="http://feeds.feedburner.com/plabs" type="application/rss+xml" /><atom10:link xmlns:atom10="http://www.w3.org/2005/Atom" rel="hub" href="http://pubsubhubbub.appspot.com" /><item>
		<title>PHP Custom MySQL Session Handler</title>
		<link>http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/</link>
		<comments>http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/#comments</comments>
		<pubDate>Mon, 05 Oct 2009 14:00:30 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[memcache]]></category>
		<category><![CDATA[mysql]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[session]]></category>
		<category><![CDATA[session handler]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=302</guid>
		<description><![CDATA[The Problem
I'm sure many have used PHP's default session handling capabilities.  By default, PHP uses the filesystem to store session data naming files with their session id # and putting them in /tmp.
This is done for the sake of simplicity.  On a single-server, low load website, this particular setup works fine.  It's [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/06/mutex-with-php-and-mysql/' rel='bookmark' title='Permanent Link: Mutex with PHP and mySQL'>Mutex with PHP and mySQL</a></li><li><a href='http://blog.perplexedlabs.com/2008/12/17/php-parallel-web-scraper/' rel='bookmark' title='Permanent Link: PHP Parallel Web Scraper'>PHP Parallel Web Scraper</a></li><li><a href='http://blog.perplexedlabs.com/2008/02/04/php-simple-profiling-class/' rel='bookmark' title='Permanent Link: PHP Simple Profiling Class'>PHP Simple Profiling Class</a></li></ol>]]></description>
			<content:encoded><![CDATA[<h3>The Problem</h3>
<p>I'm sure many have used PHP's default session handling capabilities.  By default, PHP uses the filesystem to store session data naming files with their session id # and putting them in /tmp.</p>
<p>This is done for the sake of simplicity.  On a single-server, low load website, this particular setup works fine.  It's when you start having multiple simultaneous requests from a single client (identified by a session) that the problems begin to show their ugly heads.  Utilizing AJAX multiple simultaneous requests might be the norm, even for a low load website.</p>
<p>Essentially, in order to prevent <a href="http://en.wikipedia.org/wiki/Race_condition">race conditions</a> PHP internally uses a lock to maintain exclusive access to the file containing the session data for the client connection.  This means that as soon as a single request acquires exclusive access to that session file, no other request can access the file until the original request completes.  What happens when that second request asks to start the session?  It waits.</p>
<h3>The Solution</h3>
<p>Fortunately there's a solution to all this.  Implementing your own custom session handler and moving your session storage backend to another technology (such as a MySQL database or memcache) affords you the ability to handle simultaneous requests in a thread-safe manner.  Remember, it's a good thing that PHP prevents race conditions by locking the session file.  What we're looking to do is increase the granularity of the lock to the level of individual session data key =&gt; value pairs.</p>
<p>For this post we're going to stick to storing sessions in the database with our own custom session save handler.  Perhaps in another post I'll talk about doing the same in memcache.  It's the theory we're concerned about, not necessarily the exact storage mechanism implementation.</p>
<p>Implementing your own custom session handler is simply a matter of calling session_set_save_handler() with the appropriate callback methods for handling the following scenarios:</p>
<ul>
<li><strong>open</strong><br />
Open function, this works like a constructor in classes and is executed when the session is being opened. The open function expects two parameters, where the first is the save path and the second is the session name.</li>
<li><strong>close</strong><br />
Close function, this works like a destructor in classes and is executed when the session operation is done.</li>
<li><strong>read</strong><br />
Read function must return string value always to make save handler work as expected. Return empty string if there is no data to read. Return values from other handlers are converted to boolean expression. TRUE for success, FALSE for failure.</li>
<li><strong>write</strong><br />
The "write" handler is not executed until after the output stream is closed.</li>
<li><strong>destroy</strong><br />
The destroy handler, this is executed when a session is destroyed with session_destroy() and takes the session id as its only parameter.</li>
<li><strong>gc</strong><br />
The garbage collector, this is executed when the session garbage collector is executed and takes the max session lifetime as its only parameter.</li>
</ul>
<p>There's a well known trick for situations like this that allow you to pass a class method (static or instance) for callbacks.  Let's take a look at a simple example.  This correctly implements the required methods but obviously doesn't do much:</p>
<pre class="brush: php;">
class MySession
{
	public function __construct()
	{
		session_set_save_handler(
						array('MySession', 'sess_open'),
						array('MySession', 'sess_close'),
						array('MySession', 'sess_read'),
						array('MySession', 'sess_write'),
						array('MySession', 'sess_destroy'),
						array('MySession', 'sess_gc')
						);

		ini_set('session.auto_start',					0);
		ini_set('session.gc_probability',				1);
		ini_set('session.gc_divisor',					100);
		ini_set('session.gc_maxlifetime',				604800);
		ini_set('session.referer_check',				'');
		ini_set('session.entropy_file',					'/dev/urandom');
		ini_set('session.entropy_length',				16);
		ini_set('session.use_cookies',					1);
		ini_set('session.use_only_cookies',				1);
		ini_set('session.use_trans_sid',				0);
		ini_set('session.hash_function',				1);
		ini_seT('session.hash_bits_per_character',		5);

		session_cache_limiter('nocache');
		session_set_cookie_params(0, '/', '.mydomainname.com');
		session_name('mySessionName');
		session_start();
	}

	public static function sess_open($save_path, $session_name)
	{
		return true;
	}

	public static function sess_close()
	{
		return true;
	}

	public static function sess_read($id)
	{
		return '';
	}

	public static function sess_write($id, $sess_data)
	{
		return true;
	}

	public static function sess_destroy($id)
	{
		return true;
	}

	public static function sess_gc($maxlifetime)
	{
		return true;
	}
}
</pre>
<h3>SPL and Storing Session Data In MySQL</h3>
<p>Adding support for MySQL to this class is fairly trivial.  Let's start off by creating a table to store our session data:</p>
<pre class="brush: sql;">
CREATE TABLE `sessions` (
  `sesskey` char(32) NOT NULL,
  `timestamp` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `varkey` varchar(128) NOT NULL,
  `varval` longtext NOT NULL,
  PRIMARY KEY  (`sesskey`,`varkey`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
</pre>
<p><a href="http://us3.php.net/spl">PHP's SPL</a> provides the ability to create objects that behave as though they were arrays.  You can make objects that iterate, respond to accessing them via array notation ($object['key']), and lots of other interesting things.</p>
<p>We're going to enhance the MySession class with a couple SPL interfaces that will allow the object, when instantiated, to behave like an array.   We can then override the $_SESSION superglobal with an instance of our new MySession class.  It will provide an identical interface to access session data while internally storing session data to the database.  Also, internally it will use methods for row-level locking via MySQL's advisory locks (GET_LOCK() and RELEASE_LOCK()).</p>
<p>The next step is to implement the required methods for the interfaces we're using.  These methods allow the object to behave as though it's an array.</p>
<pre class="brush: php;">
class MySession implements Countable, ArrayAccess, Iterator
{
	private $index;
	private $curElement;
	private $locks = array();
	private $sessionName = 'sessionId';
	private $serialize = 'serialize';
	private $unserialize = 'unserialize';
	private $session_id = null;

	public function __construct()
	{
		session_set_save_handler(
						array('MySession', 'sess_open'),
						array('MySession', 'sess_close'),
						array('MySession', 'sess_read'),
						array('MySession', 'sess_write'),
						array('MySession', 'sess_destroy'),
						array('MySession', 'sess_gc')
						);

		ini_set('session.auto_start',					0);
		ini_set('session.gc_probability',				1);
		ini_set('session.gc_divisor',					100);
		ini_set('session.gc_maxlifetime',				604800);
		ini_set('session.referer_check',				'');
		ini_set('session.entropy_file',					'/dev/urandom');
		ini_set('session.entropy_length',				16);
		ini_set('session.use_cookies',					1);
		ini_set('session.use_only_cookies',				1);
		ini_set('session.use_trans_sid',				0);
		ini_set('session.hash_function',				1);
		ini_seT('session.hash_bits_per_character',		5);

		session_cache_limiter('nocache');
		session_set_cookie_params(0, '/', '.mydomainname.com');
		session_name('mySessionName');
		session_start();
	}

	public function destroy()
	{
		$sessionName = session_name();
		$cookieInfo = session_get_cookie_params();
		$cookieExpires = time() - 3600;
		if((empty($cookieInfo['domain'])) &amp;&amp; (empty($cookieInfo['secure']))) {
			setcookie($sessionName, '', $cookieExpires, $cookieInfo['path']);
		} elseif(empty($cookieInfo['secure'])) {
			setcookie($sessionName, '', $cookieExpires, $cookieInfo['path'], $cookieInfo['domain']);
		} else {
			setcookie($sessionName, '', $cookieExpires, $cookieInfo['path'], $cookieInfo['domain'], $cookieInfo['secure']);
		}
		unset($_COOKIE[$sessionName]);

		$dbo = DBO::getInstance();
		$q = &quot;DELETE FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;'&quot;;
		$dbo-&gt;query($q);

		session_destroy();
	}

	private function lockName($k)
	{
		return 'sesslock'.$this-&gt;session_id.$k;
	}

	public function locked($k)
	{
		$k = $this-&gt;lockName($k);

		return isset($this-&gt;locks[$k]);
	}

	public function acquire($k, $timeout = 0)
	{
		$k = $this-&gt;lockName($k);

		if(!isset($this-&gt;locks[$k])) {
			$dbo = DBO::getInstance();
			$q = &quot;SELECT GET_LOCK('&quot;.$k.&quot;', &quot;.$timeout.&quot;)&quot;;
			$rs = $dbo-&gt;query($q);
			$this-&gt;locks[$k] = $dbo-&gt;result($rs, 0);
			$dbo-&gt;fr($rs);

			return $this-&gt;locks[$k];
		}

		return false;
	}

	public function release($k)
	{
		$k = $this-&gt;lockName($k);

		unset($this-&gt;locks[$k]);

		$dbo = DBO::getInstance();
		$q = &quot;SELECT RELEASE_LOCK('&quot;.$k.&quot;')&quot;;
		$rs = $dbo-&gt;query($q);
		$ret = $dbo-&gt;fetch($rs);
		$dbo-&gt;fr($rs);

		return true;
	}

	public function count()
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT COUNT(*) FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;'&quot;;
		$rs = $dbo-&gt;query($q);
		$ret = $dbo-&gt;result($rs, 0);
		$dbo-&gt;fr($rs);

		return $ret;
	}

	public function rewind()
	{
		$this-&gt;index = 0;
		$this-&gt;getCurElement();
	}

	private function getCurElement()
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT `varkey`, `varval` FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' LIMIT &quot;.$this-&gt;index.&quot;,1&quot;;
		$rs = $dbo-&gt;query($q);
		$row = $dbo-&gt;fetch($rs);
		$dbo-&gt;fr($rs);
		if(is_array($row) &amp;&amp; (count($row) == 2)) {
			$this-&gt;curElement = $row;
		} else {
			$this-&gt;curElement = array(null, null);
		}
	}

	public function key()
	{
		return $this-&gt;curElement[0];
	}

	public function current()
	{
		return call_user_func($this-&gt;unserialize, $this-&gt;curElement[1]);
	}

	public function next()
	{
		$this-&gt;index++;
		$this-&gt;getCurElement();
	}

	public function valid()
	{
		return ($this-&gt;curElement[0] !== null);
	}

	public function offsetSet($k, $v)
	{
		$dbo = DBO::getInstance();
		$q = &quot;REPLACE INTO `sessions` (`sesskey`, `varkey`, `varval`) VALUES ('&quot;.$this-&gt;session_id.&quot;', '&quot;.$k.&quot;', '&quot;.$dbo-&gt;sanitize(call_user_func($this-&gt;serialize, $v)).&quot;')&quot;;
		$dbo-&gt;query($q);
	}

	public function offsetGet($k)
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT `varval` FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' AND `varkey` = '&quot;.$k.&quot;'&quot;;
		$rs = $dbo-&gt;query($q);
		if($ret = $dbo-&gt;result($rs, 0)) {
			$ret = call_user_func($this-&gt;unserialize, $ret);
		}
		$dbo-&gt;fr($rs);

		return $ret;
	}

	public function offsetUnset($k)
	{
		$dbo = DBO::getInstance();
		$q = &quot;DELETE FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' AND `varkey` = '&quot;.$k.&quot;'&quot;;
		$dbo-&gt;query($q);
	}

	public function offsetExists($k)
	{
		$dbo = DBO::getInstance();
		$q = &quot;SELECT `varval` FROM `sessions` WHERE `sesskey` = '&quot;.$this-&gt;session_id.&quot;' AND `varkey` = '&quot;.$k.&quot;'&quot;;
		$rs = $dbo-&gt;query($q);
		$ret = $dbo-&gt;result($rs, 0);
		$dbo-&gt;fr($rs);

		return (bool)$ret;
	}

	public static function sess_open($save_path, $session_name)
	{
		return true;
	}

	public static function sess_close()
	{
		return true;
	}

	public static function sess_read($id)
	{
		return '';
	}

	public static function sess_write($id, $sess_data)
	{
		return true;
	}

	public static function sess_destroy($id)
	{
		return true;
	}

	public static function sess_gc($maxlifetime)
	{
		$dbo = DBO::getInstance();
		$q = &quot;DELETE FROM `sessions` WHERE `timestamp` &lt; '&quot;.date('Y-m-d H:i:s', time() - $maxlifetime).&quot;'&quot;;
		$dbo-&gt;query($q);

		return $dbo-&gt;query($q);
	}
}
</pre>
<p>The $dbo object is just an example of an interface to the database through the use of a singleton.  Replace the $dbo object with your preferred mysql database interface and you'll be set to go!</p>
<p>Starting your session is now as simple as:</p>
<pre class="brush: php;">
$_SESSION = new MySession;
</pre>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/06/mutex-with-php-and-mysql/' rel='bookmark' title='Permanent Link: Mutex with PHP and mySQL'>Mutex with PHP and mySQL</a></li><li><a href='http://blog.perplexedlabs.com/2008/12/17/php-parallel-web-scraper/' rel='bookmark' title='Permanent Link: PHP Parallel Web Scraper'>PHP Parallel Web Scraper</a></li><li><a href='http://blog.perplexedlabs.com/2008/02/04/php-simple-profiling-class/' rel='bookmark' title='Permanent Link: PHP Simple Profiling Class'>PHP Simple Profiling Class</a></li></ol></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/plabs?a=zAmgElMnrJY:teZ4N1w2_IE:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/plabs?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=zAmgElMnrJY:teZ4N1w2_IE:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/plabs?i=zAmgElMnrJY:teZ4N1w2_IE:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=zAmgElMnrJY:teZ4N1w2_IE:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/plabs?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=zAmgElMnrJY:teZ4N1w2_IE:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/plabs?i=zAmgElMnrJY:teZ4N1w2_IE:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=zAmgElMnrJY:teZ4N1w2_IE:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/plabs?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=zAmgElMnrJY:teZ4N1w2_IE:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/plabs?i=zAmgElMnrJY:teZ4N1w2_IE:gIN9vFwOqvQ" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/10/05/php-custom-session-handler/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Backing Up Subversion Repositories Using svnadmin hotcopy</title>
		<link>http://blog.perplexedlabs.com/2009/09/11/backing-up-subversion-repositories-using-svnadmin-hotcopy/</link>
		<comments>http://blog.perplexedlabs.com/2009/09/11/backing-up-subversion-repositories-using-svnadmin-hotcopy/#comments</comments>
		<pubDate>Fri, 11 Sep 2009 17:18:02 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[backup]]></category>
		<category><![CDATA[bash]]></category>
		<category><![CDATA[hotcopy]]></category>
		<category><![CDATA[subversion]]></category>
		<category><![CDATA[svn]]></category>
		<category><![CDATA[svnadmin]]></category>

		<guid isPermaLink="false">http://blog.perplexedlabs.com/?p=363</guid>
		<description><![CDATA[Just wanted to post this quick bash script to iterate over the repositories in a directory, perform an svnadmin hotcopy, and tar/gzip the output.
By using hotcopy this can be performed on a live subversion repository and will produce a pristine backup.

#!/bin/bash
REPOS_PATH=/var/repos
mkdir -p /backups/weekly
rm -rf /backups/tmp
mkdir -p /backups/tmp/repos
for i in $(ls $REPOS_PATH); do
    [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/02/09/automated-backups-a-5-minute-script-to-safer-data/' rel='bookmark' title='Permanent Link: Automated Backups &#8211; A 5 Minute Script To Safer Data'>Automated Backups &#8211; A 5 Minute Script To Safer Data</a></li><li><a href='http://blog.perplexedlabs.com/2008/03/18/svn-keyword-substitution/' rel='bookmark' title='Permanent Link: SVN Keyword Substitution svn:keywords'>SVN Keyword Substitution svn:keywords</a></li><li><a href='http://blog.perplexedlabs.com/2008/11/10/setup-python-25-mod_wsgi-and-django-10-on-centos-5-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)'>Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)</a></li></ol>]]></description>
			<content:encoded><![CDATA[<p>Just wanted to post this quick bash script to iterate over the repositories in a directory, perform an svnadmin hotcopy, and tar/gzip the output.</p>
<p>By using hotcopy this can be performed on a live subversion repository and will produce a pristine backup.</p>
<pre class="brush: bash;">
#!/bin/bash
REPOS_PATH=/var/repos
mkdir -p /backups/weekly
rm -rf /backups/tmp
mkdir -p /backups/tmp/repos
for i in $(ls $REPOS_PATH); do
        /path/to/svnadmin hotcopy $REPOS_PATH/$i /backups/tmp/repos/$i
done
FN=svn.weekly.`date '+%Y%m%d'`.tar.gz
tar -czf /backups/weekly/$FN -C /backups/tmp .
</pre>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/02/09/automated-backups-a-5-minute-script-to-safer-data/' rel='bookmark' title='Permanent Link: Automated Backups &#8211; A 5 Minute Script To Safer Data'>Automated Backups &#8211; A 5 Minute Script To Safer Data</a></li><li><a href='http://blog.perplexedlabs.com/2008/03/18/svn-keyword-substitution/' rel='bookmark' title='Permanent Link: SVN Keyword Substitution svn:keywords'>SVN Keyword Substitution svn:keywords</a></li><li><a href='http://blog.perplexedlabs.com/2008/11/10/setup-python-25-mod_wsgi-and-django-10-on-centos-5-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)'>Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)</a></li></ol></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/plabs?a=kIb4aofqyGs:RO-sOdecooQ:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/plabs?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=kIb4aofqyGs:RO-sOdecooQ:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/plabs?i=kIb4aofqyGs:RO-sOdecooQ:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=kIb4aofqyGs:RO-sOdecooQ:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/plabs?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=kIb4aofqyGs:RO-sOdecooQ:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/plabs?i=kIb4aofqyGs:RO-sOdecooQ:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=kIb4aofqyGs:RO-sOdecooQ:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/plabs?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=kIb4aofqyGs:RO-sOdecooQ:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/plabs?i=kIb4aofqyGs:RO-sOdecooQ:gIN9vFwOqvQ" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/09/11/backing-up-subversion-repositories-using-svnadmin-hotcopy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Adventures in Django and Python – Part III</title>
		<link>http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/</link>
		<comments>http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/#comments</comments>
		<pubDate>Thu, 13 Aug 2009 14:00:38 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[session]]></category>
		<category><![CDATA[ternary]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=307</guid>
		<description><![CDATA[Read my previous two posts on Django and Python - Part I and Part II
I've been working on a project management tool suite in Django.  It's been a great side project to really experiment with Django in real-world scenarios.
Forms
At times I feel like I fight with newforms.  In particular, it lacks the ability [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/04/22/django-url-parameter-passing-and-python-strings/' rel='bookmark' title='Permanent Link: Django URL Parameter Passing and Python Strings'>Django URL Parameter Passing and Python Strings</a></li><li><a href='http://blog.perplexedlabs.com/2009/03/20/django-and-python-first-impressions-part-ii/' rel='bookmark' title='Permanent Link: Django and Python First Impressions &#8211; Part II'>Django and Python First Impressions &#8211; Part II</a></li><li><a href='http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/' rel='bookmark' title='Permanent Link: Getting Started with Django and Python &#8211; First Impressions'>Getting Started with Django and Python &#8211; First Impressions</a></li></ol>]]></description>
			<content:encoded><![CDATA[<p><em>Read my previous two posts on Django and Python - <a href="http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/">Part I</a> and <a href="http://blog.perplexedlabs.com/2009/03/20/django-and-python-first-impressions-part-ii/">Part II</a></em></p>
<p>I've been working on a project management tool suite in Django.  It's been a great side project to really experiment with Django in real-world scenarios.</p>
<h3>Forms</h3>
<p>At times I feel like I fight with newforms.  In particular, it lacks the ability to specify basic class or style attributes for a given form field from within the template.  I'd like to be able to more finely tune the display of the field, directly within the template, with a style attribute or a class.  Is it suggested you write your own custom form field widget for a single element?  I've been getting around this by doing the following:</p>
<pre class="brush: python;">
&lt;input type=&quot;text&quot; name=&quot;{{ todo_form.item.name }}&quot; style=&quot;width: 720px;&quot;/&gt;
</pre>
<p>This gets more complicated if you want to set a style attribute for a form field that's a select box (for a ForeignKey model field, for example).  </p>
<pre class="brush: python;">
&lt;label for=&quot;category&quot;&gt;Category:&lt;/label&gt; &lt;select name=&quot;{{ category_form.project.name }}&quot; style=&quot;width: 221px;&quot;&gt;
{% for choice_val, choice_label in category_form.project.field.choices %}
	&lt;option value=&quot;{{ choice_val }}&quot;&gt;{{ choice_label }}&lt;/option&gt;
{% endfor %}
</pre>
<p>Is this a good use case for template tags?  I feel like I'm missing something here.</p>
<p>On the positive side, it was an absolute pleasure to work with multiple forms on a single page submitted to and processed by a single view.  This is primarily thanks to prefixes.  Excellent, that's how easy it should be.</p>
<h3>Ternary Operator</h3>
<p><strong>Update:</strong> <em>It's been pointed out in comments (thanks!) that Python 2.5 introduced a ternary operator.  It's syntax is as follows:</em></p>
<pre class="brush: python;">
label = &quot;true&quot; if booleanVariable else &quot;false&quot;
</pre>
<p>I also ran into a minor Python syntax issue.  I love the ternary operator in languages that offer it.  It's a concise, one-line, syntax for an if-else clause.  Consider the following PHP code:</p>
<pre class="brush: php;">
$label = $booleanVariable ? 'true' : 'false';

// the above is identical to the following block:
if($booleanVariable) {
   $label = 'true';
} else {
   $label = 'false';
}
</pre>
<p>Python unfortunately lacks this syntactic sugar.  Fortunately, however, you can effectively accomplish the same thing by doing this:</p>
<pre class="brush: python;">
label = (booleanVariable and 'true' or 'false')

# the above is equivalent to the following block:
if booleanVariable:
   label = 'true'
else:
   label = 'false'
</pre>
<h3>Sessions</h3>
<p>Django has built in support for sessions.  By default, sessions last longer than the lifecycle of the user's browser.  I personally think it should be the other way around.  It's easily changed though (in your settings.py):</p>
<pre class="brush: python;">
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
</pre>
<h3>Views</h3>
<p>In one of my views I wanted to test whether a filtered result set was empty or not.  I was curious whether this was the "pythonic" way to accomplish this:</p>
<pre class="brush: python;">
account = get_object_or_404(Account, pk=account_id)
if account.useraccount_set.filter(user__exact=request.user) != []:
</pre>
<p>Also, with respect to views and passing context to the response, sometimes it's an efficient shortcut to use <strong>locals()</strong> instead of explicitly typing out all the variables you'd like to expose.  locals() returns a dictionary of all variables defined within the local scope.</p>
<pre class="brush: python;">
def myview(request, id):
    account = get_object_or_404(Account, pk=id)
    new_account_form = NewAccountForm()

    return render_to_response('myview.html', locals())
</pre>
<p>More soon!</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/04/22/django-url-parameter-passing-and-python-strings/' rel='bookmark' title='Permanent Link: Django URL Parameter Passing and Python Strings'>Django URL Parameter Passing and Python Strings</a></li><li><a href='http://blog.perplexedlabs.com/2009/03/20/django-and-python-first-impressions-part-ii/' rel='bookmark' title='Permanent Link: Django and Python First Impressions &#8211; Part II'>Django and Python First Impressions &#8211; Part II</a></li><li><a href='http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/' rel='bookmark' title='Permanent Link: Getting Started with Django and Python &#8211; First Impressions'>Getting Started with Django and Python &#8211; First Impressions</a></li></ol></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/plabs?a=eZoIWwAWDro:-geIyGjxeg8:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/plabs?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=eZoIWwAWDro:-geIyGjxeg8:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/plabs?i=eZoIWwAWDro:-geIyGjxeg8:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=eZoIWwAWDro:-geIyGjxeg8:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/plabs?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=eZoIWwAWDro:-geIyGjxeg8:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/plabs?i=eZoIWwAWDro:-geIyGjxeg8:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=eZoIWwAWDro:-geIyGjxeg8:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/plabs?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=eZoIWwAWDro:-geIyGjxeg8:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/plabs?i=eZoIWwAWDro:-geIyGjxeg8:gIN9vFwOqvQ" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
		<item>
		<title>Twitter and SD News</title>
		<link>http://blog.perplexedlabs.com/2009/07/31/twitter-and-sd-news/</link>
		<comments>http://blog.perplexedlabs.com/2009/07/31/twitter-and-sd-news/#comments</comments>
		<pubDate>Fri, 31 Jul 2009 17:24:28 +0000</pubDate>
		<dc:creator>Eric</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[hacker news]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[twitter]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=336</guid>
		<description><![CDATA[Motivation: SD News is a "social news site" (basically a Hacker News clone), written in Rails, that I work on as part of my efforts with a Christian publishing company I run with some friends.  As part of the administrative backend, I wanted to be able to send posts to our Twitter profile.  The site [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/26/flickr-rss-and-ruby/' rel='bookmark' title='Permanent Link: Flickr, RSS, and Ruby'>Flickr, RSS, and Ruby</a></li><li><a href='http://blog.perplexedlabs.com/2009/06/01/be-language-agnostic-solve-the-problem/' rel='bookmark' title='Permanent Link: Be Language Agnostic &#8211; Solve the Problem!'>Be Language Agnostic &#8211; Solve the Problem!</a></li><li><a href='http://blog.perplexedlabs.com/2009/01/13/installing-ruby-enterprise-edition-with-phusion-passenger/' rel='bookmark' title='Permanent Link: Installing Ruby Enterprise Edition with Phusion Passenger'>Installing Ruby Enterprise Edition with Phusion Passenger</a></li></ol>]]></description>
			<content:encoded><![CDATA[<p><strong>Motivation:</strong> <a href="http://news.sensusdivinitatis.com">SD News</a> is a "social news site" (basically a Hacker News clone), written in Rails, that I work on as part of my efforts with a Christian publishing company I run with some friends.  As part of the administrative backend, I wanted to be able to send posts to our <a href="http://twitter.com/sdpub">Twitter profile</a>.  The site is still young, and the community still growing, so I wanted the admins to have complete control over what gets sent to Twitter.  I had thought of automating this process based on which items have the most votes in a given time period, but trust is easy to lose and all it would take is 1 or 2 irrelevant, or irreverent. posts to lose that trust.</p>
<p><strong>Methodology:</strong> I would first need a good Twitter gem for Ruby, and I'd need to decide which URL shortening service I'd use.  <a href="http://twitter.rubyforge.org/">Ruby Twitter</a> seemed to be the simplest gem for Twitter.  For the URL shortening I chose bit.ly, because the <a href="http://github.com/philnash/bitly/tree/master">bitly gem</a> seemed like the easiest, and the documentation was good.  My plan of attack was:</p>
<ol>
<li>Grab one item from the queue, that has not been Twittered</li>
<li>Shorten the URL via bitly</li>
<li>Send the item's title and shortened URL to Twitter</li>
<li>Save the shortened URL in the database so I could retrieve stats later</li>
</ol>
<p>The script that did this would be run every hour.<br />
<strong><br />
Implementation: </strong>The two gems made this an almost trivial implementation.</p>
<pre class="brush: ruby;">
require 'twitter'
require 'bitly'

@item = Item.find(:first, :conditions =&gt; &quot;send_to_twitter = 1 and twitterd = 0&quot;, :order =&gt; &quot;posted_on desc&quot;)

if !@item.nil?
   b = Bitly.new(username, password)
   @url = b.shorten(&quot;http://news.sensusdivinitatis.com/item/#{@item.id}&quot;).short_url

   httpauth = Twitter::HTTPAuth.new(username, password)
   base = Twitter::Base.new(httpauth)
   base.update(&quot;#{@item.title[0...110]} - #{@url}&quot;) #shorten the title if it's too long

   Item.update(@item.id, :twitterd =&gt; 1, :bitly_url =&gt; @url) #save the bit.ly url
end
</pre>
<p>That's pretty much it.  Incidentally, the bitly gem makes it very easy to grab the stats for any URL.  For instance, if you wanted to see how many clicks a given URL has received:</p>
<pre class="brush: ruby;">
require 'twitter'
require 'bitly'

@item = Item.find(id)
b = Bitly.new(username, password)
@clicks = b.stats(i.bitly_url).stats[&quot;clicks&quot;]
</pre>
<p>Done.</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/02/26/flickr-rss-and-ruby/' rel='bookmark' title='Permanent Link: Flickr, RSS, and Ruby'>Flickr, RSS, and Ruby</a></li><li><a href='http://blog.perplexedlabs.com/2009/06/01/be-language-agnostic-solve-the-problem/' rel='bookmark' title='Permanent Link: Be Language Agnostic &#8211; Solve the Problem!'>Be Language Agnostic &#8211; Solve the Problem!</a></li><li><a href='http://blog.perplexedlabs.com/2009/01/13/installing-ruby-enterprise-edition-with-phusion-passenger/' rel='bookmark' title='Permanent Link: Installing Ruby Enterprise Edition with Phusion Passenger'>Installing Ruby Enterprise Edition with Phusion Passenger</a></li></ol></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/plabs?a=YnM5NXp4sC8:Jviq4rN3Obk:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/plabs?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=YnM5NXp4sC8:Jviq4rN3Obk:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/plabs?i=YnM5NXp4sC8:Jviq4rN3Obk:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=YnM5NXp4sC8:Jviq4rN3Obk:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/plabs?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=YnM5NXp4sC8:Jviq4rN3Obk:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/plabs?i=YnM5NXp4sC8:Jviq4rN3Obk:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=YnM5NXp4sC8:Jviq4rN3Obk:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/plabs?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=YnM5NXp4sC8:Jviq4rN3Obk:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/plabs?i=YnM5NXp4sC8:Jviq4rN3Obk:gIN9vFwOqvQ" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/07/31/twitter-and-sd-news/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Book Review: The Passionate Programmer</title>
		<link>http://blog.perplexedlabs.com/2009/07/29/book-review-the-passionate-programmer/</link>
		<comments>http://blog.perplexedlabs.com/2009/07/29/book-review-the-passionate-programmer/#comments</comments>
		<pubDate>Wed, 29 Jul 2009 19:41:36 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Book Reviews]]></category>
		<category><![CDATA[Development]]></category>
		<category><![CDATA[career]]></category>
		<category><![CDATA[consulting]]></category>
		<category><![CDATA[freelance]]></category>
		<category><![CDATA[india]]></category>
		<category><![CDATA[outsource]]></category>
		<category><![CDATA[programming]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=312</guid>
		<description><![CDATA[I recently picked up a copy of The Passionate Programmer: Creating a Remarkable Career in Software Development (Pragmatic Life).  I read it over the course of a week and wanted to share some of my thoughts.
Chad Fowler co-authored the book Programming Ruby 1.9: The Pragmatic Programmers' Guide (Facets of Ruby) and is a huge [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/07/28/django-1-0-template-development-sample-chapter-serving-multiple-templates/' rel='bookmark' title='Permanent Link: Django 1.0 Template Development: Sample Chapter &#8220;Serving Multiple Templates&#8221;'>Django 1.0 Template Development: Sample Chapter &#8220;Serving Multiple Templates&#8221;</a></li><li><a href='http://blog.perplexedlabs.com/2009/01/13/installing-ruby-enterprise-edition-with-phusion-passenger/' rel='bookmark' title='Permanent Link: Installing Ruby Enterprise Edition with Phusion Passenger'>Installing Ruby Enterprise Edition with Phusion Passenger</a></li></ol>]]></description>
			<content:encoded><![CDATA[<p>I recently picked up a copy of <a href="http://www.amazon.com/gp/product/1934356344?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=9325&#038;creativeASIN=1934356344">The Passionate Programmer: Creating a Remarkable Career in Software Development (Pragmatic Life)</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=1934356344" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />.  I read it over the course of a week and wanted to share some of my thoughts.</p>
<p><a href="http://chadfowler.com/">Chad Fowler</a> co-authored the book <a href="http://www.amazon.com/gp/product/1934356085?ie=UTF8&#038;tag=perplabs-20&#038;linkCode=as2&#038;camp=1789&#038;creative=390957&#038;creativeASIN=1934356085">Programming Ruby 1.9: The Pragmatic Programmers' Guide (Facets of Ruby)</a><img src="http://www.assoc-amazon.com/e/ir?t=perplabs-20&#038;l=as2&#038;o=1&#038;a=1934356085" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" /> and is a huge advocate of Rails.  The book is a practical guide to growing as a professional software developer and is broken down into many 2-5 page sections focusing on a specific concept related to accomplishing this goal.</p>
<p>Some general themes permeate the entire 200 page text.  One in particular is to embrace the opportunities that exist in a world where many software development roles are being outsourced overseas.  He outlines a number of ways to take advantage of this situation to position yourself in a way where you are more attractive to potential employers, even the ones doing the outsourcing.</p>
<p>He encourages a constant, methodical, evaluation of your skillset in order to target your research and development towards the technologies that will become desirable next.  Stay ahead of the wave instead of being tossed around by it.</p>
<p>Each section ends with an "Act On It!" portion which provides actionable tasks that reinforce the concept you've just read.  Many of these are good suggestions and are easily incorporated into your day to day activities.</p>
<p>Another concept is the idea that your career in software development, essentially yourself, should be treated as a product.  A product which requires constant attention in both R&#038;D and marketing.  Staying stagnant too long and not expanding the tools in your chest is a recipe for opportunity passing you by.  Marketing suggestions include blogging and contributing to open source projects.</p>
<p>If you've lost the passion for software development.  This book may provide just enough insight and food for thought to encourage you to get back on the right track and return to the days where you typed away into the wee hours of the night, not because you were getting paid, but because you loved it.</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/07/28/django-1-0-template-development-sample-chapter-serving-multiple-templates/' rel='bookmark' title='Permanent Link: Django 1.0 Template Development: Sample Chapter &#8220;Serving Multiple Templates&#8221;'>Django 1.0 Template Development: Sample Chapter &#8220;Serving Multiple Templates&#8221;</a></li><li><a href='http://blog.perplexedlabs.com/2009/01/13/installing-ruby-enterprise-edition-with-phusion-passenger/' rel='bookmark' title='Permanent Link: Installing Ruby Enterprise Edition with Phusion Passenger'>Installing Ruby Enterprise Edition with Phusion Passenger</a></li></ol></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/plabs?a=O6ZWGq9sVsI:qCTKJDo3hwE:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/plabs?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=O6ZWGq9sVsI:qCTKJDo3hwE:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/plabs?i=O6ZWGq9sVsI:qCTKJDo3hwE:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=O6ZWGq9sVsI:qCTKJDo3hwE:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/plabs?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=O6ZWGq9sVsI:qCTKJDo3hwE:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/plabs?i=O6ZWGq9sVsI:qCTKJDo3hwE:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=O6ZWGq9sVsI:qCTKJDo3hwE:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/plabs?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=O6ZWGq9sVsI:qCTKJDo3hwE:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/plabs?i=O6ZWGq9sVsI:qCTKJDo3hwE:gIN9vFwOqvQ" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/07/29/book-review-the-passionate-programmer/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django 1.1 Released</title>
		<link>http://blog.perplexedlabs.com/2009/07/29/django-1-1-released/</link>
		<comments>http://blog.perplexedlabs.com/2009/07/29/django-1-1-released/#comments</comments>
		<pubDate>Wed, 29 Jul 2009 13:18:12 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=327</guid>
		<description><![CDATA[The Django project just announced that v1.1 is ready!  Go here for the full release notes.
Some notable changes include support for aggregate functions from within the ORM.  This includes COUNT(), MAX(), MIN(), etc.
Also, the concept of "unmanaged" models which allow you to easily support tables that pre-existed or were created by some other [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/06/30/php-5-3-0-released-and-firefox-3-5/' rel='bookmark' title='Permanent Link: PHP 5.3.0 Released and Firefox 3.5'>PHP 5.3.0 Released and Firefox 3.5</a></li><li><a href='http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/' rel='bookmark' title='Permanent Link: Adventures in Django and Python &#8211; Part III'>Adventures in Django and Python &#8211; Part III</a></li><li><a href='http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/' rel='bookmark' title='Permanent Link: Getting Started with Django and Python &#8211; First Impressions'>Getting Started with Django and Python &#8211; First Impressions</a></li></ol>]]></description>
			<content:encoded><![CDATA[<p>The Django project just <a href="http://www.djangoproject.com/weblog/2009/jul/29/1-point-1/">announced</a> that v1.1 is ready!  Go <a href="http://docs.djangoproject.com/en/dev/releases/1.1/">here</a> for the full release notes.</p>
<p>Some notable changes include support for aggregate functions from within the ORM.  This includes COUNT(), MAX(), MIN(), etc.</p>
<p>Also, the concept of "unmanaged" models which allow you to easily support tables that pre-existed or were created by some other means to not be maintained by Django.</p>
<p>Another useful addition is deferred fields.  This is useful in instances where you store large amounts of text or binary data in a table and may not necessarily want to retrieve that data in a query.</p>
<p>Looks like an excellent point release.  Go grab it now!</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/06/30/php-5-3-0-released-and-firefox-3-5/' rel='bookmark' title='Permanent Link: PHP 5.3.0 Released and Firefox 3.5'>PHP 5.3.0 Released and Firefox 3.5</a></li><li><a href='http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/' rel='bookmark' title='Permanent Link: Adventures in Django and Python &#8211; Part III'>Adventures in Django and Python &#8211; Part III</a></li><li><a href='http://blog.perplexedlabs.com/2009/02/08/getting-started-with-django-and-python-first-impressions/' rel='bookmark' title='Permanent Link: Getting Started with Django and Python &#8211; First Impressions'>Getting Started with Django and Python &#8211; First Impressions</a></li></ol></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/plabs?a=DfdqHGIcKeI:5NTxz7Qhfwc:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/plabs?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=DfdqHGIcKeI:5NTxz7Qhfwc:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/plabs?i=DfdqHGIcKeI:5NTxz7Qhfwc:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=DfdqHGIcKeI:5NTxz7Qhfwc:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/plabs?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=DfdqHGIcKeI:5NTxz7Qhfwc:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/plabs?i=DfdqHGIcKeI:5NTxz7Qhfwc:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=DfdqHGIcKeI:5NTxz7Qhfwc:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/plabs?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=DfdqHGIcKeI:5NTxz7Qhfwc:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/plabs?i=DfdqHGIcKeI:5NTxz7Qhfwc:gIN9vFwOqvQ" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/07/29/django-1-1-released/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Django 1.0 Template Development: Sample Chapter “Serving Multiple Templates”</title>
		<link>http://blog.perplexedlabs.com/2009/07/28/django-1-0-template-development-sample-chapter-serving-multiple-templates/</link>
		<comments>http://blog.perplexedlabs.com/2009/07/28/django-1-0-template-development-sample-chapter-serving-multiple-templates/#comments</comments>
		<pubDate>Wed, 29 Jul 2009 01:26:00 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[templates]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=323</guid>
		<description><![CDATA[DISCLOSURE: I was sent this sample chapter by the publisher.  I'm sure some of you folks will find it useful so I'm posting it.  For a review of the book go here.
Download Sample Chapter 6 "Serving Multiple Templates"


Related posts:Django and Python First Impressions &#8211; Part IISetup Python 2.5, mod_wsgi, and Django 1.0 on [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/03/20/django-and-python-first-impressions-part-ii/' rel='bookmark' title='Permanent Link: Django and Python First Impressions &#8211; Part II'>Django and Python First Impressions &#8211; Part II</a></li><li><a href='http://blog.perplexedlabs.com/2008/11/10/setup-python-25-mod_wsgi-and-django-10-on-centos-5-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)'>Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)</a></li><li><a href='http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/' rel='bookmark' title='Permanent Link: Adventures in Django and Python &#8211; Part III'>Adventures in Django and Python &#8211; Part III</a></li></ol>]]></description>
			<content:encoded><![CDATA[<p>DISCLOSURE: I was sent this sample chapter by the publisher.  I'm sure some of you folks will find it useful so I'm posting it.  For a review of the <a href="http://www.packtpub.com/django-1.0-template-design-practical-guide/book">book</a> go <a href="http://arthurkoziel.com/2009/07/27/book-review-django-10-template-development/">here</a>.</p>
<p><a href='http://www.perplexedlabs.com/wp-content/uploads/2009/07/django-1-0-template-development-sample-chapter-6-serving-multiple-templates.pdf'>Download Sample Chapter 6 "Serving Multiple Templates"</a></p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/03/20/django-and-python-first-impressions-part-ii/' rel='bookmark' title='Permanent Link: Django and Python First Impressions &#8211; Part II'>Django and Python First Impressions &#8211; Part II</a></li><li><a href='http://blog.perplexedlabs.com/2008/11/10/setup-python-25-mod_wsgi-and-django-10-on-centos-5-cpanel/' rel='bookmark' title='Permanent Link: Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)'>Setup Python 2.5, mod_wsgi, and Django 1.0 on CentOS 5 (cPanel)</a></li><li><a href='http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/' rel='bookmark' title='Permanent Link: Adventures in Django and Python &#8211; Part III'>Adventures in Django and Python &#8211; Part III</a></li></ol></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/plabs?a=8iPcFAkZ61c:xlsDllIktZA:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/plabs?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=8iPcFAkZ61c:xlsDllIktZA:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/plabs?i=8iPcFAkZ61c:xlsDllIktZA:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=8iPcFAkZ61c:xlsDllIktZA:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/plabs?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=8iPcFAkZ61c:xlsDllIktZA:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/plabs?i=8iPcFAkZ61c:xlsDllIktZA:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=8iPcFAkZ61c:xlsDllIktZA:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/plabs?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=8iPcFAkZ61c:xlsDllIktZA:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/plabs?i=8iPcFAkZ61c:xlsDllIktZA:gIN9vFwOqvQ" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/07/28/django-1-0-template-development-sample-chapter-serving-multiple-templates/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>XMLRPC Pingbacks Using PHP</title>
		<link>http://blog.perplexedlabs.com/2009/07/15/xmlrpc-pingbacks-using-php/</link>
		<comments>http://blog.perplexedlabs.com/2009/07/15/xmlrpc-pingbacks-using-php/#comments</comments>
		<pubDate>Wed, 15 Jul 2009 17:39:46 +0000</pubDate>
		<dc:creator>Eric</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[pingback]]></category>
		<category><![CDATA[xmlrpc]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=308</guid>
		<description><![CDATA[Motivation: For a side project I am working on, I wanted to be able to send a trackback to WordPress blogs (or any blog with an XMLRPC endpoint), using PHP.   Simple enough.
Methodology: I had a few different options.  I could have manually made a POST request to the endpoint, hand-coding the XML (this brute force [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/04/09/php-daisy-chain-class-method-calls/' rel='bookmark' title='Permanent Link: PHP Daisy Chain Class Method Calls'>PHP Daisy Chain Class Method Calls</a></li><li><a href='http://blog.perplexedlabs.com/2009/02/05/dynamic-javascript-script-insertion-for-embedding/' rel='bookmark' title='Permanent Link: PHP Dynamic JavaScript SCRIPT Insertion for Embedding'>PHP Dynamic JavaScript SCRIPT Insertion for Embedding</a></li><li><a href='http://blog.perplexedlabs.com/2009/04/22/php-named-parameters/' rel='bookmark' title='Permanent Link: PHP Named Parameters'>PHP Named Parameters</a></li></ol>]]></description>
			<content:encoded><![CDATA[<p><strong>Motivation: </strong>For a side project I am working on, I wanted to be able to send a trackback to WordPress blogs (or any blog with an XMLRPC endpoint), using PHP.   Simple enough.</p>
<p><strong>Methodology: </strong>I had a few different options.  I could have manually made a POST request to the endpoint, hand-coding the XML (this brute force method).  Or I could stand on the shoulders of giants and leverage the power of open-source libraries.  I chose the latter, in keeping with my new guiding principle of pursuing beauty, truth and goodness.</p>
<p>To brush up on how an XMLRPC pingback works, from a 30,000 foot level, I checked out the <a href="http://hixie.ch/specs/pingback/pingback-1.0">Pingback 1.0 Specification</a>.  As far as the libraries, I settled on <a href="http://phpxmlrpc.sourceforge.net">phpxmlrpc</a>.  First, the documentation existed and second, it was straightforward.  According to the <a href="http://phpxmlrpc.sourceforge.net/doc-1.1/index.html">documentation</a>, the process goes like this:</p>
<ol>
<li>Create an xmlrpc client</li>
<li>Set the debug level you think you need (optional)</li>
<li>Create a new message</li>
<li>Send the message</li>
<li>Parse the response</li>
</ol>
<p><strong>Implementation:</strong> Again, this isn't rocket science, but here's what I did.  Suggestions and critiques are welcome.</p>
<pre class="brush: php;">
$xmlrpc_client = new xmlrpc_client(&quot;xmlrpc.php&quot;, $host_url, 80);
$xmlrpc_client-&gt;setDebug(1); //this will print all the responses as they come back
$xmlrpc_message = new xmlrpcmsg(&quot;pingback.ping&quot;, array(new xmlrpcval($site_linking_from), new xmlrpcval($site_linking_to)));
$xmlrpc_response = $xmlrpc_client-&gt;send($xmlrpc_message);
if($xmlrpc_response-&gt;faultCode() == 0){
echo $xmlrpc_response-&gt;faultString();
}else{
echo &quot;Pingback successful&quot;;
}
</pre>
<p>A couple of notes.  On line 3, "pingback.ping" is the XMLRPC API method that handles pingbacks.  Also, the order of the sites you send to the call would be your site, and the site you're pinging.  I switched this order around the first time and got some interesting error messages.</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2008/04/09/php-daisy-chain-class-method-calls/' rel='bookmark' title='Permanent Link: PHP Daisy Chain Class Method Calls'>PHP Daisy Chain Class Method Calls</a></li><li><a href='http://blog.perplexedlabs.com/2009/02/05/dynamic-javascript-script-insertion-for-embedding/' rel='bookmark' title='Permanent Link: PHP Dynamic JavaScript SCRIPT Insertion for Embedding'>PHP Dynamic JavaScript SCRIPT Insertion for Embedding</a></li><li><a href='http://blog.perplexedlabs.com/2009/04/22/php-named-parameters/' rel='bookmark' title='Permanent Link: PHP Named Parameters'>PHP Named Parameters</a></li></ol></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/plabs?a=9GNQAJ1Uy4Q:BkQ5TUCr3lE:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/plabs?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=9GNQAJ1Uy4Q:BkQ5TUCr3lE:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/plabs?i=9GNQAJ1Uy4Q:BkQ5TUCr3lE:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=9GNQAJ1Uy4Q:BkQ5TUCr3lE:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/plabs?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=9GNQAJ1Uy4Q:BkQ5TUCr3lE:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/plabs?i=9GNQAJ1Uy4Q:BkQ5TUCr3lE:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=9GNQAJ1Uy4Q:BkQ5TUCr3lE:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/plabs?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=9GNQAJ1Uy4Q:BkQ5TUCr3lE:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/plabs?i=9GNQAJ1Uy4Q:BkQ5TUCr3lE:gIN9vFwOqvQ" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/07/15/xmlrpc-pingbacks-using-php/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>PHP 5.3.0 Released and Firefox 3.5</title>
		<link>http://blog.perplexedlabs.com/2009/06/30/php-5-3-0-released-and-firefox-3-5/</link>
		<comments>http://blog.perplexedlabs.com/2009/06/30/php-5-3-0-released-and-firefox-3-5/#comments</comments>
		<pubDate>Tue, 30 Jun 2009 17:00:32 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Random]]></category>
		<category><![CDATA[firefox]]></category>
		<category><![CDATA[php]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=304</guid>
		<description><![CDATA[Pretty major day in the world of the web, eh?
The PHP development team announced the release of version 5.3.0.  This is a major milestone.  Here's what I'm most excited about:

Lambda Functions and Closures
ternary short cut "?:"
Optional garbage collection for cyclic references

I'm excited about support for namespaces, but not the particular syntactical implementation chosen.
Also, [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/07/29/django-1-1-released/' rel='bookmark' title='Permanent Link: Django 1.1 Released'>Django 1.1 Released</a></li><li><a href='http://blog.perplexedlabs.com/2008/11/11/remove-firefox-link-outline/' rel='bookmark' title='Permanent Link: Remove Firefox Link Outline'>Remove Firefox Link Outline</a></li><li><a href='http://blog.perplexedlabs.com/2008/12/22/jquery-13-beta-testing/' rel='bookmark' title='Permanent Link: jQuery 1.3 Beta Testing'>jQuery 1.3 Beta Testing</a></li></ol>]]></description>
			<content:encoded><![CDATA[<p>Pretty major day in the world of the web, eh?</p>
<p>The PHP development team announced the <a href="http://php.net/releases/5_3_0.php">release of version 5.3.0</a>.  This is a <strong>major</strong> milestone.  Here's what I'm most excited about:</p>
<ul>
<li>Lambda Functions and Closures</li>
<li>ternary short cut "?:"</li>
<li>Optional garbage collection for cyclic references</li>
</ul>
<p>I'm excited about support for namespaces, but not the particular syntactical implementation chosen.</p>
<p>Also, the Mozilla team announced the official release of <a href="http://download.mozilla.org/?product=firefox-3.5&#038;os=win&#038;lang=en-US">Firefox 3.5</a>.  Firefox is my browser of choice thanks to the great web development plugins available for it such as:</p>
<ul>
<li><a href="http://getfirebug.com/">Firebug</a></li>
<li><a href="http://code.google.com/p/httpfox/">HttpFox</a></li>
<li><a href="http://chrispederick.com/work/web-developer/">Web Developer Toolbar</a>
</ul>
<p>Version 3.5 looks to make an already excellent browser even faster.</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/07/29/django-1-1-released/' rel='bookmark' title='Permanent Link: Django 1.1 Released'>Django 1.1 Released</a></li><li><a href='http://blog.perplexedlabs.com/2008/11/11/remove-firefox-link-outline/' rel='bookmark' title='Permanent Link: Remove Firefox Link Outline'>Remove Firefox Link Outline</a></li><li><a href='http://blog.perplexedlabs.com/2008/12/22/jquery-13-beta-testing/' rel='bookmark' title='Permanent Link: jQuery 1.3 Beta Testing'>jQuery 1.3 Beta Testing</a></li></ol></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/plabs?a=qBNBMo3flzI:GubxNPqgtjY:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/plabs?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=qBNBMo3flzI:GubxNPqgtjY:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/plabs?i=qBNBMo3flzI:GubxNPqgtjY:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=qBNBMo3flzI:GubxNPqgtjY:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/plabs?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=qBNBMo3flzI:GubxNPqgtjY:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/plabs?i=qBNBMo3flzI:GubxNPqgtjY:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=qBNBMo3flzI:GubxNPqgtjY:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/plabs?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=qBNBMo3flzI:GubxNPqgtjY:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/plabs?i=qBNBMo3flzI:GubxNPqgtjY:gIN9vFwOqvQ" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/06/30/php-5-3-0-released-and-firefox-3-5/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Be Language Agnostic – Solve the Problem!</title>
		<link>http://blog.perplexedlabs.com/2009/06/01/be-language-agnostic-solve-the-problem/</link>
		<comments>http://blog.perplexedlabs.com/2009/06/01/be-language-agnostic-solve-the-problem/#comments</comments>
		<pubDate>Mon, 01 Jun 2009 14:15:28 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Django]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[Ruby]]></category>
		<category><![CDATA[Ruby on Rails]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[rails]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://www.perplexedlabs.com/?p=280</guid>
		<description><![CDATA[I like Python.  I like Ruby.  I like C, C++, and Objective-C.  I like Java.  I also (actually) like PHP.  I like programming - get it?  Use whatever gets the job done and done well.  Use whatever achieves the performance and scalability you require for a given task. [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/03/20/django-and-python-first-impressions-part-ii/' rel='bookmark' title='Permanent Link: Django and Python First Impressions &#8211; Part II'>Django and Python First Impressions &#8211; Part II</a></li><li><a href='http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/' rel='bookmark' title='Permanent Link: Adventures in Django and Python &#8211; Part III'>Adventures in Django and Python &#8211; Part III</a></li><li><a href='http://blog.perplexedlabs.com/2009/07/31/twitter-and-sd-news/' rel='bookmark' title='Permanent Link: Twitter and SD News'>Twitter and SD News</a></li></ol>]]></description>
			<content:encoded><![CDATA[<p>I like Python.  I like Ruby.  I like C, C++, and Objective-C.  I like Java.  I also (actually) like PHP.  I like programming - get it?  Use whatever gets the job done and done well.  Use whatever achieves the performance and scalability you require for a given task.  Use what makes sense given a specific problem's domain.  <del datetime="2009-06-09T13:43:01+00:00">Use whatever aligns itself with the way your mind works</del>.  Don't be a one trick pony (that isn't a Django reference).  Learn multiple languages, their strengths, and their weaknesses.  Understand when a language's strengths will allow you to solve a problem faster, easier, better.  Don't force a square peg through a round hole.</p>
<p>Watch <strong><a href="http://www.itworld.com/video?bcpid=1578108607&#038;bclid=1588003312&#038;bctid=23220283001">this</a></strong> video.  It's excellent food for thought and drives home the importance, as a programmer, of learning new, different, languages to expand your ability to solve problems in a variety of ways.  The video mentions the <a href="http://en.wikipedia.org/wiki/Sapir%E2%80%93Whorf_hypothesis">Sapir–Whorf hypothesis</a> which suggests that a particular language influences how a person understands and interacts with the world.  This makes a lot of sense.</p>
<p><strong>A programmer who thinks only in terms of a single language will attempt to solve every problem with that language.  You need more than one tool on your belt because not every problem is a nail.</strong></p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2009/03/20/django-and-python-first-impressions-part-ii/' rel='bookmark' title='Permanent Link: Django and Python First Impressions &#8211; Part II'>Django and Python First Impressions &#8211; Part II</a></li><li><a href='http://blog.perplexedlabs.com/2009/08/13/adventures-in-django-and-python-part-iii/' rel='bookmark' title='Permanent Link: Adventures in Django and Python &#8211; Part III'>Adventures in Django and Python &#8211; Part III</a></li><li><a href='http://blog.perplexedlabs.com/2009/07/31/twitter-and-sd-news/' rel='bookmark' title='Permanent Link: Twitter and SD News'>Twitter and SD News</a></li></ol></p><div class="feedflare">
<a href="http://feeds.feedburner.com/~ff/plabs?a=By_DnqUwFj0:024GIbPmfaI:yIl2AUoC8zA"><img src="http://feeds.feedburner.com/~ff/plabs?d=yIl2AUoC8zA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=By_DnqUwFj0:024GIbPmfaI:F7zBnMyn0Lo"><img src="http://feeds.feedburner.com/~ff/plabs?i=By_DnqUwFj0:024GIbPmfaI:F7zBnMyn0Lo" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=By_DnqUwFj0:024GIbPmfaI:7Q72WNTAKBA"><img src="http://feeds.feedburner.com/~ff/plabs?d=7Q72WNTAKBA" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=By_DnqUwFj0:024GIbPmfaI:V_sGLiPBpWU"><img src="http://feeds.feedburner.com/~ff/plabs?i=By_DnqUwFj0:024GIbPmfaI:V_sGLiPBpWU" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=By_DnqUwFj0:024GIbPmfaI:qj6IDK7rITs"><img src="http://feeds.feedburner.com/~ff/plabs?d=qj6IDK7rITs" border="0"></img></a> <a href="http://feeds.feedburner.com/~ff/plabs?a=By_DnqUwFj0:024GIbPmfaI:gIN9vFwOqvQ"><img src="http://feeds.feedburner.com/~ff/plabs?i=By_DnqUwFj0:024GIbPmfaI:gIN9vFwOqvQ" border="0"></img></a>
</div>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2009/06/01/be-language-agnostic-solve-the-problem/feed/</wfw:commentRss>
		<slash:comments>8</slash:comments>
		</item>
	</channel>
</rss>
