<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Jon Maddox</title>
  <link href="http://www.jonmaddox.com/atom.xml" rel="self" type="application/atom+xml" />
  <link href="http://www.jonmaddox.com/" rel="alternate" type="text/html" />
  <id>http://www.jonmaddox.com/</id>
  <updated>2012-03-27T16:26:12-07:00</updated>
  <author>
    <name>Jon Maddox</name>
  </author>

  
    <entry>
      <title type="html">Your Platform Must be Compelling</title>
      <link href="http://www.jonmaddox.com/2011/09/05/your-platform-or-language-must-be-compelling/" rel="alternate" type="text/html" />
      <published>2011-09-05T00:00:00-07:00</published>
      <updated>2011-09-05T00:00:00-07:00</updated>
      <id>http://www.jonmaddox.com/2011/09/05/your-platform-or-language-must-be-compelling</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2011/09/05/your-platform-or-language-must-be-compelling/">&lt;p&gt;To attract the attention of developers, your platform must be compelling. Wikipedia defines The World Wide Web as:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A system of interlinked hypertext documents accessed via the Internet.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The web provided a simple way for posting text and images whenever you wanted, available all the time, accessible by anyone in the world. Using a simple markup language, all a programmer had to do was write a little bit of markup, use a simple tool to upload it to a server, and her work was viewable by anyone in the world at any time of the day. There was no one telling her what she could or couldn&amp;#8217;t post. Armed with a browser to decode these documents, users could consume and bounce between all of these documents. It was simple, valuable, and extremely accessible. Most importantly, it was extremely compelling.&lt;/p&gt;

&lt;p&gt;But what makes a platform compelling? Programmers are finicky, but simple to please. Hitting a few key points may make all the difference in growing your developer community.&lt;/p&gt;

&lt;h2 id='low_effort_high_reward'&gt;Low effort, high reward&lt;/h2&gt;

&lt;p&gt;Programmers are lazy. That&amp;#8217;s usually what makes them good programmers. They&amp;#8217;re willing to put in a little work if it means they don&amp;#8217;t have to do a lot of work later. Something that lets a programmer jump right in, put in a little time, and end up with something great will have that person hooked.&lt;/p&gt;

&lt;p&gt;Ruby on Rails nailed this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removed core decisions from the boostrapping process of starting a project&lt;/li&gt;

&lt;li&gt;Provided a source tree for developers allowing them to bounce between projects without a learning curve&lt;/li&gt;

&lt;li&gt;Provided well thought out support for tons of features every web app needs&lt;/li&gt;

&lt;li&gt;Provided a drop dead simple plug-in system allowing common patterns in web apps to be reused with other projects, shared with other developers, and more importantly, not reinvented&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These added up to an environment that removed wheel reinventing and allowed developers to add awesome features to their app with very little effort. Developers flocked to it.&lt;/p&gt;

&lt;h2 id='enable_multiple_points_of_value_for_free'&gt;Enable multiple points of value for free&lt;/h2&gt;

&lt;p&gt;There&amp;#8217;s nothing a developer likes more than creating a project and getting things for free. Make getting extras easier for your developers and they&amp;#8217;ll run to you.&lt;/p&gt;

&lt;p&gt;iOS nailed this.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A very exciting hardware target (iPhone) allowing developers to create software they&amp;#8217;d have with them anywhere they go&lt;/li&gt;

&lt;li&gt;Provided development tools allowing ideas to go from inside a head to a desirable mobile device unprecedentedly fast&lt;/li&gt;

&lt;li&gt;Trivially support 2 other device classes (iPod touch &amp;amp; iPad)&lt;/li&gt;

&lt;li&gt;Deployable to the App Store with millions of users that actually pay for apps&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;iOS developers typically get all of these things at trivial costs. Say what you want about Android and its device numbers, but it barely hits these four points. That&amp;#8217;s why it&amp;#8217;s void of interesting, classy apps.&lt;/p&gt;

&lt;h2 id='make_it_accessible'&gt;Make it accessible&lt;/h2&gt;

&lt;p&gt;How long does it take for me to access, learn, and integrate your thing? Programmers have short attention spans. When picking from a pool, the speed in which they can get their hands dirty is a major decision factor.&lt;/p&gt;

&lt;p&gt;Twitter nailed this. At least in the beginning.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;RESTful API&lt;/li&gt;

&lt;li&gt;No API key needed&lt;/li&gt;

&lt;li&gt;Basic Auth for authentication&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After a single glance at the API documentation, a programmer could start playing with the API. What&amp;#8217;s easier and cooler than getting your Twitter stream by doing a cURL GET? No need to sign up or get approved for an API key first. No obscure awkward authentication scheme for posting. Just 100% HTTP, something every modern programming language has support for. It was trivial to begin creating one of the over &lt;a href='http://blog.twitter.com/2011/07/one-million-registered-twitter-apps.html'&gt;1 million&lt;/a&gt; Twitter clients and integrations.&lt;/p&gt;

&lt;h2 id='it_all_adds_up_to_fun'&gt;It all adds up to fun&lt;/h2&gt;

&lt;p&gt;All of these add up to something super fun to play with. Yes, play. Programming is a funny thing. It lies somewhere between career/hobby and eager programmers are always looking for something new to play with.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">iLife Server</title>
      <link href="http://www.jonmaddox.com/2011/03/22/5-ilife-server/" rel="alternate" type="text/html" />
      <published>2011-03-22T00:00:00-07:00</published>
      <updated>2011-03-22T00:00:00-07:00</updated>
      <id>http://www.jonmaddox.com/2011/03/22/5-ilife-server</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2011/03/22/5-ilife-server/">&lt;p&gt;The following is not a description of what I think Apple will ship or would ever ship. It is not what I think the general world of consumers wants or needs. This is a software platform I want. What I want for my house. What I want for my family. Are there holes in this idea? Of course there is, this is a blog post, not a 500 page spec.&lt;/p&gt;
&lt;p&gt;Simply put, this is the dream setup I like to call iLife server. It&amp;#8217;s a canonical meta store and server for your music, photos, and video (if buying &lt;span class=&quot;caps&quot;&gt;DRM&lt;/span&gt; video is something you&amp;#8217;re interested in). Not just &lt;strong&gt;your&lt;/strong&gt; music, but your wife&amp;#8217;s, children&amp;#8217;s, or whoever else lives in your house (most likely anyone using the same credit card).&lt;/p&gt;
&lt;h2&gt;What?&lt;/h2&gt;
&lt;p&gt;iLife server lives on your always on computer. This is most likely a family iMac. It runs silently like any server software would, waiting for you to interact with it remotely. It stores all of the music, photos, and videos for the entire household. Maybe it has a local client you can use to do some management, but this typically isn&amp;#8217;t the way you interact with the media that it stores. So how do you use it?&lt;/p&gt;
&lt;p&gt;iTunes and iPhoto are now dumb clients. When you open iTunes on your laptop or Mac Pro, it connects to iLife server and looks exactly like what you&amp;#8217;re used to seeing now. Except none of the media is actually local. You can play music all day, make playlists, buy music, import music, delete music, edit metadata. iTunes relays all of this over to iLife server. Most of these ideas work for iPhoto too but won&amp;#8217;t be detailed here.&lt;/p&gt;
&lt;p&gt;But what about when you leave the house, you might say. The idea of branded Home Sharing is dead. It&amp;#8217;s just sharing. Because that&amp;#8217;s the entire point of iLife server. Media accessibility. When you pop open your MacBook air at your coffee shop, all of your music is right there in iTunes. It streams right from iLife server at your house. Oh, and this works on your iPhone, iPad, and iPod touch too.&lt;/p&gt;
&lt;p&gt;But what about syncing, you might say. It&amp;#8217;s simple, you sync just like you did before. Except the media is fetched over the network. You might think this would be slow, but you would be surprised. Remember FireWire iPods? Mmm&amp;#8230; That was some nice syncing. Those days are gone. Between &lt;span class=&quot;caps&quot;&gt;USB&lt;/span&gt; 2 and insanely slow sustained transfers on solid state, these devices sync super slow. The bottle neck isn&amp;#8217;t wifi anymore like it might have been 5 years ago.&lt;/p&gt;
&lt;p&gt;But wouldn&amp;#8217;t this get messy, you&amp;#8217;re basically suggesting a single media library for multiple people, you might say. Have you seen your own iTunes and iPhoto libraries recently? I rest my case. But to preserve some identity, iLife server would be user spaced. You have an identity on iLife server.&lt;/p&gt;
&lt;p&gt;When you open iTunes it connects to iLife server with your identity. Just think logging in to YouTube. When you make a new playlist, this is your playlist. It appears in your iTunes clients and only yours. Want to make it available to everyone? Set it to be global and it appears available to everyone.&lt;/p&gt;
&lt;h2&gt;Why?&lt;/h2&gt;
&lt;p&gt;90% of the computers that Apple sells are notebooks. Notebooks get closed, put away, and are basically never &amp;#8220;on&amp;#8221;. Meanwhile, they make incredible music sharing software with AirTunes, AirPlay, iTunes DJ, and iPhone Remote. When I want to listen to my music (it&amp;#8217;s all on my Mac Pro) I flip open the Remote app and push music to my multiple Airport Expresses. Great. My wife uses a MacBook. When she wants to listen to her music, she gets to pull out her notebook, plug it in, open it, set it on the table, and flip open the Remote app. This isn&amp;#8217;t even an option. She has not and never will do this. Having her media on an available server resolves this.&lt;/p&gt;
&lt;p&gt;When I want to grab photos or music out of her library, I have to go downstairs and open her laptop. What?&lt;/p&gt;
&lt;p&gt;Why do we even have individual iTunes and iPhoto libraries? If we were roommates this pattern is forgivable. But we&amp;#8217;re not. There&amp;#8217;s no reason for this level of personalization of media in a household. Personalization should be maintained with playlists, photo albums, etc. The &amp;#8220;library&amp;#8221; is nothing but a pool of media that everyone wants access to.&lt;/p&gt;
&lt;h2&gt;Will this ever happen?&lt;/h2&gt;
&lt;p&gt;I wouldn&amp;#8217;t bet on it. But I&amp;#8217;ve tortured myself wanting something like it for years now. For me, this solves the accessibility problems of libraries trapped on laptops, merges redundant libraries, and ubiquitous availability of my media. The scary part is that, even though it would be a massive undertaking, it&amp;#8217;s definitely doable.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">$5 for Xcode Isn't Wrong, It's Just Weird</title>
      <link href="http://www.jonmaddox.com/2011/03/09/5-for-xcode-isnt-wrong/" rel="alternate" type="text/html" />
      <published>2011-03-09T00:00:00-08:00</published>
      <updated>2011-03-09T00:00:00-08:00</updated>
      <id>http://www.jonmaddox.com/2011/03/09/5-for-xcode-isnt-wrong</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2011/03/09/5-for-xcode-isnt-wrong/">&lt;p&gt;There&amp;#8217;s a lot of talk on Twitter about Apple charging $5 for Xcode. People are either complaining about the price, or complaining about people complaining about the price. I think a lot of people are missing the point.&lt;/p&gt;
&lt;p&gt;Xcode is a very large piece of software. I have no problem with Apple charging $499 for it. The problem is, Xcode isn&amp;#8217;t just Xcode, its the canonical way to get a &lt;span class=&quot;caps&quot;&gt;GCC&lt;/span&gt; toolchain in OS X. And now Apple has it on a store front as &amp;#8220;Xcode&amp;#8221;, the software that provides &amp;#8220;everything developers need to create great applications for Mac, iPhone, and iPad.&amp;#8221;&lt;/p&gt;
&lt;p&gt;This means in order to build software like ImageMagick, anything distributed with MacPorts or Homebrew, or other &lt;span class=&quot;caps&quot;&gt;OSS&lt;/span&gt; software packages you might use, you need Xcode. Where it gets weird is that none of these things actually &lt;span class=&quot;caps&quot;&gt;NEED&lt;/span&gt; the software called Xcode. But you have to buy Xcode if you want to use them. This is not only confusing but also creates very weird scenarios. I&amp;#8217;ll try to describe a few of them.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Software like &lt;a href=&quot;https://github.com/holman/boom&quot;&gt;Boom&lt;/a&gt; isn&amp;#8217;t even developer oriented. A normal user now has to pay $5 to use it. Yes, normal people use this&lt;/li&gt;
	&lt;li&gt;Ruby developers that have spent years installing Xcode just to build their C backed gems now have to pay $5 for Xcode.&lt;/li&gt;
	&lt;li&gt;What about employees that work in other languages? Now, they not only have to expense $5 for tools but explain why. Manager: &amp;#8220;Why? You don&amp;#8217;t write iOS apps.&amp;#8221;&lt;/li&gt;
	&lt;li&gt;How does the employee purchase Xcode? If buys and expenses an Xcode license, its not even property of their employer. It&amp;#8217;s the employee&amp;#8217;s, because of the way the Mac App Store works.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Having to pay $5 for an advertised piece of software that you&amp;#8217;re not really even using is weird. Apple very well may be planning on shipping 10.7 with a toolchain to allow the building of just such software. That would definitely remove the weirdness.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">Get Push Notifications From Campfire or Anything Else That Growls</title>
      <link href="http://www.jonmaddox.com/2009/08/12/get-push-notifications-from-campfire-or-anything-else-that-growls/" rel="alternate" type="text/html" />
      <published>2009-08-12T00:00:00-07:00</published>
      <updated>2009-08-12T00:00:00-07:00</updated>
      <id>http://www.jonmaddox.com/2009/08/12/get-push-notifications-from-campfire-or-anything-else-that-growls</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2009/08/12/get-push-notifications-from-campfire-or-anything-else-that-growls/">&lt;p&gt;Use Case:&lt;/p&gt;
&lt;p&gt;You&amp;#8217;re away from your computer and something tragic/awesome happens in Campfire. Someone pings you, but you&amp;#8217;re not there to get it. You come back to your desktop with 100 growl messages of people pinging you in Campfire, and you missed something tragic/awesome. Eff that. Thats what happens to you. Here&amp;#8217;s what happens to me:&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m on the couch, someone pings me in Campfire: &amp;#8220;Maddox, we&amp;#8217;re adding an instance because of load&amp;#8221;. I immediately get that message pushed to my phone and can then open &lt;a href=&quot;http://overcommittedapps.com/ember/&quot;&gt;Ember&lt;/a&gt; on my iPhone to immediately drop into the campfire and find out what&amp;#8217;s going on. That means at the airport, in bed, or at a bar. It&amp;#8217;s a really amazing flow.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://img.skitch.com/20090812-nex6mngpytcxx7tt9s6j71wf9h.jpg&quot; class=&quot;centered&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;With &lt;a href=&quot;http://prowl.weks.net/&quot;&gt;Prowl&lt;/a&gt; you can get all your growl notifications pushed to your iPhone. It&amp;#8217;s a simple plug-in that pushes every growl message you get, to your phone. But we don&amp;#8217;t want every message sent to our phone. So here&amp;#8217;s the quick lesson on my awesome strategy for keeping the noise down while getting the important things.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;You need to purchase Prowl from the &lt;a href=&quot;http://itunes.apple.com/WebObjects/MZStore.woa/wa/viewSoftware?id=320876271&quot;&gt;app store&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;You need sign up with the prowl service. Register &lt;a href=&quot;https://prowl.weks.net/register.php&quot;&gt;here&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;Read installation &lt;a href=&quot;http://prowl.weks.net/installation.php&quot;&gt;instructions&lt;/a&gt; or just skip ahead&lt;/li&gt;
	&lt;li&gt;Download the &lt;a href=&quot;http://prowl.weks.net/static/Prowl_1.0.4_growlView.zip&quot;&gt;plug-in&lt;/a&gt;&lt;/li&gt;
	&lt;li&gt;Install it&lt;/li&gt;
	&lt;li&gt;Now you have to set it up which is the &lt;span class=&quot;caps&quot;&gt;TRICKY&lt;/span&gt; part&lt;/li&gt;
	&lt;li&gt;Go to the app that you want to Prowl, because you don&amp;#8217;t want &lt;span class=&quot;caps&quot;&gt;EVERY&lt;/span&gt; growl notification going to your phone. Just the apps that you choose.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;http://img.skitch.com/20090812-jwd7jjf4hyx7x3pneibwsc4624.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;Set the display style for the app to &amp;#8220;Prowl&amp;#8221;&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;Butttt the trick is, you dont want it to always use Prowl. That would be annoying. You want the app to use Growl while you&amp;#8217;re using your computer, but Prowl when you&amp;#8217;re away. So&amp;#8230; you trick it out.&lt;/li&gt;
&lt;/ul&gt;
&lt;ul&gt;
	&lt;li&gt;Go to the display options tab and configure Prowl&lt;/li&gt;
	&lt;li&gt;Add your username and password&lt;/li&gt;
	&lt;li&gt;Now, this is the most crucial step:&lt;/li&gt;
	&lt;li&gt;Set Prowl up to only work if your computer has been idle for a set amount of time&lt;/li&gt;
	&lt;li&gt;And tell it what growl style to use when you haven&amp;#8217;t been idle.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&quot;http://img.skitch.com/20090812-jrcggnuuyjdqsswf29xuj2rdpr.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;And you&amp;#8217;re all set up. So all day long as I work, Campfire (via the awesome &lt;a href=&quot;http://propaneapp.com&quot;&gt;Propane.app&lt;/a&gt;) will growl to my screen. But, the second I&amp;#8217;m away from my computer for longer than 5 minutes, all the growl notifications from Campfire will be sent via push notification to my iPhone.&lt;/p&gt;
&lt;p&gt;This is an amazing flow, especially when you know everyone is using it. It means you can page them remotely anytime you really really need them. It&amp;#8217;s the future.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">Nginx + SSL from Network Solutions</title>
      <link href="http://www.jonmaddox.com/2009/02/03/nginx-ssl-from-network-solutions/" rel="alternate" type="text/html" />
      <published>2009-02-03T00:00:00-08:00</published>
      <updated>2009-02-03T00:00:00-08:00</updated>
      <id>http://www.jonmaddox.com/2009/02/03/nginx-ssl-from-network-solutions</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2009/02/03/nginx-ssl-from-network-solutions/">&lt;p&gt;These days you need an intermediate cert when using an &lt;span class=&quot;caps&quot;&gt;SSL&lt;/span&gt;. Last time I set up a cert from Network Solutions, this intermediate cert was clearly named. This time around they issued quite a few certs and it made it confusing.&lt;/p&gt;
&lt;ul&gt;
	&lt;li&gt;AddTrustExternalCARoot.crt&lt;/li&gt;
	&lt;li&gt;MY.&lt;span class=&quot;caps&quot;&gt;DOMAIN&lt;/span&gt;.&lt;span class=&quot;caps&quot;&gt;COM&lt;/span&gt;.crt&lt;/li&gt;
	&lt;li&gt;NetworkSolutions_CA.crt&lt;/li&gt;
	&lt;li&gt;UTNAddTrustServer_CA.crt&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The two certs we care about here are &lt;strong&gt;MY.&lt;span class=&quot;caps&quot;&gt;DOMAIN&lt;/span&gt;.&lt;span class=&quot;caps&quot;&gt;COM&lt;/span&gt;.crt&lt;/strong&gt;, my site&amp;#8217;s ssl cert, and NetworkSolutions_CA.crt, Network Solution&amp;#8217;s intermediate key.&lt;/p&gt;
&lt;p&gt;Ok, now the tricky part. With old Apache, there was a specific directive for defining the intermediate cert. Nginx participates in no such thing. With Nginx we just need to cat our two certs together.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;cat MY.DOMAIN.COM.crt NetworkSolutions_CA.crt &amp;gt; combined.crt
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now just add this to your conf with:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;ssl_certificate /usr/local/nginx/certs/combined.crt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</content>
    </entry>
  
    <entry>
      <title type="html">Jeez People, Stop Fretting Over Installing RMagick</title>
      <link href="http://www.jonmaddox.com/2008/06/06/jeez-people-stop-fretting-over-installing-rmagick/" rel="alternate" type="text/html" />
      <published>2008-06-06T00:00:00-07:00</published>
      <updated>2008-06-06T00:00:00-07:00</updated>
      <id>http://www.jonmaddox.com/2008/06/06/jeez-people-stop-fretting-over-installing-rmagick</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2008/06/06/jeez-people-stop-fretting-over-installing-rmagick/">&lt;p&gt;Rmagick. It sucks, I know. We all hate it. But you have apps that depend on it and haven&amp;#8217;t changed that yet. So you need it installed for development. And you hate installing it.&lt;/p&gt;
&lt;p&gt;But really, its not that installing RMagick sucks, its installing ImageMagick that sucks. Its hard. But the only thing worse than hard, is stupid ass MacPorts. Which is ********.&lt;/p&gt;
&lt;p&gt;So all I hear is people complain over and over about having to install it. Meanwhile there has been a script to install ImageMagick and RMagick on OS X forever, but people just don&amp;#8217;t seem to know its there.&lt;/p&gt;
&lt;p&gt;I tell people about it and they just ignore me and then complain about installing RMagick to me later on!! &lt;span class=&quot;caps&quot;&gt;WTF&lt;/span&gt;?!&lt;/p&gt;
&lt;p&gt;So, I&amp;#8217;ve had it. When you format or get a new computer go here: &lt;del datetime=&quot;2009-01-22T07:54:59+00:00&quot;&gt;&lt;a href=&quot;http://rubyforge.org/frs/?group_id=12&amp;release_id=21634&quot;&gt;http://rubyforge.org/frs/?group_id=12&amp;amp;release_id=21634&lt;/a&gt;&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;&lt;del datetime=&quot;2009-01-22T07:58:22+00:00&quot;&gt;Download rm_install&lt;/del&gt;&lt;/p&gt;
&lt;p&gt;That old script isn&amp;#8217;t maintained and never works. I&amp;#8217;ve decided to post one myself on GitHub with the hope that people will not only use it, but make sure it never gets stale. Grab it here: &lt;a href=&quot;http://www.github.com/maddox/magick-installer&quot;&gt; http://www.github.com/maddox/magick-installer &lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Now, once its downloaded. Do this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;sudo ./magick-installer.sh
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;and let it run forever. Done. Pretty easy right? There. &lt;span class=&quot;caps&quot;&gt;PLEASE&lt;/span&gt; &lt;span class=&quot;caps&quot;&gt;USE&lt;/span&gt; IT. Its a fantastic script that works really well. Its not hard to install ImageMagick anymore! Dance!&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">Show Your GIT Branch Name In Your Prompt</title>
      <link href="http://www.jonmaddox.com/2008/03/13/show-your-git-branch-name-in-your-prompt/" rel="alternate" type="text/html" />
      <published>2008-03-13T00:00:00-07:00</published>
      <updated>2008-03-13T00:00:00-07:00</updated>
      <id>http://www.jonmaddox.com/2008/03/13/show-your-git-branch-name-in-your-prompt</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2008/03/13/show-your-git-branch-name-in-your-prompt/">&lt;p&gt;Typing &lt;code&gt;git branch&lt;/code&gt; over and over to see what branch you are on sucks. Sure, you could argue that you should always &lt;span class=&quot;caps&quot;&gt;KNOW&lt;/span&gt; what branch you&amp;#8217;re currently working on. And if you did, you would obviously not be a git user.&lt;/p&gt;
&lt;p&gt;Bouncing around branches can be pretty common, and I know I&amp;#8217;ve messed some things up pretty bad not knowing what branch I was on.&lt;/p&gt;
&lt;p&gt;So set up your shell to always put the name of the current branch into your prompt.&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://img.skitch.com/20080313-1qjnda6nm9reb4sgkii4qy3bcf.jpg&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;parse_git_branch &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  git branch --no-color 2&amp;gt; /dev/null | sed -e &lt;span class=&quot;s1&quot;&gt;&amp;#39;/^[^*]/d&amp;#39;&lt;/span&gt; -e &lt;span class=&quot;s1&quot;&gt;&amp;#39;s/* \(.*\)/(\1)/&amp;#39;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;function &lt;/span&gt;proml &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;local        &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;BLUE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;\[\033[0;34m\]&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;local         &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;RED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;\[\033[0;31m\]&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;local   &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LIGHT_RED&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;\[\033[1;31m\]&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;local       &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;GREEN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;\[\033[0;32m\]&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;local &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LIGHT_GREEN&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;\[\033[1;32m\]&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;local       &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;WHITE&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;\[\033[1;37m\]&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;nb&quot;&gt;local  &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;LIGHT_GRAY&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;\[\033[0;37m\]&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;case&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$TERM&lt;/span&gt; in
    xterm*&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;TITLEBAR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;\[\033]0;\u@\h:\w\007\]&amp;#39;&lt;/span&gt;
    ;;
    *&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;TITLEBAR&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;quot;&lt;/span&gt;
    ;;
  &lt;span class=&quot;k&quot;&gt;esac&lt;/span&gt;

&lt;span class=&quot;nv&quot;&gt;PS1&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;${TITLEBAR}\&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;$BLUE[$RED\$(date +%H:%M)$BLUE]\&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;$BLUE[$RED\u@\h:\w$GREEN\$(parse_git_branch)$BLUE]\&lt;/span&gt;
&lt;span class=&quot;s2&quot;&gt;$GREEN\$ &amp;quot;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;PS2&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;&amp;gt; &amp;#39;&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;PS4&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;+ &amp;#39;&lt;/span&gt;
&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
proml
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a href=&quot;http://pastie.caboo.se/pastes/165446&quot;&gt;Pastie Link&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Put this at the top of your .bash_profile and you&amp;#8217;ll be pimping your branch all over.&lt;/p&gt;
&lt;p&gt;Thanks to &lt;a href=&quot;http://ozmm.org&quot;&gt;@defunkt&lt;/a&gt; for this.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">Cleanly Migrate Your Subversion Repository To a GIT Repository</title>
      <link href="http://www.jonmaddox.com/2008/03/05/cleanly-migrate-your-subversion-repository-to-a-git-repository/" rel="alternate" type="text/html" />
      <published>2008-03-05T00:00:00-08:00</published>
      <updated>2008-03-05T00:00:00-08:00</updated>
      <id>http://www.jonmaddox.com/2008/03/05/cleanly-migrate-your-subversion-repository-to-a-git-repository</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2008/03/05/cleanly-migrate-your-subversion-repository-to-a-git-repository/">&lt;p&gt;So you&amp;#8217;re ready to ride the &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; train eh? But what about all those projects in your subversion repository? Sure you could just use &lt;code&gt;git-svn&lt;/code&gt; but what you really want is to cleanly move that repository and all its history to a nice new &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; repository.&lt;/p&gt;
&lt;p&gt;Luckily it&amp;#8217;s not that hard. For this example I&amp;#8217;m going to use &lt;strong&gt;my_blog&lt;/strong&gt; as the application name you&amp;#8217;re porting to a &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; repository. We&amp;#8217;re basically going to just initialize a new &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; repository, point it at your &lt;span class=&quot;caps&quot;&gt;SVN&lt;/span&gt; repository, and suck in your code and history while remapping the users to &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; users. We&amp;#8217;ll then just clone &lt;span class=&quot;caps&quot;&gt;THAT&lt;/span&gt; repository to have a clean &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; repository free of &lt;span class=&quot;caps&quot;&gt;SVN&lt;/span&gt; clutter.&lt;/p&gt;
&lt;p&gt;The first thing we need to do is create a &lt;em&gt;users&lt;/em&gt; file that maps all your &lt;span class=&quot;caps&quot;&gt;SVN&lt;/span&gt; users to your &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; users. Just make a file on your Desktop named &amp;#8216;users.txt&amp;#8217;. Map the users using this format:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;text&quot;&gt;jmaddox = Jon Maddox &amp;lt;jon@gmail.com&amp;gt;
bigpappa = Brian Biggs &amp;lt;bigpappa@gmail.com&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Simple. Now here are the commands you&amp;#8217;ll run. I&amp;#8217;ll explain them below.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;mkdir my_blog_tmp
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;my_blog_tmp
git-svn init http://code.yoursite.net/my_blog/trunk/ --no-metadata
git config svn.authorsfile ~/Desktop/users.txt
git-svn fetch
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;The first two are self explanatory, we&amp;#8217;re making a new directory for the temporary repository. The second command initializes the directory as a git-svn hybrid thing and points the origin at your &lt;span class=&quot;caps&quot;&gt;SVN&lt;/span&gt; repository. The flag, &lt;code&gt;--no-metadata&lt;/code&gt;, tells &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; to leave all the &lt;span class=&quot;caps&quot;&gt;SVN&lt;/span&gt; details behind (not the commit log). The fourth command tells &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; to remap all the &lt;span class=&quot;caps&quot;&gt;SVN&lt;/span&gt; users to &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; users when it sucks down the source and history. The last command actually does the fetching.&lt;/p&gt;
&lt;p&gt;Ok, so now after a few &lt;span class=&quot;caps&quot;&gt;LONG&lt;/span&gt; minutes, your source is all there. Do a &lt;code&gt;git log&lt;/code&gt; to see that your users have been mapped. Sweet!&lt;/p&gt;
&lt;p&gt;Now you just have one last step. You need to clone this repository. Why do we do this? When doing a normal &lt;code&gt;git clone&lt;/code&gt; it will take everything we want from the temporary repository, while leaving behind all the &lt;span class=&quot;caps&quot;&gt;SVN&lt;/span&gt; cruft that was there to support the &lt;code&gt;git-svn&lt;/code&gt; stuff.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;git clone my_blog_tmp my_blog
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Boom! You&amp;#8217;re done. Now you have a super clean &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; repository all ready to use.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">Git It?</title>
      <link href="http://www.jonmaddox.com/2008/03/04/git-it/" rel="alternate" type="text/html" />
      <published>2008-03-04T00:00:00-08:00</published>
      <updated>2008-03-04T00:00:00-08:00</updated>
      <id>http://www.jonmaddox.com/2008/03/04/git-it</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2008/03/04/git-it/">&lt;p&gt;While working today I think I heard &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; mentioned on every form of communication I use daily. It was mentioned in several &lt;span class=&quot;caps&quot;&gt;IRC&lt;/span&gt; channels, on twitter a ton, in an email, over IM, and a couple of new posts in my feeds. It has become obvious that &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; has hit the early adopter phase.&lt;/p&gt;
&lt;p&gt;I&amp;#8217;m not going to &lt;em&gt;sell&lt;/em&gt; &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; or even try to explain why it&amp;#8217;s so great. There are tons of places online that already do that. I&amp;#8217;m just going to share a couple stories.&lt;/p&gt;
&lt;h2&gt;Who Gives a Crap, I Gotta Get Work Done&lt;/h2&gt;
&lt;p&gt;This new source code management software that seems to be gaining a &lt;span class=&quot;caps&quot;&gt;HUGE&lt;/span&gt; amount of steam first popped on my radar early last summer. I contemplated playing with it, but like most things, I skipped it. I figured as a single developer, it wouldn&amp;#8217;t mean much to me. I code,commit, iterate. &lt;em&gt;&amp;#8220;What do i need with fancy branching?&amp;#8221;&lt;/em&gt; I &lt;a href=&quot;http://twitter.com/maddox/statuses/170044712&quot;&gt;said&lt;/a&gt;. But &lt;a href=&quot;http://twitter.com/peterc&quot;&gt;@peterc&lt;/a&gt; &lt;a href=&quot;http://twitter.com/peterc/statuses/170110182&quot;&gt;replied&lt;/a&gt; with my answer. I just didn&amp;#8217;t get it then.&lt;/p&gt;
&lt;p&gt;Months went by as I used &lt;span class=&quot;caps&quot;&gt;SVN&lt;/span&gt; like everyone else. I was just happy with versioned code, that&amp;#8217;s about all that &lt;span class=&quot;caps&quot;&gt;SVN&lt;/span&gt; did for me.&lt;/p&gt;
&lt;h2&gt;RubyConf 2007&lt;/h2&gt;
&lt;p&gt;As I was outside with the rest of the Ruby Smoking Crew, I got Chris Wanstrath, unofficial &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; ambassador, to try to sell me on it. He started with the normal branching pitch, and I stoppped him.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&amp;#8220;I don&amp;#8217;t branch&amp;#8221;&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;&amp;#8220;Because you don&amp;#8217;t use &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt;&amp;#8221;&lt;/em&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;BOOM. It hit me. I don&amp;#8217;t &lt;strong&gt;branch&lt;/strong&gt; because branching with &lt;span class=&quot;caps&quot;&gt;SVN&lt;/span&gt; &lt;strong&gt;sucks&lt;/strong&gt; so much fucking arse that it&amp;#8217;s just not worth it. &lt;span class=&quot;caps&quot;&gt;CERTAINLY&lt;/span&gt; not for changes that take only a few hours. With &lt;span class=&quot;caps&quot;&gt;SVN&lt;/span&gt; you hang out on trunk and make your thousands of commits. Forget if you want to try something wacky real fast, you&amp;#8217;d have to branch it! And thats just a whole lot of work.&lt;/p&gt;
&lt;h2&gt;Fast Forward a Few Days.&lt;/h2&gt;
&lt;p&gt;I&amp;#8217;m sitting in a cube at Yahoo!. I&amp;#8217;d been given a task a few days earlier. I was working on it and hit a stopping point. I needed to make a huge sweeping change. &lt;span class=&quot;caps&quot;&gt;BUT&lt;/span&gt;, two hours later, I had to demo it to a room full of Yahoo! people I had never even met. &lt;span class=&quot;caps&quot;&gt;AND&lt;/span&gt; they expected it to work. I knew my changes were going to break the whole app for a little while.&lt;/p&gt;
&lt;p&gt;Ok, so first off, this project was neither IN a repository, nor did I even have access to one. I&amp;#8217;d only been there for like two days. So I could copy my source directory and file it away for the demo, or&amp;#8230; I could finally throw &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; on my laptop and try it out! So I installed &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt;, initialized my rep. Branched it&amp;#8230;whoa what? Thats it. &lt;code&gt;git checkout -b new_model&lt;/code&gt;? Thats it?! &lt;span class=&quot;caps&quot;&gt;SICK&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;I continued my work and when it came time for the demo I just used &lt;code&gt;git checkout master&lt;/code&gt; and voila, I was back to my fully working copy. I finally &amp;#8216;got&amp;#8217; &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt;. At least the branching part. Not only is it easy to branch, you can branch locally. And by locally, I mean privately. So if you want to try something weird and crazy out, you don&amp;#8217;t need to worry about making a new branch at the central repository, just branch it locally and go to town. If it worked out, merge it back into &lt;em&gt;master&lt;/em&gt;. If it doesn&amp;#8217;t, just dump it. Now, like most &lt;span class=&quot;caps&quot;&gt;GIT&lt;/span&gt; users, I don&amp;#8217;t even work directly on master.&lt;/p&gt;
&lt;p&gt;Ok, thats my story. I guess the moral is, you&amp;#8217;re never going to get why something is good for &lt;span class=&quot;caps&quot;&gt;YOU&lt;/span&gt; until &lt;span class=&quot;caps&quot;&gt;YOU&lt;/span&gt; use it. Everyone takes different things away from products, especially when you&amp;#8217;re talking about productivity.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">Spec Out Your Cookies with Rspec</title>
      <link href="http://www.jonmaddox.com/2008/02/04/spec-out-your-cookies-with-rspec/" rel="alternate" type="text/html" />
      <published>2008-02-04T00:00:00-08:00</published>
      <updated>2008-02-04T00:00:00-08:00</updated>
      <id>http://www.jonmaddox.com/2008/02/04/spec-out-your-cookies-with-rspec</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2008/02/04/spec-out-your-cookies-with-rspec/">&lt;p&gt;Here&amp;#8217;s something that got me this weekend and I couldn&amp;#8217;t find much info on it anywhere. So like my Internet taught me, I&amp;#8217;m sharing.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;links_controller.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;cookies&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:phone&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:phone&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;links_controller_spec.rb&lt;/code&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;it&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;should save the phone number in a cookie&amp;quot;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@my_cookies&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mock&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;cookies&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;vi&quot;&gt;@my_cookies&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stub!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:[]=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;controller&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;stub!&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:cookies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;and_return&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@my_cookies&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;vi&quot;&gt;@my_cookies&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;should_receive&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:[]=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;with&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:phone&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;804-755-7555&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;

  &lt;span class=&quot;n&quot;&gt;do_post&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here I&amp;#8217;m making the expectation that a cookie will be set with the key of &amp;#8216;phone&amp;#8217; and a value of a passed in parameter.&lt;br /&gt;
My &lt;code&gt;do_post&lt;/code&gt; method takes care of the post and passes in the parameter &lt;code&gt;:phone =&amp;gt; '804-755-7555'&lt;/code&gt;.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">Put Your PHP on Port 3000 With Lighttpd</title>
      <link href="http://www.jonmaddox.com/2008/02/01/put-your-php-on-port-3000-with-lighttpd/" rel="alternate" type="text/html" />
      <published>2008-02-01T00:00:00-08:00</published>
      <updated>2008-02-01T00:00:00-08:00</updated>
      <id>http://www.jonmaddox.com/2008/02/01/put-your-php-on-port-3000-with-lighttpd</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2008/02/01/put-your-php-on-port-3000-with-lighttpd/">&lt;p&gt;While most of my work these days is in a Ruby and Rails environment, I still get my hands dirty with &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; every now and again.&lt;/p&gt;
&lt;p&gt;When working with web projects I practically require the ability to use absolute links for static content. Because of this, I usually have to host a &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; project under Apache and create a virtual name, oh and make a fake host entry in my hosts file. Blegh.&lt;/p&gt;
&lt;p&gt;This usually sucks even more when I throw in the fact that my project is under some kind of source control (which is usually always the case). So what is a frustrated developer to do?&lt;/p&gt;
&lt;p&gt;Well, the only reason this is annoying is because I don&amp;#8217;t &lt;strong&gt;have&lt;/strong&gt; to deal with it in my &lt;code&gt;script/server&lt;/code&gt; world of Rails development. So I solved my litle &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; problem by borrowing from what I learned with Rails.&lt;/p&gt;
&lt;p&gt;Instead of developing &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; apps under Apache, I installed Lighttpd and fire off an instance whenever I need to do work on the app. Very simply I added a lighty config file under &lt;code&gt;APP_ROOT_/config&lt;/code&gt; and when I want to work on my &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; app, I just start lighty. Booom. Now I have my app running at localhost:3000. Just like my Rails apps, so my fancy bookmark in my bookmark bar works for both my &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; and Rails projects. Now that &lt;span class=&quot;caps&quot;&gt;DRY&lt;/span&gt;!&lt;/p&gt;
&lt;p&gt;So, how do you get it going like this? First off, all of these instructions were done in OS X 10.5 Leopard, so be warned.&lt;/p&gt;
&lt;h2&gt;Roll Your &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;You need to roll your own &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; in order to get support for php-cgi. Do this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;mkdir src
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;src
wget http://us.php.net/distributions/php-5.2.5.tar.gz
tar xfz php-5.2.5.tar.gz
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;php-5.2.5
./configure --enable-fastcgi --enable-discard-path
  --enable-force-redirect --with-zlib --with-xml
  --with-mysql&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/usr/local/mysql
  --prefix&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/usr/local/php5-fcgi
  --disable-cli --enable-memory-limit
  --with-layout&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;GNU --with-regex&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;php

make
sudo make install
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Pretty basic. Download, configure, make, and install &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Next, rename the php binary, use tab complete I think the &lt;span class=&quot;caps&quot;&gt;XXX&lt;/span&gt; part is different per install(?)&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;sudo mv /usr/local/php-cgi/bin/php-cgi.XXX /usr/local/php-cgi/bin/php
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;Lighttpd&lt;/h2&gt;
&lt;p&gt;Ok, I&amp;#8217;m not a big fan of MacPorts, but I was having so much trouble getting Lighttpd installed on Leopard, that I had macports do it for me. So, go install macports first and:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;sudo port -d selfupdate
sudo port install lighttpd
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;All the Rest&lt;/h2&gt;
&lt;p&gt;Ok, now you need to fix a small little problem with MySql and your new &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt;. The new &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; may not see your MySql stuff correctly, so do this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;sudo mkdir /usr/local/mysql/lib/mysql
sudo cp /usr/local/mysql/lib/libmysqlclient.15.dylib /usr/local/mysql/lib/mysql/libmysqlclient.15.dylib
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;Set Up Your App&lt;/h2&gt;
&lt;p&gt;Ok, you&amp;#8217;re all set up. Now you just need to add a lighttpd config file to your app. Create APP_ROOT/config/lighty.conf with this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;server.port &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; 3000

&lt;span class=&quot;c&quot;&gt;# server.errorlog    = CWD + &amp;quot;/log/lighttpd.error.log&amp;quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# accesslog.filename = CWD + &amp;quot;/log/lighttpd.access.log&amp;quot;&lt;/span&gt;

server.modules &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;mod_rewrite&amp;quot;&lt;/span&gt;,
                   &lt;span class=&quot;s2&quot;&gt;&amp;quot;mod_access&amp;quot;&lt;/span&gt;,
                   &lt;span class=&quot;s2&quot;&gt;&amp;quot;mod_fastcgi&amp;quot;&lt;/span&gt;,
                   &lt;span class=&quot;s2&quot;&gt;&amp;quot;mod_userdir&amp;quot;&lt;/span&gt;,
                   &lt;span class=&quot;s2&quot;&gt;&amp;quot;mod_accesslog&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;


&lt;span class=&quot;c&quot;&gt;# If no file is specified, what to look for?&lt;/span&gt;
index-file.names &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;index.html&amp;quot;&lt;/span&gt;, &lt;span class=&quot;s2&quot;&gt;&amp;quot;index.php&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# Run the server under the user-name &amp;quot;www&amp;quot; for security purposes&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# To bind to port 80, the server must be called by root user, but we don&amp;#39;t want&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# the server to have free run of the box, so it runs as &amp;quot;www&amp;quot;&lt;/span&gt;


&lt;span class=&quot;c&quot;&gt;# Set up the appropriate MIME type mappings&lt;/span&gt;
mimetype.assign             &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.pdf&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/pdf&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.sig&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/pgp-signature&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.spl&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/futuresplash&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.class&amp;quot;&lt;/span&gt;        &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/octet-stream&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.ps&amp;quot;&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/postscript&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.torrent&amp;quot;&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/x-bittorrent&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.dvi&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/x-dvi&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.gz&amp;quot;&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/x-gzip&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.pac&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/x-ns-proxy-autoconfig&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.swf&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/x-shockwave-flash&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.tar.gz&amp;quot;&lt;/span&gt;       &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/x-tgz&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.tgz&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/x-tgz&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.tar&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/x-tar&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.zip&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/zip&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.mp3&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;audio/mpeg&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.m3u&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;audio/x-mpegurl&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.wma&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;audio/x-ms-wma&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.wax&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;audio/x-ms-wax&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.ogg&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/ogg&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.wav&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;audio/x-wav&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.gif&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;image/gif&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.jpg&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;image/jpeg&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.jpeg&amp;quot;&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;image/jpeg&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.png&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;image/png&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.xbm&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;image/x-xbitmap&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.xpm&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;image/x-xpixmap&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.xwd&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;image/x-xwindowdump&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.css&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;text/css&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.html&amp;quot;&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;text/html&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.htm&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;text/html&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.js&amp;quot;&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;text/javascript&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.asc&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.c&amp;quot;&lt;/span&gt;            &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.cpp&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.log&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.conf&amp;quot;&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.text&amp;quot;&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.txt&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;text/plain&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.dtd&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;text/xml&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.xml&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;text/xml&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.mpeg&amp;quot;&lt;/span&gt;         &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;video/mpeg&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.mpg&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;video/mpeg&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.mov&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;video/quicktime&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.qt&amp;quot;&lt;/span&gt;           &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;video/quicktime&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.avi&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;video/x-msvideo&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.asf&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;video/x-ms-asf&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.asx&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;video/x-ms-asf&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.wmv&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;video/x-ms-wmv&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.bz2&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/x-bzip&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.tbz&amp;quot;&lt;/span&gt;          &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/x-bzip-compressed-tar&amp;quot;&lt;/span&gt;,
  &lt;span class=&quot;s2&quot;&gt;&amp;quot;.tar.bz2&amp;quot;&lt;/span&gt;      &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt;      &lt;span class=&quot;s2&quot;&gt;&amp;quot;application/x-bzip-compressed-tar&amp;quot;&lt;/span&gt;
 &lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;


server.document-root &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; CWD

fastcgi.server &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;.php&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;localhost&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;
                     &lt;span class=&quot;s2&quot;&gt;&amp;quot;bin-path&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;/usr/local/php5-fcgi/bin/php&amp;quot;&lt;/span&gt;, &lt;span class=&quot;c&quot;&gt;# path to php binary&lt;/span&gt;
                     &lt;span class=&quot;s2&quot;&gt;&amp;quot;socket&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&amp;gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;/tmp/php.socket&amp;quot;&lt;/span&gt;
                 &lt;span class=&quot;o&quot;&gt;)))&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now at the root of your app run:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;lighttpd -D -f config/lighty.conf
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;Boom&lt;/h2&gt;
&lt;p&gt;Now you&amp;#8217;re deving with port 3000. Plus, your app isn&amp;#8217;t constantly available and just sitting there. When you need to work on it, fire it up, work, and shut&amp;#8217;r down.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">BDD’n All Over The Place</title>
      <link href="http://www.jonmaddox.com/2007/11/29/bddn-all-over-the-place/" rel="alternate" type="text/html" />
      <published>2007-11-29T00:00:00-08:00</published>
      <updated>2007-11-29T00:00:00-08:00</updated>
      <id>http://www.jonmaddox.com/2007/11/29/bddn-all-over-the-place</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2007/11/29/bddn-all-over-the-place/">&lt;p&gt;I&amp;#8217;ve been working on quite a range of different projects recently, not all of which are Ruby/Rails.&lt;/p&gt;
&lt;p&gt;One is in &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; and one is a cross platform widget that will work for the Yahoo Widgets engine as well as OS X&amp;#8217;s Dashboard.&lt;/p&gt;
&lt;p&gt;Of these two projects I&amp;#8217;ve managed to work some &lt;span class=&quot;caps&quot;&gt;BDD&lt;/span&gt; into both of them. I thought I&amp;#8217;d spread the word on the packages I&amp;#8217;m using. For javascript I&amp;#8217;m using &lt;a href=&quot;http://code.google.com/p/jsspec/&quot;&gt;JSSpec&lt;/a&gt; and for &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; I&amp;#8217;m using &lt;a href=&quot;http://dev.phpspec.org/manual/&quot;&gt;PHPSpec&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Neither are documented very much, and both are less than 6 months old. They&amp;#8217;re limited, but I&amp;#8217;ve been able to get what I need out of them and its been nice to be able to keep the same dev style even if I have to deal with JS and &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt;.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s a couple pieces of syntax.&lt;/p&gt;
&lt;h2&gt;PHPSpec&lt;/h2&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;php&quot;&gt;&lt;span class=&quot;cp&quot;&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DescribePage&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;PHPSpec_Context&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;before&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;p&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Page&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;itShouldReturnAShortUrl&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;url&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://www.apple.com/imac/features.html&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;short_url&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;())&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;be&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;www.apple.com&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

 &lt;span class=&quot;k&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;itShouldNotBelongToAQueryWhenCreated&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(){&lt;/span&gt;
   &lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;spec&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;p&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;should&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;-&amp;gt;&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;beNull&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;cp&quot;&gt;?&amp;gt;&lt;/span&gt;&lt;span class=&quot;x&quot;&gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;h2&gt;JSSpec&lt;/h2&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;javascript&quot;&gt;&lt;span class=&quot;nx&quot;&gt;describe&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Get Song From XML&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;nx&quot;&gt;before_each&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;podcast&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;Podcast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
    &lt;span class=&quot;nx&quot;&gt;podcast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;xmlUrl&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;podcast_xml_fixture.xml&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
    &lt;span class=&quot;s1&quot;&gt;&amp;#39;should parse song title from xml&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;topTune&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;readFeed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;song&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;podcast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parseTune&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
      &lt;span class=&quot;nx&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;song&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;title&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;should_be&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;Willoughby - Frankenstein&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&amp;#39;should parse song url from xml&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;podcast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;readFeed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;song&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nx&quot;&gt;podcast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;parseSong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;song&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;][&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;link&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;].&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;strip&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;should_be&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;http://www.domain.com/featured/willoughby_-_franken&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
  &lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;
      &lt;span class=&quot;s1&quot;&gt;&amp;#39;should create song&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;kd&quot;&gt;function&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;podcast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;getSong&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
        &lt;span class=&quot;nx&quot;&gt;expect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;podcast&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;song&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;nx&quot;&gt;should_not_be&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;</content>
    </entry>
  
    <entry>
      <title type="html">Display Validation Errors For Your Ajaxified Form</title>
      <link href="http://www.jonmaddox.com/2007/11/05/display-validation-errors-for-your-ajaxified-form/" rel="alternate" type="text/html" />
      <published>2007-11-05T00:00:00-08:00</published>
      <updated>2007-11-05T00:00:00-08:00</updated>
      <id>http://www.jonmaddox.com/2007/11/05/display-validation-errors-for-your-ajaxified-form</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2007/11/05/display-validation-errors-for-your-ajaxified-form/">&lt;p&gt;One of the sweet things Rails provides is practically free error notification on forms. With just a little bit of&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error_messages_for&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:post&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;validations on your model, and some logic in your controller, bam, instant error messaging. Very simple.&lt;/p&gt;
&lt;p&gt;But what if your form is using &lt;span class=&quot;caps&quot;&gt;AJAX&lt;/span&gt; to post? The page doesn&amp;#8217;t re-render, so how is our buddy, &lt;code&gt;error_messages_for&lt;/code&gt;, going to work? Well its not, we&amp;#8217;re gonna have to use &lt;span class=&quot;caps&quot;&gt;AJAX&lt;/span&gt; to display the errors. It turns out this isn&amp;#8217;t too touch, but as usual we want to keep it &lt;span class=&quot;caps&quot;&gt;DRY&lt;/span&gt;.&lt;/p&gt;
&lt;h2&gt;The Controller&lt;/h2&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;PostsController&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ApplicationController&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;create&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@post&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;params&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;ss&quot;&gt;:post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;vi&quot;&gt;@post&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;save!&lt;/span&gt;

    &lt;span class=&quot;c1&quot;&gt;# automatically renders create.rjs.erb&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;rescue&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;RecordInvalid&lt;/span&gt;

    &lt;span class=&quot;vi&quot;&gt;@model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@kit_item&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;render&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:template&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;shared/validation_error.rjs.erb&amp;#39;&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We will attempt to create the Post. If all goes well, then the &lt;em&gt;create&lt;/em&gt; rjs action will fire and all is well. But if the record fails validation, we catch it, assign the current model to &lt;code&gt;@model&lt;/code&gt;, and render &lt;em&gt;shared/validaiton_error.rjs.erb&lt;/em&gt;. This is our generic &lt;span class=&quot;caps&quot;&gt;AJAX&lt;/span&gt; action for rendering errors for Models.&lt;/p&gt;
&lt;h2&gt;The &lt;span class=&quot;caps&quot;&gt;AJAX&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Create &lt;em&gt;shared/validation_error.rjs&lt;/em&gt; and use this little snippit, and thats pretty much it for this part.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;replace_html&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;errors_for_&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;underscore&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;Oops! &amp;lt;ul&amp;gt;&amp;quot;&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;errors&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;lt;li&amp;gt;The &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;k&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;lt;/li&amp;gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;to_s&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;lt;/ul&amp;gt;&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;page&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;visual_effect&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:highlight&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;errors_for_&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;vi&quot;&gt;@model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;underscore&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:startcolor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;#39;#ff0000&amp;#39;&amp;quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:endcolor&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;&amp;#39;#aca2b6&amp;#39;&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;So, the errors just get collected and printed out to a &lt;code&gt;div&lt;/code&gt; in the page. Let&amp;#8217;s go add this fancy &lt;code&gt;div&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;The View&lt;/h2&gt;
&lt;p&gt;This part is pretty simple. You just need to open up your form partial, &lt;em&gt;posts/_form.html.erb&lt;/em&gt; and add this little view helper to the form:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;o&quot;&gt;&amp;lt;%=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;error_div_for&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;post&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;%&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This does nothing but prints out a &lt;code&gt;div&lt;/code&gt; with a specific ID to prepare for our &lt;span class=&quot;caps&quot;&gt;RJS&lt;/span&gt; action, if it&amp;#8217;s needed. So let&amp;#8217;s go make the view helper.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;module&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;ApplicationHelper&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;error_div_for&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;sx&quot;&gt;%{&amp;lt;div id=&amp;quot;errors_for_&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;underscore&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;sx&quot;&gt;&amp;quot;&amp;gt;&amp;lt;/div&amp;gt;}&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now we can just call this simple view helper and pass in any model and it will dynamically build a div with the appropriate ID inside.&lt;/p&gt;
&lt;p&gt;And thats pretty much it. To go through it again, when the form is loaded, the error div will be placed in the page, waiting to be used. Once the form is submitted in the background, the controller&amp;#8217;s create action will attempt to save it. If the record is invalid with validation errors, then the universal validation_errror &lt;span class=&quot;caps&quot;&gt;RJS&lt;/span&gt; template will be fired off and replace all the errors from the failed validation, into the div we placed in the page. Oh and of course a little red flash action to make sure the user sees the errors.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">Flush your DNS Cache in Leopard</title>
      <link href="http://www.jonmaddox.com/2007/10/29/flush-your-dns-cache-in-leopard/" rel="alternate" type="text/html" />
      <published>2007-10-29T00:00:00-07:00</published>
      <updated>2007-10-29T00:00:00-07:00</updated>
      <id>http://www.jonmaddox.com/2007/10/29/flush-your-dns-cache-in-leopard</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2007/10/29/flush-your-dns-cache-in-leopard/">&lt;p&gt;As a web dev, you may spend some time messing with &lt;span class=&quot;caps&quot;&gt;DNS&lt;/span&gt; and overriding it. Though, it can get annoying when you can&amp;#8217;t get things back to the way that mere mortals see things on the internet.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;lookupd -flushcache&lt;/code&gt; was a big tool in my arsenal in Tiger. But Leopard put a rightful end to &lt;code&gt;lookupd&lt;/code&gt;, but left us without our known way of flushing the cache.&lt;/p&gt;
&lt;p&gt;For the last couple of weeks its been pretty annoying, but I&amp;#8217;m here to bring you, the &lt;strong&gt;&lt;span class=&quot;caps&quot;&gt;NEW&lt;/span&gt;&lt;/strong&gt; way to flush dns cache for OS X users!!! Holy crap this is exciting.&lt;/p&gt;
&lt;p&gt;So it&amp;#8217;s as simple as this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;dscacheutil -flushcache
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;There you have it.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">Start and Stop MySql in Mac Os X 10.5 Leopard</title>
      <link href="http://www.jonmaddox.com/2007/10/27/start-and-stop-mysql-in-mac-os-x-105-leopard/" rel="alternate" type="text/html" />
      <published>2007-10-27T00:00:00-07:00</published>
      <updated>2007-10-27T00:00:00-07:00</updated>
      <id>http://www.jonmaddox.com/2007/10/27/start-and-stop-mysql-in-mac-os-x-105-leopard</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2007/10/27/start-and-stop-mysql-in-mac-os-x-105-leopard/">&lt;p&gt;Yay! Its finally here! The newest version of my favorite operating system. And it comes with 98% of my dev stack &lt;span class=&quot;caps&quot;&gt;ALREADY&lt;/span&gt; installed! Yay! This is perfect.&lt;/p&gt;
&lt;p&gt;Wait&amp;#8230; ok, no MySql. Thats ok, I&amp;#8217;ll just install it&amp;#8230;Wait&amp;#8230; why isn&amp;#8217;t it running&amp;#8230;why isn&amp;#8217;t this stupid preference panel turning it on! &lt;span class=&quot;caps&quot;&gt;ARGHHH&lt;/span&gt;!&lt;/p&gt;
&lt;p&gt;Ok, that may have just happened to you, and you dramatically hit the Google looking for answers. So answers I give you.&lt;/p&gt;
&lt;p&gt;If you want to start MySql in Leopard, you need to call the startup item directly from the command line:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;sudo /Library/StartupItems/MySQLCOM/MySQLCOM start
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And to stop it:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;sudo /Library/StartupItems/MySQLCOM/MySQLCOM stop
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Not so bad. But long&amp;#8230; So make an alias in your &lt;em&gt;.bash_profile&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;start_mysql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;/Library/StartupItems/MySQLCOM/MySQLCOM start&amp;quot;&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;alias &lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;stop_mysql&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;/Library/StartupItems/MySQLCOM/MySQLCOM stop&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Happier? Relaxed? Good.&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">Use Capistrano to disable_web On Your PHP Apps</title>
      <link href="http://www.jonmaddox.com/2007/03/09/use-capistrano-to-disable_web-on-your-php-apps/" rel="alternate" type="text/html" />
      <published>2007-03-09T00:00:00-08:00</published>
      <updated>2007-03-09T00:00:00-08:00</updated>
      <id>http://www.jonmaddox.com/2007/03/09/use-capistrano-to-disable_web-on-your-php-apps</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2007/03/09/use-capistrano-to-disable_web-on-your-php-apps/">&lt;p&gt;Just like using Capistrano to deploy my Rails apps made my &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; apps jealous, being able to disable the entire app with a single command did too.&lt;/p&gt;
&lt;p&gt;Using a simple &lt;em&gt;cap disable_web&lt;/em&gt; command allows my Rails apps to be completely disabled while I perform maintenance on the site without having to worry about users accessing it. Being able to suspend the site with a single command is &lt;strong&gt;wonderful&lt;/strong&gt;, trust me.&lt;/p&gt;
&lt;p&gt;Well I want to do this with my &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; apps too. Here&amp;#8217;s how I did it.&lt;/p&gt;
&lt;p&gt;First create a generic page that you want your users to see when your site is disabled and name it &lt;em&gt;maintenance.html&lt;/em&gt;. Then throw it into your &lt;em&gt;/config&lt;/em&gt; directory.&lt;/p&gt;
&lt;h2&gt;Cap Tasks&lt;/h2&gt;
&lt;p&gt;Next, write a &lt;em&gt;disable_web&lt;/em&gt; task for Capistrano.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;This will disable the application and show a warning screen&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:disable_web&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;cp &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/config/maintenance.html &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/maintenance.html&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This simple one liner will copy the maintenance.html file into the root of your application. Now lets write a task to re-enable the application.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;This will enable the application and remove the warning screen&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:enable_web&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;rm &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/maintenance.html&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This will simply delete the maintenance file.&lt;/p&gt;
&lt;p&gt;Ok, so, basically, when you run &lt;em&gt;cap disable_web&lt;/em&gt;, Capistrano will move your maintenance file into the root of your app, and &lt;em&gt;enable_web&lt;/em&gt; will delete it. So far this does nothing to disable your &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; application.&lt;/p&gt;
&lt;h2&gt;mod_rewrite Magic&lt;/h2&gt;
&lt;p&gt;The trick to this is using mod_rewrite to look for your maintenance file. If it&amp;#8217;s at the root of your application, then all requests will be directed to it. Otherwise the application works as normal.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;RewriteEngine On

&lt;span class=&quot;c&quot;&gt;# Check for maintenance file and redirect all requests&lt;/span&gt;
RewriteCond %&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;DOCUMENT_ROOT&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;/maintenance.html -f
RewriteCond %&lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;SCRIPT_FILENAME&lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; !maintenance.html
RewriteRule ^.*&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;html|&lt;span class=&quot;se&quot;&gt;\.&lt;/span&gt;php&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;nv&quot;&gt;$ &lt;/span&gt;/maintenance.html &lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;L&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This will look for any requests of .html and .php files. All requests that fit that condition will pointed to the maintenance.html page if it exists at the root of the application.&lt;/p&gt;
&lt;p&gt;Huzzah! You&amp;#8217;ve now got a &amp;#8220;we&amp;#8217;re fixin&amp;#8217; stuff&amp;#8221; page. &lt;strong&gt;And you can go from this:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://img.skitch.com/20100102-8iefy1eautujt11jc9c9frfcb8.jpg&quot; class=&quot;centered&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;To this:&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;http://img.skitch.com/20100102-mw61w28djwhrrxn96qj8ais24e.jpg&quot; class=&quot;centered&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">Associated and Non Associated Resources</title>
      <link href="http://www.jonmaddox.com/2007/01/25/associated-and-non-associated-resources/" rel="alternate" type="text/html" />
      <published>2007-01-25T00:00:00-08:00</published>
      <updated>2007-01-25T00:00:00-08:00</updated>
      <id>http://www.jonmaddox.com/2007/01/25/associated-and-non-associated-resources</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2007/01/25/associated-and-non-associated-resources/">&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Your site has resources that can exist alone as well as be associated to another resource. You want to stay &lt;span class=&quot;caps&quot;&gt;DRY&lt;/span&gt; as well as keep your routing RESTful.&lt;/p&gt;
&lt;p&gt;I just came across this problem while working on a project, and its solution is a bit easier than you&amp;#8217;d think. Thanks to Ruby being so dynamic, Rails provides a really easy way to accomplish this. I&amp;#8217;ll give my use case as an example.&lt;/p&gt;
&lt;p&gt;So I&amp;#8217;ve got a website I&amp;#8217;m working on. It&amp;#8217;s for a church. Part of the data model will include Events and Groups. Groups will &lt;em&gt;have_many&lt;/em&gt; Events. Simple enough. Here&amp;#8217;s the wrench, Events can exist on their own as well. So the &amp;#8220;Church&amp;#8221; can have Events, which would be an Event existing alone without an association to anything, and every Group in the church will &lt;em&gt;have_many&lt;/em&gt; Events.&lt;/p&gt;
&lt;p&gt;We also want to use RESTful routes so that we can have routes like: /events, /events/1, /groups/1/events, /groups/4/events/32&lt;/p&gt;
&lt;p&gt;Ok, so lets get jammin&amp;#8217; on the solution.&lt;/p&gt;
&lt;p&gt;Lets knock out the data model first. We want to keep this &lt;span class=&quot;caps&quot;&gt;DRY&lt;/span&gt;, so we&amp;#8217;ll use Single Table Inheritance to keep track of the Events. We&amp;#8217;ll have Events and GroupEvents. GroupEvents will inherit from the parent Events:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;no&quot;&gt;Class&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;GroupEvent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Event&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;This way Events.find(:all) will return regular Events as well as GroupEvents. We&amp;#8217;ll add one extra attribute for GroupEvent, group_id, this allows a GroupEvent to &lt;em&gt;belong_to&lt;/em&gt; a Group. Lets make our migrations:&lt;/p&gt;
&lt;h2&gt;Data Tables&lt;/h2&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateGroups&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;up&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:groups&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;down&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;drop_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:groups&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;


&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;CreateEvents&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Migration&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;up&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;create_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:events&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:title&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:string&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:description&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:text&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:date_on_&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:datetime&lt;/span&gt;

      &lt;span class=&quot;c1&quot;&gt;# extra attribute for GroupEvent&lt;/span&gt;
      &lt;span class=&quot;n&quot;&gt;t&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;column&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:group_id&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:integer&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

  &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nf&quot;&gt;down&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;drop_table&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:events&lt;/span&gt;
  &lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you&amp;#8217;re unfamiliar with &lt;span class=&quot;caps&quot;&gt;STI&lt;/span&gt;, the type column is there to keep track of what type of object the row is. We also added the group_id attribute for the GroupEvent.&lt;/p&gt;
&lt;p&gt;Ok, so our DB is set up. Lets get our association on&amp;#8230; Start by creating a new model named GroupEvent:&lt;/p&gt;
&lt;h2&gt;Data Model&lt;/h2&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;GroupEvent&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Event&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;belongs_to&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:group&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;GroupEvent inherits from Event. Since Event inherits from ActiveRecord::Base, our GroupEvent class is still an ActiveRecord. Its just extended a bit. We also added a &lt;em&gt;belongs_to&lt;/em&gt; association to Group. So Group will &lt;em&gt;have_many&lt;/em&gt; GroupEvents, not Events. Next up, the Group model&amp;#8230;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Group&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;ActiveRecord&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;no&quot;&gt;Base&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;has_many&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:events&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:class_name&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;GroupEvent&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:foreign_key&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;group_id&amp;#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Here&amp;#8217;s where we&amp;#8217;re pulling a few Rails tricks. We&amp;#8217;re using telling Group that it has many events, but GroupEvents, using group_id as a foreign key (remember when we added that?!). So now we&amp;#8217;re able to use:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;vi&quot;&gt;@group&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;Group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;find&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;vi&quot;&gt;@events&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;events&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Whoa, nice! We didn&amp;#8217;t have to have a separate table just for GroupEvents, which means we dont have to manage 2 different models and their attributes if they get updated. We also don&amp;#8217;t have to use syntax like &lt;em&gt;@group.group_events&lt;/em&gt; which is a bit ugly if you ask me. Groups have events, lets obfuscate &lt;span class=&quot;caps&quot;&gt;HOW&lt;/span&gt; it has them to the back, so in the implementation we&amp;#8217;re just worried about events, not namespaced events, blegh.&lt;/p&gt;
&lt;p&gt;OK! &lt;span class=&quot;caps&quot;&gt;NICE&lt;/span&gt;! So far so good. What about the routes? I want http://mychurch.com/events and http://mychurch.com/groups/2/events, not http://mychurch.com/groups/3/group_events.&lt;/p&gt;
&lt;p&gt;Lets dig into the routing&amp;#8230;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:events&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:groups&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;|&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;resources&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:events&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:controller&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;group_events&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:name_prefix&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;group_&amp;#39;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;First things first, we add a resource for Event. Next, since we want nice pretty nested routing, we nest GroupEvent into Group. Here&amp;#8217;s some more configuration (&lt;span class=&quot;caps&quot;&gt;EEP&lt;/span&gt;! yes yes, our requirements are outside of a convention, &lt;span class=&quot;caps&quot;&gt;BUT&lt;/span&gt; &lt;span class=&quot;caps&quot;&gt;NOT&lt;/span&gt; BY &lt;span class=&quot;caps&quot;&gt;MUCH&lt;/span&gt;!). We want to define the route to use &amp;#8216;events&amp;#8217; in the actual &lt;span class=&quot;caps&quot;&gt;URI&lt;/span&gt;, but we don&amp;#8217;t want to stomp all over our previously declared resource, events. Our generated named routes will clash, we can&amp;#8217;t have 2 events_path methods. So we add a :name_prefix option. This basically appends &amp;#8216;group_&amp;#8217; to every little route helper we have. So event_path in the Group context becomes, group_event_path. The tricky one is new_event_path turns to group_new_event_path, so its not AS pretty, but good enough :)&lt;/p&gt;
&lt;p&gt;Next we know our @group.events are going to be very different from our @events, so we need a GroupEventController. In order to properly map this, we use the :controller option. Without it, the routing will use EventController (those darn conventions&amp;#8230;), and that would just process our logic for normal Events, not our scoped Group events.&lt;/p&gt;
&lt;p&gt;So there ya have it. Around 10 lines of code and we have our domain model nice and dry, and our routes very logical!&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">Test Your respond_to Block</title>
      <link href="http://www.jonmaddox.com/2006/11/30/test-your-respond_to-block/" rel="alternate" type="text/html" />
      <published>2006-11-30T00:00:00-08:00</published>
      <updated>2006-11-30T00:00:00-08:00</updated>
      <id>http://www.jonmaddox.com/2006/11/30/test-your-respond_to-block</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2006/11/30/test-your-respond_to-block/">&lt;p&gt;Getting RESTful and wondering how to test your &lt;em&gt;respond_to&lt;/em&gt; block in your controller to make sure its returning the right format? Drop these into your &lt;em&gt;test_helper.rb&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assert_xml&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;assert_match&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;application/xml&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;assert_rss&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;assert_match&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;application/rss+xml&amp;#39;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;vi&quot;&gt;@response&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;headers&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s1&quot;&gt;&amp;#39;Content-Type&amp;#39;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Then in your functional tests, use them like this:&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_return_xml&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;xml&amp;#39;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;assert_xml&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;test_return_rss&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:index&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:format&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&amp;gt;&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&amp;#39;rss&amp;#39;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;assert_rss&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Yay!&lt;/p&gt;</content>
    </entry>
  
    <entry>
      <title type="html">Automated PHP Deployment With Capistrano</title>
      <link href="http://www.jonmaddox.com/2006/08/16/automated-php-deployment-with-capistrano/" rel="alternate" type="text/html" />
      <published>2006-08-16T00:00:00-07:00</published>
      <updated>2006-08-16T00:00:00-07:00</updated>
      <id>http://www.jonmaddox.com/2006/08/16/automated-php-deployment-with-capistrano</id>
      <content type="html" xml:base="http://www.jonmaddox.com/2006/08/16/automated-php-deployment-with-capistrano/">&lt;p&gt;Sure, I&amp;#8217;d like to spend my days and nights writing Ruby and &lt;em&gt;cap deploy&lt;/em&gt; ing it to success, but I have to face the facts.&lt;/p&gt;
&lt;h2&gt;I Still Have &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; Apps To Maintain.&lt;/h2&gt;
&lt;p&gt;I have no one to blame but myself. I wrote them. And I still work where they were written. There&amp;#8217;s no escape. After using &lt;a href=&quot;http://manuals.rubyonrails.com/read/book/17&quot;&gt;Capistrano&lt;/a&gt; more than one time, you&amp;#8217;ll be completely spoiled.&lt;/p&gt;
&lt;p&gt;But Capistrano is only for crazy Rails apps right? Nope. Its simply a platform for automation. It just so happens that deployment usually requires quite a bit of steps. Hmmm&amp;#8230;lots of steps&amp;#8230;automation&amp;#8230; a match made in heaven. So sure, Capistrano was thought up with Rails in mind, but created openly so you&amp;#8217;re able to use it any way you&amp;#8217;d like.&lt;/p&gt;
&lt;p&gt;Capistrano&amp;#8217;s power is in its remoting features. You tell it what to do on that &lt;em&gt;other server&lt;/em&gt;, and it does it. Beauty. So, if your &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; deployment process requires an &lt;em&gt;svn export&lt;/em&gt; combined with a few other repetitive steps you have to do &lt;strong&gt;&lt;em&gt;every&lt;/em&gt;&lt;/strong&gt; time, Capistrano is here to help you too.&lt;/p&gt;
&lt;p&gt;Without going into all the details of &lt;a href=&quot;http://manuals.rubyonrails.com/read/book/17&quot;&gt;Capistrano&lt;/a&gt; (following the link will give you a nice overview), it uses a deploy script (recipe) to understand your deployment environment and required tasks.&lt;/p&gt;
&lt;h2&gt;Ok ok ok, Lets Get On With It&lt;/h2&gt;
&lt;p&gt;So, we have a &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; app that we need to deploy to server X. It needs to be sucked out of its repository, its version symlinked to a doc_root, and then a shared directory symlinked into the site. This is a pretty common deployment scenario, so lets go with this.&lt;/p&gt;
&lt;p&gt;First, we need a Capistrano recipe. Currently, it can only be auto generated for a Rails app, so psst, &lt;a href=&quot;http://maddox.s3.amazonaws.com/sites/deploy.rb&quot;&gt;here it is&lt;/a&gt;. Download this and create a directory at the root of your &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; app and name it &lt;em&gt;config&lt;/em&gt;.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;mkdir config
&lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;config
wget http://maddox.s3.amazonaws.com/sites/deploy.rb
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Capistrano wants its recipe to be in the &lt;em&gt;config&lt;/em&gt; directory, so do as it likes. Now lets open it up and make it work with us. Once again, a complete description of the Capistrano recipe file is bit out of scope for this little post, so please &lt;a href=&quot;http://manuals.rubyonrails.com/read/book/17&quot;&gt;read up&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;We&amp;#8217;ll edit all the normal items&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:application&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;php_app&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:repository&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;http://code.phpdevelopers.com/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/trunk&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;We only deploy to one server, so edit&amp;#8230;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;role&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:web&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;phpsite.com&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Now you need to add the directory you want to deploy to. Add&amp;#8230;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:deploy_to&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;/home/www/&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;application&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Capistrano defaults to &lt;em&gt;checkout&lt;/em&gt; when pulling from subversion. I prefer an export, if you do too, then add&amp;#8230;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;set&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:checkout&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;export&amp;quot;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Ok, so if you&amp;#8217;re familiar with Capistrano, you know exactly what we did. If you&amp;#8217;re not, the most important thing to know is that Capistrano is going to suck your code from the repository defined, do everything it&amp;#8217;s told to do in &lt;em&gt;/home/www/php_app&lt;/em&gt; on your deployment server, phpsite.com.&lt;/p&gt;
&lt;h2&gt;Set&amp;#8217;r Up&lt;/h2&gt;
&lt;p&gt;Believe it or not, we&amp;#8217;re half way done. Next, lets let Capistrano get the directory we just defined ready for its magic.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;cap setup
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;If you look on your deployment server, you should see 2 directories that were just created.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;drwxrwxr-x    2 user  group      4096 Aug 16 01:48 releases
drwxr-sr-x    4 user  group      4096 Aug 16 01:48 shared
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;&lt;span class=&quot;caps&quot;&gt;WOW&lt;/span&gt;! We&amp;#8217;re already saving time! &lt;em&gt;releases&lt;/em&gt; is where your app will be sucked down to. Capistrano exports your &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; app into a directory named after the current date/time. Each version you deploy will be kept in this directory. Shared is where you can keep files like uploaded assets or images that aren&amp;#8217;t kept in your repository, but need to be around every time you export from subversion. If your users upload files all day long, and you redeploy your &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; app from subversion, all your uploaded files will be gone. So we keep those in here.&lt;/p&gt;
&lt;p&gt;Here&amp;#8217;s the deployment rub. Every time you deploy, you have to go symlink these directories from your /shared directory, into your current version of your &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; app. And a small bit of math can calculate the complexity when you start adding 2,3, or 6 asset folders. Yuck.&lt;/p&gt;
&lt;p&gt;That&amp;#8217;s where Capistrano helps ya out.&lt;/p&gt;
&lt;h2&gt;Capistrano, Take Me Away&lt;/h2&gt;
&lt;p&gt;Ok, so our deploy directory is set up and ready to get our files. Now we need to tell Capistrano exactly what to do when we deploy. Get it? Recipe&amp;#8230;&lt;/p&gt;
&lt;p&gt;Define a new task called &lt;em&gt;deploy&lt;/em&gt;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;This will deploy the app&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:deploy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Inside the block we&amp;#8217;ll just tell Capistrano exactly what it needs to do in order to deploy our app.&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;ruby&quot;&gt;&lt;span class=&quot;n&quot;&gt;desc&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;This will deploy the app&amp;quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;task&lt;/span&gt; &lt;span class=&quot;ss&quot;&gt;:deploy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;svn --quiet &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;checkout&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;repository&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;release_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;ln -nfs &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;release_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt; &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&amp;quot;&lt;/span&gt;
  &lt;span class=&quot;n&quot;&gt;run&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&amp;quot;ln -nfs &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;shared_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/photos &lt;/span&gt;&lt;span class=&quot;si&quot;&gt;#{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;current_path&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;/photos&amp;quot;&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Ok, lets walk through exactly what this does. First, Capistrano uses subversion to suck your app out of the repository and into the &lt;em&gt;releases&lt;/em&gt; directory. That &lt;em&gt;releases_path&lt;/em&gt; is just a nice convenient variable for &lt;em&gt;/home/www/php_site/releases/200608162012&lt;/em&gt;. Next, it will create a &lt;em&gt;current&lt;/em&gt; symlink to that version of your app in the &lt;em&gt;releases&lt;/em&gt; directory. Then we told it to symlink the directory &lt;em&gt;photos&lt;/em&gt; from &lt;em&gt;shared&lt;/em&gt; into the current version of the application.&lt;/p&gt;
&lt;p&gt;And since you&amp;#8217;re hosting a &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; app with Apache (a 99% guess), there&amp;#8217;s no need to restart any servers. You&amp;#8217;re done. You&amp;#8217;re new version will be deployed safely and linked. The &lt;strong&gt;&lt;span class=&quot;caps&quot;&gt;IMPORTANT&lt;/span&gt;&lt;/strong&gt; thing to note, is that your new &lt;em&gt;doc_root&lt;/em&gt; for your site will be &lt;em&gt;/home/www/php_app/current&lt;/em&gt;.&lt;/p&gt;
&lt;h2&gt;Deploy!&lt;/h2&gt;
&lt;p&gt;Now, all ya gotta do to redeploy your site is&amp;#8230;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;cap deploy
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;Capistrano will go on to do everything you told it to do, on your deployment server. When its done, your directory should look like this&amp;#8230;&lt;/p&gt;
&lt;div class=&quot;highlight&quot;&gt;&lt;pre&gt;&lt;code class=&quot;bash&quot;&gt;lrwxrwxrwx   1 user group   42 2006-08-16 01:32 current -&amp;gt; /home/www/php_site_/releases/200608162012
drwxrwxr-x   4 user group 4096 2006-08-16 01:31 releases
drwxr-xr-x   4 user group 4096 2006-06-24 02:08 shared
&lt;/code&gt;&lt;/pre&gt;
&lt;/div&gt;
&lt;p&gt;And there you have it. Automated &lt;span class=&quot;caps&quot;&gt;PHP&lt;/span&gt; deployment, with one simple command. Peering inside the current directory, you&amp;#8217;ll see where the photos directory has been symlinked into it.&lt;/p&gt;
&lt;p&gt;The moral is, Capistrano isn&amp;#8217;t just for Rails deployment. Its powerful remote execution can be exploited to do anything you want. While our Rails apps get a lot for free when it comes to recipes, creating your own isn&amp;#8217;t complicated. Especially if its replacing repetitive tasks you&amp;#8217;ve done over and over and over and over and over again.&lt;/p&gt;
&lt;p&gt;You&amp;#8217;ll see the payoff the minute you get this set up. Especially on a Friday night at 5:30p when you&amp;#8217;re walking out the door. And its not just for Rails!&lt;/p&gt;</content>
    </entry>
  
</feed>

