21 April 2008

Movable Type 4 Setup on Ubuntu

I am in the process of consolidating my blogs to a single system, and in doing so, am switching to Movable Type. This blog will eventually move, but the initial impetus was for some other blogs that were running on WordPress where I was having some problems (and spent way too much time trying to fix that - one of the key things I don't want to be doing with my blog system). But, in relation, I found the setup instructions for Movable Type a bit lacking, especially in relation to using multiple blogs, and how this works in terms of Apache and MT setup.

I also want to say a bit thanks to Duncan for showing me his Apache config, and some brief discussion of multiple blogs with MT. The MT docs were really lacking here as said, but Duncan's knowledge made it clear this was pretty easy and a nice way to go. Thanks Duncan, and check out his blog and site, and of course all his great pics on Flickr.

I am using a single installation of Movable Type, supporting multiple blogs, with each blog having their own domain name, and that blog (or really the MT content) living at the root of that domain name (this last part isn't required/essentially for this tutorial, you can easily tweak the Apache config).

So, with that, given that it actually turns out to be relatively simple to set up once you know a few key bits, I figured I'd pass along what I did...

First, I created my standard Ubuntu slice at Slicehost. I host everything with them these days, and as such, I have a base system image that I've built for myself. It's built with their standard Ubuntu 7.10 choice, and then I make tweaks to the SSH setup, add a few bits I need, etc. But, I believe you could simply take pretty much a standard Ubuntu server install and use that. Please let me know if this guide doesn't work for you in that case.

Preparations



In preparation for the move, while my existing blogs were running, I exported their data from WordPress using WordPress' Export feature, which produces a WXR file. Save that to my local machine.

I also made sure I had my domain names secured, and the DNS for them setup. In particular, one thing to note is that Movable Type is sort of a two part system. You have the MT web application, which is a publishing application, but is not what someone hits when visiting your blog(s). MT publishes your blog out as static content (or that's the default option anyway). So, I setup an "mt" subdomain on one of my domains where I will access MT (more on that below).

Apache and Perl Install



Apache 2 and mod_perl were not on my system by default, so I needed to install those. This amounts to:
sudo apt-get install apache2 libapache2-mod-perl2 libapache2-mod-perl2-doc

The above will not only install it, but configure mod_perl for use in Apache, and you can now run Perl based web apps (MT is developed in Perl). Also, depending on how you want to do email, you may need to install Perl's Mail::Sendmail (if using SMTP; if you use sendmail, then you can choose that when setting up MT):

sudo perl -MCPAN -e "install Mail::Sendmail"



Create a Database



I created a database for MT using MySQL. I also setup a specific MySQL user, password, and assigned them rights to that database. You'll need this info later when setting up/configuring MT.

I have been using Navicat for nearly all my DB management. It works really well given it can do SSH tunneling as I don't open the MySQL port on my servers, etc. It is a commercial app, but as a developer who works with DB's often, has proven to be my tool of choice (I've tried many others, and this is the one that's worked best for me).

Apache Configuration



I have a relatively minimal Apache configuration file. The bulk of it is done with a file that sets up my virtual hosts (several domains point to a single machine). The setup for MT has a few critical pieces:

  • Setting a ScriptAlias for the "mt" directory, so mod_perl knows to execute .cgi files there.

  • Setting an Alias for the "mt-static" directory, which is MT's static content, and which you'll want to be referenced from all your blogs. You can also do this with a symlink, but I've done it with an Apache alias below so that I don't have to worry about a given blog's static content getting wiped out and breaking this (if the static content gets wiped out, you can just republish in MT to restore it, so I prefer to keep that purely maintained by MT).

  • Setup the proper options/settings for the MT directory.



Thus, my configuration for my virtual hosts looks like the following (fake domain names used), notes following:


NameVirtualHost *
<VirtualHost *>
ServerAdmin chris@example.com
ServerName mt.example.net
DocumentRoot /var/www/mt

Alias /mt-static /var/www/mt/mt-static
<Directory /var/www/mt/mt-static>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
</Directory>

ScriptAlias / /var/www/mt/
<Directory /var/www/mt>
AllowOverride None
Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch
Order allow,deny
Allow from all
</Directory>
</VirtualHost>

<VirtualHost *>
ServerAdmin chris@example.com
ServerName example.com
ServerAlias example.com www. example.com
DocumentRoot /var/www/example

Alias /mt-static /var/www/mt/mt-static
<Directory /var/www/mt/mt-static>
Options Indexes FollowSymLinks MultiViews
AllowOverride All
Order allow,deny
allow from all
</Directory>
Redirect /mt.cgi http://mt.example.net/mt.cgi
</VirtualHost>


The first VirtualHost block sets up where I'll access MT, thus at http://mt.example.com. There you will see both the Alias for mt-static, and also the ScriptAlias for mt. These are critical.

The second Virtual Host block defines one of the actual blogs, which will be accessible at http://example.com or http://www.example.com. For additional blogs, you would add another of these blocks per domain name. The key bits here are the DocumentRoot and the Alias for mt-static. The DocumentRoot is where you will have MT publish your static blog content. Make sure that directory is writable by Apache/MT.

Finally, the Redirect sets things up so that when you are logged in and visit your blog, and see the various links for "Edit this content" that those will actually work (they point to mt.cgi, so this redirects them to whatever domain is serving mt).

Setup Movable Type



Next, visit http://mt.example.com/mt.cgi in your browser to begin setting up and configurating Movable Type. It will ask you about your database, and a few other bits, and then prompt you to create the first blog. Make sure the blog URL and directory match what you setup in your Apache configuration above. Beyond this you will need to refer to Movable Type docs for questions. But, you should essentially have a blog running, and will just need to Publish it to have MT write the static files into the directory you've defined.

The last step for me was to import my blog content from my prior WordPress setup, using the WXR dump I created at the beginning of all this. One key note here. When you go into MT's Import page - choose the blog you want to import to (even if you only have one) first. Only then when you pick WXR (if appropriate) for your import will it give you the proper fields for the info it needs for the WXR import. Otherwise it'll set your import type back to MovableType, and claim it has read in your import file fine (but you'll of course have no content).

I'm still tweaking my templates and doing a few bits to the sites I'm moving over, so I'm off to continue on that. Hope this helps someone.

16 April 2008

Working at Home, The Zone, and Importance of Equipment

Recently, there was a good writeup at Hivelogic on Offices and the Creativity Zone. This is partially also in response to the Jason Calcanas' post on how to save money, 37 Signals response, and so on. I'm getting around to my thoughts/response, as someone who has been working at home for about 60% of the last 10 years.

I'd like to comment on/respond to a few things, in particular:


  • chairs and desks

  • pair programming

  • "The Creativity Zone", as well as working in coffee shops

  • what I think is important for a home office - and in working at home

  • passion for your work, and the relation of that and hours put in at startups



Chairs and Desks



First, chairs and desks. As most folks will say, do NOT skimp on a chair. Go straight to a Herman Miller Aeron, or a Human Scale chair, do not pass go. I'll wait. I've had my posterior in an Aeron since I started at Adobe (thank you Adobe!) in 1996. When I moved, and was no longer working in the office, I used their program that allowed employees to buy these chairs at a discount, and picked one up for $500. I'd have gladly paid full price. Additionally, make sure you get the proper size, it makes a huge difference!

Following on that, I completely disagree on getting cheap desks, or doing the door/board on top of a file cabinet approach mentioned in Calcanas' post. I don't think you need to spend a lot on a desk, afterall, you just need a good surface. However, the key here is getting a desk that is the proper height. If you do the file cabinets thing, or buy your average stock desk they are almost always too tall. Take it from me, I'm 6'2" tall, and these desks are still too tall if you properly set your chair height (thighs level, feet flat on ground, forearms level or close to it, etc.). So, I suggest finding adjustable height desks, or if you are building your own, to make sure you figure out the proper height. I've been using Anthro's AnthroBench desks, which are not cheap, but are kick ass. However, the height adjustment is non-trivial, so you mostly have to get it right the first time. I've seen some since, that consumers can buy, that have more of an infinite height adjustment (which is what we had with the desks at Adobe, but I was unable to buy those).

Pair Programming



I'll cut to the chase: I'm not into it. I know folks who are and swear by it (e.g. Pivotal Labs does it the most and best I've ever seen). But, it's not for me. It doesn't fit with the way I think and work. I like a personalized environment, I like things quiet, and I like a bit more free flow. I also don't feel that it is a guarantee of better code quality.

Some of the complications to me are all the personalization developers like to do, whether that be fonts, keyboards, screen arrangements, colors, coding styles, and so on. Some of that can be worked around, but I'm simply not a fan, and don't believe it's the big advantage various others believe it is. But, in the same note, if it works for you, you prefer it, and you find someone/people to pair with that works well, then more power to you.

Also, I have a long history of doing remote work, working with other remote folks, and so on, and that is either impossible, or mostly defeats pair programming (Pivotal may disagree, but I do know they've had some hardships in this area as well). Differing time zones are not friendly to pair programming.

All this also ties into the next topic...

The Creativity Zone



I think Hivelogic nails it with this:
Unfortunately, most people can’t simply step into The Zone. In the very same way you’d want to find the right time and place to read a book, creative types need to setup the specific conditions they need to enter The Zone. For some people, this might mean listening to a certain kind of music. It might be fueled by caffeine and a dark room late at night. Some people work best in the silence of the early morning. It all depends on the person.


As you can guess from my pair programming comments above, I agree about having the right environment, and that you can't just force the work to flow. I've worked with a lot of different folks. Some people like to listen to music, some don't. Those that do range from listening to classical on low volumn to high volumn metal. Some work at night best, others can do the 9-5 thing, etc. This to me is similar to the situation of working in a cafe.

I think working in cafes is not good. I'm ok with popping in for an espresso, having a casual meeting there, or just using it to take a break from the office (whether that be a company office, or home office), and just checking email or reading RSS, or what not. But I don't buy it for serious work, and secondarily, I think you people who do do that suck. Yep, straight up, you suck. You go into a coffee shop, and take up space, and then ignore everyone. Why are you there? And why do you think that's fair? You are in no way contributing to the "cafe culture" or environment of a cafe, you are detracting from it. I was glad to see Ritual take away outlets and such. You shouldn't be sitting there for hours on end leeching from them.

And furthermore, I simply don't buy it as a productive environment, even when you wall yourself off from what is around you - which by definition tells me you don't think it's a productive environment either, otherwise you wouldn't need to bring your headphones and ignore everyone and all that.

Instead, make yourself a nice home office. There are a ton of resources on the web on how to do this if you need some pointers. Which leads me to...

What's Important in a Home Office - And In Working at Home



The above referenced articles already cover some of this, I'll try to be brief. Bring on the bullets:

  • Great chair and desk, see above

  • Proper lighting. In this I mean both the actual lights, but also how windows affect your workspace. Do not face directly into a window, as much as the view may be awesome. Usually you want windows on the side of you (not in front or back). I have a nice forest, mostly, to look out on my left side window - easy enough when I need a break to just turn my head.

  • A separate room. Not everyone can do this, but I feel VERY strongly about this if you plan to do significant amounts of work at home. You need a space that you can go to that is your office, where you can make a shift into work mode, have some isolation, close doors (so phone calls are quiet and so you can work without distraction), etc. It doesn't have to be huge, but make it your office space.

  • Good machine and monitor(s). Big monitors are key. I use a MacBook Pro as my main machine, but have an external 24" monitor (I want to go to a 30" when I can) on it as well, and an external keyboard is good too.

  • I feel you shouldn't have a beverage bar in your office. Just keep it in the kitchen, save electricity or whatever. But, for me, this is a good way to force me to get up and walk a bit, allows some different thinking time, etc. I almost always have a glass of water on my desk, but I get up to make an espresso, or maybe grab some fruit, or whatever. The break is always good.

  • Ok, I used to laugh at this recommendation, but I'm now one who does it, although I don't think it's required... Get up, and take a shower, get dressed, etc. I mention this for the two reasons I need to do it (but if you don't, then no biggy): I am not a morning person. I need to wake up a bit slower, and I prefer less interaction with people when I first get up. So, for me, what I've found is great, is to get up, and go shower. It is my way of having a slower re-entry. But, it also helps shift me into work mode (even if I don't wind up "going to work" for another hour or two. It flips that switch more explicitly for me.



That's all for now, as I want to get on to the last point...

Passion



In some of the referenced articles, and this has grown to be discussed a lot in reference to these, there is mention of whether folks need to be work-a-holics to have a successful startup. 37 Signals says to fire them. Calcanas mostly the opposite. I'm very strongly in the 37 Signals camp on this - to me it all comes down to passion. I believe this beyond startups as well, and it's one reason I just have no interest in working for larger companies anymore, because I feel the logistics simply make it a lot harder to have everyone be passionate. But, in the end, the folks I want to work with are passionate about their work/the project. This is how I want to be with what I'm working on. Sure, there are always parts that aren't as fun, but the overall idea is to have an overarching passion for what you're doing. To me that produces the best result, regardless of actual hours worked. In fact, I'd argue that you will get FAR better results from passionate folks working moderate hours, than simply a box of people putting in massive hours.

I recall we used to joke about how there was this notion that at Oracle (or substitute various others), all the engineers worked 80 hour weeks. That was BS of course. Note, I haven't worked at Oracle, but know folks who did, although that is somewhat beside the point... They may have been at the office 80 hours a week, but there is no way they were productively cranking out great work for all 80 hours. No, they were going to the gym, eating in the cafes, goofing off, or half awake at the keyboard. Recipe for burnout.

Now, as long as you have the passion, that's the key to me. After that, if you want to put in some epic hours because you're so psyched to be moving some great project forward, that's cool. I've done it. I don't think it's something that's sustainable long term, but bursts of this are great, go for it.

Right now, to share a bit, I'm making less money than I have in a long long time, but I'm more psyched than I probably ever have been, on the work I'm doing. I'm hoping the money part changes as the startups I'm working on grow, but I'm just loving it. Working with others who are passionate, working on cool stuff, running the show myself, or being involved at fundamental levels is why I left the mothership, and I really just wish I'd left sooner. I can get into that Creative Zone every day, and I look forward to doing so!

So, I recommend you think hard about your work environment, how you make for a productive and enjoyable environment. But most of all, shoot for the passion, and mold your environment to support and foster that passion.

11 April 2008

Shoulda and object_daddy Sitting in a Tree, t-e-s-t-i-n-g

Like some other folks working with Rails, I've been a bit frustrated with Fixtures. Foxy Fixtures, Rathole, and such things, including what is in Rails 2, have helped a lot. However, the two biggest frustrations for me come down to the fragility of fixtures, and knowing what fixtures you have and how they relate to their associated fixtures. I would find myself thinking, "hmm, which user do I use when I want the one with X associations" or what not. Naming your fixtures well helps, but only so much.

Lately, I've come across two plugins that I am really loving. These are Shoulda and object_daddy. Shoulda seems to be gaining in popularity in the Rails community, which doesn't surprise me. It gives you some of the best syntax of RSpec, without having to use RSpec (which I am not all in love with, unlike various others), as well as it gives you some nice "should" methods, and other features I'll get into in a minute.

object_daddy I think is rather obscure. I only found out about it due to Tammer Saleh's presentation (video link) on Shoulda from the 2008 MountainWest RubyConf. object_daddy has improved in its short life, and is quite useful today. What it does, is provide a factory/generator mechanism for creating model objects. I've done this in the past with object constructors or factories, etc., but object_daddy organizes all this, and provides a slick mechanism, called "exemplars", that specify how model attributes are defined when generating objects, and more importantly, when generating multiple objects. Tammer covers this issue in his presentation, which I highly recommend watching.

It's taken me a bit of time/use of Shoulda to get really into it, but like so many things, use it a bit and then the light bulb not only goes off, but seems to erupt with light. The big one here for me was how to leverage contexts, and by that I really mean nested contexts. My tests now not only read better, but can be written in a much nicer fashion, as well as organized in a great way. On the organization front for example, I now often have two top level contexts in a functional test: one for cases where I'm testing actions without a user being logged in, and the other for when a user is logged in. Great way to group them.

But, what's got me most excited lately is the combination of these two testing tools, and what it's done to my tests. First, I've darn near eliminated fixtures on the project I'm using this most with so far. This has removed the fragility, as well as it's just FAR easier to understand a test case's setup/scenario. I use Shoulda's contexts and setup, combined with generated objects from object_daddy to create "scenarios" (to steal the term from the plugin that provides this kind of thing for fixtures). The benefit is that you have all of the info about your test right there in one place in front of you. You don't need to bounce between likely multiple fixture files and your test code file to ascertain what's being used in your test. Plus, you can be very specific about the data being used in that particular test (or tests).

I heartily recommend you try this out. Two other things of note:


  • Shoulda can be mixed in with existing TestUnit. So, you can slowly convert to it, or just build new tests with it, etc. Very nice. And, it doesn't require anything special to run the tests (it really is just a method generator for building TestUnit tests).

  • Check out Shoulda's "should_eventually" method. I'm making more and more use of this, as I use a Test First approach. So, I go in, and build lots of tests, and do a lot of "should_eventually" as I think of things to test and functionality I need, etc. Then as I determine how to write those tests, and following on from that, write the implementation, you simply remove the "_eventually" and let it rip.



Note, if you are an RSpec fan, you can of course achieve the same as Shoulda for contexts and such (I think anyway), so just pull in object_daddy and leverage that aspect.

And last, but not least, I've forked object_daddy to make one tiny change (a single line, actually, a single method call name change!) that's made a big difference for me (comments very welcome). This change is to, by default, call create in the generate method that object_daddy adds to your ActiveRecord objects, instead of calling new. This avoids what I found to be the common case of doing:

my_new_object = SomeModel.generate
my_new_object.save
my_new_object # or some use of it

Now, you can simply call SomeModel.generate, and use that inline, knowing it's saved in the DB, etc. I want to take a look at adding options to generate, or additional generate methods that provide the flexibility to use new, or create! or such things, for the cases where those are needed. My fork is hosted on GitHub, and is public, so feel free to check it out: http://github.com/chris/object_daddy.

p.s. For those that wonder why I've "all but eliminated fixtures", as in, what do I still have in fixtures? The only things are standard data, which in my case amounts to a couple specific users, and a couple of Roles and Permissions. These normally get setup in the DB migrations, but I'm working through what Rails does when you run tests and it wipes the DB clean (and thus doesn't pick up seed data from migrations), and other such issues.

07 April 2008

Setting up CruiseControl.rb with/for Git Based Projects

[Updated to refer to official ThoughtWorks CC.rb Git repo.]



I have a new Rails project I'm working on and I use Git/GitHub for source control. It was time to setup continuous integration, and my usual weapon of choice for that is CruiseControl.rb. Here's what I did to get my project setup under CruiseControl.rb with Git, on an Ubuntu 7.10 machine...



Setup for accessing GitHub repo


All I needed to do here was generate an SSH key for my account on the host machine, and then add that key to the allowed keys for my GitHub account.

Prerequisites



  • I setup a builder@mydomain.com email address which will get used by CruiseControl for sending build related emails/notifications.

  • You'll need to determine a port you want CruiseControl to run on, and your strategy for accessing it. For example, I run mine on a port other than port 80, and other than the default 3333. I then proxy that via Nginx, and also use Nginx to password protect access to it (since this is not a public project, etc. This will affect the CC dashboard URL setting specified below. Some notes on this:


    • I did my initial Nginx configuration using err's Nginx config generator. However, this makes a lot of path assumptions, and various other things, so you'll definitely want to go through the resulting file closely. I had a few sites on this server, so it was relatively useful to use this as a base starting point, and then just fix up paths to the access and error logs, and the PID file.

    • Here's a quicky on how to add password protection to an Nginx server (and a specific location).




Install CruiseControl and Do Site Configuration



  1. Cloned the Git version of CruiseControl.rb in location I wanted it (you could also simply download it and expand the tarball): git clone git://github.com/benburkert/cruisecontrolrb.git

  2. The DEPENDENCIES file indicated I needed to have the grit and mime-types gems, so installed those.

  3. Where your projects get stored for CruiseControl.rb is now defined by the CRUISE_DATA_ROOT environment variable, and if you don't set this, it defaults to $HOME/.cruise. I personally changed this to be /var/cruisecontrolrb.
  4. Edit the config/site_configuration.rb (probably need to rename the example version accordingly) to set site-wide settings, such as your email config and so on.

    • For email setup, I use Gmail for domains, so I have a block like this:

      ActionMailer::Base.smtp_settings = {
      :address => "smtp.gmail.com",
      :port => 587,
      :domain => "mydomain.com",
      :authentication => :plain,
      :user_name => "builder@ mydomain.com",
      :password => "password"
      }

    • You'll want to specify the Configuration.dashboard_url setting so URL's work properly.

    • There are a variety of other settings available in the file that you may want to tweak.




Add Project and Configure



  1. Did the usual usual cruise add command to add my project, but with the Git variant: ./cruise add MyProjectName --git-url git@github.com:mylogin/myproject.git (modify the Git project URL for your Git repo of course). Note that you can see all the options by doing a ./cruise add

  2. Create the test database for your project. The easiest way is just to go into $CRUISE_DATA_ROOT/projects/MyProjectName/work and do a rake db:create RAILS_ENV=test. Your first build will have already failed because this hasn't been made, this step hopefully fixes that.

  3. If your log directory isn't in Git, you'll need to go mkdir it, so something like:mkdir $CRUISE_DATA_ROOT/projects/MyProjectName/work/log.



Setup CruiseControl.rb Service/Daemon



  1. Copy the cruisecontrolrb file into /etc/init.d.

  2. I set the port for CruiseControl.rb to run on in the above /etc/init.d/cruisecontrolrb daemon file, by adding "--port 1234" (for example) to the DAEMON_ARGS variable.
  3. Start the CruiseControl.rb daemon as appropriate for your system (e.g. "sudo /etc/init.d/cruisecontrolrb start").



Finally, surf to your cc.rb site on the web and see how your build has done. If you run into build problems, you'll want to look at the cc.rb build logs (if it was your project test/build that failed) which are in the $CRUISE_DATA_ROOT/projects/MyProjectName directory (or rather, the subdirectory in there for the particular build). And Enjoy!

24 March 2008

Steak and Chard

My wife is visiting friends with the kids this week, during my daughter's first week of spring break. I'm home doing a lot of work, but it's also a real rarity that I'm home when I'm away from my family (usually it's me who's traveling). Anyway, I'm definitely doing some cooking, as well as will investigate one or two "sketchy" Mexican taco places (I scout them out, and then take the fam if good :) They're sketchy in appearance (hopefully not in food). But, for me, the sketchier, usually means the better. But I digress...

Tonight I made probably the best steak I've made in a long time, definitely one of my best ever. Not a new recipe, but just perfectly executed, if I do say so myself. Combined with it was an experiment with chard; recipe of my own on-the-fly creation.

The Steak

First I went and got an absolutely top quality ribeye from my local meat market (Long's, here in Eugene). A Roughly 1lb beauty. Then I ground up some fresh Blue Bottle Roman Espresso coffee (ground at a setting approx between drip and French Press, so on the course side, but not huge chunks). It is absolutely key to use fresh coffee beans, and grind them, none of this canned or pre-ground crap. Also, the better the coffee, the better the result. I could go on a long time here, but I won't (because I'm working on a blog entry about Blue Bottle :) Next, combine that with a pinch or three of either kosher salt or Fleur de Sel or similar salt of your choice. And then, add fresh ground pepper to the mix - about 1/3rd as much as you have coffee (more or less to taste I suppose - but don't put so much that you drown out the coffee). Liberally coat your steak with that - hide the steak in it.

I then recommend grilling the steak over a very hot grill. I use a gas BBQ, with my burners all on high - about 500 degrees on average. For the thickness of steak I had tonight (1.25"?), I cooked it just short of 11 minutes - about 5 minutes a side or so. This yields a medium-rare steak, and I mean truly medium-RARE, plenty of pink, but not bleeding. Once done grilling, pull it off and let it rest a few minutes. Stellar.

The Chard

I'm a big fan of chard, usually sautéed. Tonight I had some organic red chard. At Long's I'd also picked up some prosciutto, although a last minute decision to try something new, yielded some green peppercorn infused prosciutto cotto (cooked). First I sautéed some chopped red onion, with a pinch of Napa herbs, fresh ground pepper, and a bit of the helpful chunky grey garlic sea salt (go light here, this is not a garlic thing). A bit of red wine (a bottle I had open, oddly enough a tempranillo-syrah blend). Saute and fry that prosciutto up a bit.

Next, I separated the stalks from the chard, as they need to cook longer. Toss those in with the above mixture and steam/saute a bit to soften the stalks up. Then, put the chard leaves in, and essentially steam until done. Doesn't take long. Given that I made this up while I was cooking it, it worked out really well. Of course, most things with prosciutto do :)

Drink

I went with the easy choice here, although unexpected. I think most people would expect a nice bold red wine, and I do have some nice ones in the wine fridge. But, when I'm alone and not at a restaurant (thus not opening a bottle, or ordering by the glass) I'll go with a cocktail. My standard favorite is gin rocks with onions. Tonight this was Zuidam dry gin (battling for top spot with my usual favorite No. Ten by Tanqueray), and the best cocktail onions, Sable and Rosenfeld Tipsy Onions. I prefer my gin over just a couple large cubes of ice, so that it's not so cold as to take away flavor. Good botanical gin has a myriad of wonderful flavors, and I think shaking it with ice just kills some of that - No Ten is FAR better just slightly cooled over a couple cubes of ice.

All this, while listening to some great jazz (not typical for me, but "completed" the evening), and sitting at the bar-counter in our house. I felt like I was in a great restaurant, eating a wonderful meal at the bar, only it was in the comfort of my own home, relaxing, and loving it. I guess it was my own great restaurant; how nice!

20 March 2008

Creating Sparkle Appcast via Rake Tasks

I have a RubyCocoa application that self-updates using Sparkle. To do so, you need to create an "appcast" file which contains the version and download information for your application, as well as creating the zip file that holds your app. Then, you of course have to upload this to the server and location that you have specified in the SUFeedURL key value in your Info.plist file of your app. For general instructions on using Sparkle and setting it up, see their Basic Instructions page.

My Rake tasks do not create the zip file. I may enhance it to do this at some point, but so far I haven't needed to, and have had cases where I need to create it myself for various reasons. What the tasks do is to build an appcast.xml file from a YAML file that contains all the necessary information. Note that the name of my app is "Linker", so you'll see that in various spots. The tasks do rely on a simple directory structure:


  • Your app root directory


    • Rakefile

    • appcast


      • version_info.yml

      • build


        • Your app zip files go here (e.g. Linker_0.8.zip, Linker_0.9.zip, etc.)

        • Rake task will create the linker_appcast.xml file here






So, you have a spot you drop your zip files into, and this same dir is where the Rake tasks create the appcast file. The version_info.yml file is where you put the info needed to generate the appcast. It looks like this:


linker-04:
title: Linker 0.4
filename: Linker_0.4.zip
description: Added Sparkle updating mechanism.

linker-05:
title: Linker 0.5
filename: Linker_0.5.zip
description: Added help (see Help menu). Added bookmarklet support/custom URL protocol handling. See the new help for information on how to use the bookmarklet.


Note that you can put HTML into the "description" field, and my Rake task will deal with and preserve that.

Finally, I have two Rake tasks, one for building the appcast, and the other for uploading it and the latest zip file to the server. These each are simply one liners that call a parallel Ruby method within the Rakefile:


namespace :appcast do
desc "Create/update the appcast file"
task :build do
make_appcast
end

desc "Upload the appcast file to the server"
task :upload do
upload_appcast
end
end


The two methods rely on you defining a couple of variables in your Rakefile, adjust these as desired:

APPCAST_SERVER = 'your_appcast_server.com'
APPCAST_URL = "http://#{APPCAST_SERVER}"
APPCAST_FILENAME = 'linker_appcast.xml'


Here are the methods, first the one that builds the appcast, which you'll need to modify for your app:

def make_appcast
begin
versions = YAML.load_file("appcast/version_info.yml")
rescue Exception => e
raise StandardError, "appcast/version_info.yml could not be loaded: #{e.message}"
end

appcast = File.open("appcast/build/#{APPCAST_FILENAME}", 'w')

xml = Builder::XmlMarkup.new(:target => appcast, :indent => 2)

xml.instruct!
xml.rss('xmlns:atom' => "http://www.w3.org/2005/Atom",
'xmlns:sparkle' => "http://www.andymatuschak.org/xml-namespaces/sparkle",
:version => "2.0") do
xml.channel do
xml.title('BWA Linker')
xml.link(APPCAST_URL)
xml.description('Linker app updates')
xml.language('en')
xml.pubDate(Time.now.rfc822)
xml.lastBuildDate(Time.now.rfc822)
xml.atom(:link, :href => "#{APPCAST_URL}/#{APPCAST_FILENAME}",
:rel => "self", :type => "application/rss+xml")

versions.each do |version|
guid = version.first
items = version[1]
file = "appcast/build/#{items['filename']}"

xml.item do
xml.title(items['title'])
xml.description { xml << " xml.pubDate(File.mtime(file))
xml.enclosure(:url => "#{APPCAST_URL}/#{items['filename']}",
:length => "#{File.size(file)}", :type => "application/zip")
xml.guid(guid, :isPermaLink => "false")
end
end
end
end
end

Looking through that above, you'll want to modify the title and description at least. Now on to the uploader method:

def upload_appcast
remote_dir = "/var/www/apps/bwa/shared/public/updaters/"

Net::SSH.start( APPCAST_SERVER, 'deploy' ) do |session|
cwd = Dir.pwd
Dir.chdir('appcast/build')

shell = session.shell.sync

begin
out = shell.cd remote_dir
raise "Failed to change to proper remote directory." unless out.status == 0

out = shell.ls("-1")
raise "Failed to get directory listing." unless out.status == 0

files = Array.new
out.stdout.each { |file| files << file.strip }

# Look through the list of files and see what we need to upload, as
# compared to what we have locally - but always upload the appcast itself
local_files = Dir.glob('*')
files.delete(APPCAST_FILENAME) # we always upload this
local_files.each do |local_file|
unless files.include?(local_file)
print "Uploading: #{local_file}... "
`scp #{local_file} deploy@#{APPCAST_SERVER}:#{remote_dir}`
puts $?.exitstatus == 0 ? "done." : "FAILED!"
end
end
rescue => e
puts "Failed: #{e.message}"
ensure
Dir.chdir(cwd)
shell.exit
end
end
end

You will of course want to modify the remote_dir, and the login credentials towards the bottom (where it does the scp command). This also relies on you having your SSH keys set up, so you don't have to enter a password when it does the scp.

You could further generalize this obviously, but this is what I have, it works fine, and I haven't needed to extract anything out further. Posting here as per a request, and hopefully it saves someone else a few minutes.

18 March 2008

Webcam Recommendations?

I setup the site Basecamp Silverton for a friend, and he's wanting to add a webcam to it, to show what the mountain and a bit of town look like at a fairly frequent interval. He had gotten what looked like a relatively decent web cam - the optics are fairly good for the price, but the thing's software is atrocious and it only does FTP in terms of sending images out (it does provide a live feed, but we're not after that). So, I'm seeking recommendations.

What we want is a networked webcam with the following characterisitcs:


  • A decent enough lens to point it from his house in town up at the mountains (which are right there - as in less than a mile away).

  • It should be low cost (preferably under $300, over that will be considered, but there has to be a good reason).

  • Support SFTP preferably (FTP is ok), as a way to send images at regular intervals from the camera to a server.

  • RSS Feed for the images is an acceptable alternative to FTP/SFTP.

  • Be easy to configure and manage.

  • WiFi is ideal, but not required.

  • Must be configurable from a Mac or a Mac browser (Safari or Firefox).

  • Multiple mounting options would be good - bolt on, simply sit on a desk/shelf, etc.

  • No special networking requirements.

  • Exported image size of about 500 pixels wide or more. e.g. something with reasonable size to provide reasonable detail.

  • Exported images in JPEG, PNG, or GIF.


Anyway, let me know your recommendations.


Update: It turned out that there was a network configuration issue with the camera we have. I was luckily able to figure this out within literally 1 minute of logging into the configuration web app for the camera, after Matt opened up a hole in his firewall so I could remotely access the camera. There still seems to be some problems with the FTP, but this got us closer, and I suspect we'll be staying with this camera afterall. Of course, don't hesitate to recommend what you like, may be useful later on (and we may do a second camera if this one proves as successful as the demand has indicated so far).


Tech Books for Free

I have some tech books that I will be donating to the local library, unless someone wants any of them. If you would like one or more of these books, I'm happy to send them to you for the price of shipping. Click through to the Flickr page and message me, or leave a comment on my blog (make sure you include your email address (which doesn't get published) in your comment, so I can reply. The books are primarily cover Java and Linux, but also Python, Jabber, Mozilla, Emacs, etc.

Books For Free

Also, not in the picture, but most likely available is, "Object Oriented Perl" by Damian Conway, from Manning Press. It's on eBay, but doesn't appear it'll sell.

17 March 2008

Another Reason to NOT Put Seed Data in Rails Migrations

I discussed an approach I took recently to getting standard or seed data into your app. While I've used Migrations quite successfully in the past for this, I am no longer doing so. And, on an older app, I just got bit by it. So, here's another reason not to do it...

Now that I've run into this, it's extremely obvious, but: If you change model code for a model which is used to create records in previous migrations, you can easily break those prior migrations. This won't matter when you have existing databases you are migrating, but it will matter if you need to create a database and migrate it from scratch (maybe your Continuous Integration server does that for example, or you are simply setting up a new DB in your development environment).

For example, the case that bit me was that I recently changed a model, that had some data created by migrations, to specify "acts_as_list". In doing so, I created a new migration that added the position column - an attribute that gets filled in automatically for your model when you create objects of that type. However, when recreating the database and running up through the older migrations, the prior seed data failed, since the position column did not yet exist, yet the model's code was trying to populate it.

Luckily I was actually adding an administrative interface to managed CRUD and other ops on this particular model, and as part of that, no longer needed the seed data anyway, so was able to just nuke that from the older migrations (and luckily no tests depended on it, and production and staging systems were well past those migrations).

13 March 2008

Facebooker Publisher and URL Fixes for Rails 1.2.x Use

I'm using Facebooker, and specifically the new Publisher class it has, with a Rails 1.2.6 app (hopefully I'll get us on Rails 2 sooner than later). But, Publisher uses some methods that are only available in Rails 2 it appears, as well as the mechanism it uses to look up Publisher view templates doesn't work properly in Rails 1.2. Also, link generation doesn't work quite right in all cases, so I have a fix for that too. Documenting my changes here for my own reference, as well as anyone else it may help.

For Publisher, I've made two changes to remedy these issues, both in facebooker/lib/facebooker/rails/publisher.rb:

In the initialize_template_class method, change the line:

returning ActionView::Base.new([template_root,File.join(template_root,self.class.controller_path)], assigns, self) do |template|

to instead be:
returning ActionView::Base.new(template_root, assigns, self) do |template|

This fixes the problem where the Publisher would look for views in a directory path that contained your publisher's name twice.

The second one is to change the inherited method's call to send! to instead simply call send.

For the link generation, I tweaked the implementation of Facebooker's UrlRewriter#link_to_canvas? method, shown in entirety here:

def link_to_canvas?(params, options)
option_override = options[:canvas]
options[:only_path] = false if !option_override.nil?
return false if option_override == false # important to check for false. nil should use default behavior
option_override || @request.parameters["fb_sig_in_canvas"] == "1" || @request.parameters[:fb_sig_in_canvas] == "1"
end

The result is that if the canvas parameter is specified, then we force a full URL, instead of only a path (which is the default). This covers apps that have both a regular web application and a Facebook app, where you are generating links that point to one from the other (e.g. you're in Facebook, but generating a link that points to your regular web app).

Seed Data for Your Rails 2 Apps - Another Approach

Historically, I've used migrations to set up standard data that my database must contain in a Rails app. This would be things like standard Roles for the system, or maybe country codes or such things. However, it appears this simply won't work in Rails 2.x, because as far as I can tell, when you run something like "rake test", it blows away ALL data in your database (not just fixture data). If I'm wrong about that, please correct me. This makes sense given that it seems the drive is towards schema.rb being the official way to create a DB from scratch, and that you have the equivalent of Foxy fixtures which do lots of magic to make creating your fixtures easy (but likely quite painful to figure out how to explicitly clean up those fixtures in certain cases - so it's easier just to wipe the DB clean).

There are various solutions for creating seed/standard/structured data for your app. However, from what I've seen none address this problem that that data will get wiped out when testing. For many people that may not matter, their tests may not hinge on it. But, I like to stay DRY, and when you have standard roles, or similar types of data, there is no reason I should have to recreate those in fixtures (and risk being out of sync), or leave them out, etc. I likely have app functionality that directly depends on such things, and thus I need this during testing as well.

My solution as of now is a simple one, and one that does not scale well for large amounts of data, but for the five records I need at this point in the particular app I'm working on, it's an approach (I very much welcome better approaches!)... I simply created a "seed_data.rb" file in my config/initializers directory. Within this file I have code that does a create_or_update (or similar) of the standard data I need. This seems to work out quite well.

Update: the above breaks things like "rake db:reset", because when the initializers run, as part of the Rake environment, and the DB has been dropped, the initializer fails, and thus fails rake.

11 March 2008

SVN Externals are Evil; Use Piston or Braid

I've recently spent a considerable amount of time rectifying problems caused by SVN externals. In one of the codebases I work on, it had been developed with a heavy number of Rails plugins as SVN externals. In general, it was a good approach as these were external code, or shared code, etc. This I think is at least better than directly checking the code in, as you have a more precise record of where it's from, etc. I should also note that our externals were all set to specific tags or branches specific to our code (i.e. not to trunk, where you'd be getting updates without your control). Sounds good, what about this "evil"?

The problem comes in when you need to make changes to the code of an external. You might think, well, go change the root code and then adjust your tag, etc. In some cases you can't do that - maybe it's not code you have commit rights to, or you're making a change that's specific to your app and can't be done another way, or, as was often in the case I had, we were on a much older version, and the trunk and other tags had major differences that I didn't want to integrate.

Thus, what I needed to do was remove this as an external, and check the code in directly. Another approach would be to branch it from where you were and modify that, etc. I wasn't able to do that due to various Subversion permissions (probably not a common case, but I had no choice). This action itself (remove external, add code) is not a real problem in SVN. But, it IS a problem when you go to update. A simple "svn up" on other machines failed. That is pathetic. Instead, what I had to do was go delete the existing (svn externaled) directories, then do "svn up". This of course broke our continuous integration server, and I also had to go manually fix this up on machines I was deploying to. Crappy, but if that was the end of it, I'd probably not be as unhappy...

When it comes to merging these kinds of changes into branches, watch out! This is where SVN just flails. First if you happen to use svnmerge.py to manage your branch merging, forget it. It just can't deal with it, and will leave you with a partially complete merge. Doing it manually, even with things like --ignore-ancestors, does not work either. I had to do something similar as to the "svn up" fix: I had to go in and delete all the directories that were previously svn externals, and then do my merge. And note, do NOT delete the parent directories. For example, if all of your Rails app's plugins were externals, do go and nuke "vendor/plugins". It will then be totally confused and just not do anything, and fail. Nope, you need to specifically delete each offending svn external directory. I make extensive use of branches (I do most work on a branch for daily work), so you can multiply these problems across the number of branches you might need to be merging to, etc.

Having said all that, this problem isn't really all that illogical. I don't know how SVN works internally, but the whole svn:externals thing seems a bit like a hack, or at least not a first class citizen in SVN land. SVN merge or update, should be able to see: hey, you were up to date (for your current revision) on directory X, but this update is going to replace that with new code with the same dir name. But, it doesn't, maybe because it doesn't look at the externals properly in relation. I don't know, and I don't care, since it's broken, and my fix is that I'm moving to Git soon enough :) Also, as another point of view, I know Perforce handles this kind of thing just fine (we used remote mounted Perforce depots all the time at Adobe, and made seriously extensive use of branches (in fact, we required working on a branch)).

Now that I've spent entirely too much time on the build-up, what's the solution? Simple: use Piston (or Braid if using Git). What Piston does, is to not use svn:externals, and instead check the code in directly, yet maintain linkage to the external it came from. My take is this is really probably how svn:externals should've worked (I presume that constantly updating an external is actually a rarely desired trait). You import an svn external using Piston, and it will pull the latest code from whatever SVN URL you supply. In this case, you could use trunk, or you could as usual use a tag or branch. But then it's fixed - it will not update that anytime you do "svn update". Instead, it is up to you to explicitly tell it to update. This avoids svn externals as far as your daily operations go, and also causes zero problems for merges. It does more though.

The second benefit of Piston is that you can then modify the external code, but still bring down updates from the external, allowing a synergy between using external code and your app's specific needs. This is exactly what I needed on a couple of plugins we use, where those plugins' code had deviated significantly from our codebase so I couldn't use a newer version, but I needed to make some changes.

To summarize, the evil is SVN itself not handling changing of externals (i.e. to/from an external) in basic operations like updates and merges, which may cause a lot of manual work on your end, and break automated builds or similar. The solution: use Piston or Braid and get the best of everything.

06 March 2008

Rails Applications and Gems: Solving the Dependency Problem

There's a post today on the Relevance blog about Frozen Gems Generator. I tried posting a comment there, but it seems to have not gone through, so I'll blog my solution here instead.

Chad Woolley at Pivotal Labs created GemInstaller to solve the problem of specifying exactly what gems you want your Rails app (or other Ruby code) to use. I've dealt with this issue a lot over the course of building Rails apps, and while at first blush I didn't think this was a good solution, I'm now really like it, and use it on most of my projects (basically all the projects I control or can :)

So, why is it better than other solutions, or at least the other solutions I've seen? First, let me give a quick synopsis: it is a simple gem that allows you to create a geminstaller.yml file that specifies the version(s) of gems your app wants. This can be an explicit version, or can use things like >= version, etc. It can then automatically install the gems for you on app launch, on deploy, or just at the command line. The benefits of this solution for a Rails app include:


  • Solves the arhictecture/platform-specific gems problem. I haven't seen any of the other solutions do this, or do it well. Most just punt on it, others require a convoluted process or hacking up your other code. Because geminstaller simply relies on the gems being installed on your system, it will use the proper version for whatever system it is running on. This also ties into the next point...

  • No polluting your source control with gems. This speeds up your source control, as well as your deploys. Further, for architecture specific gems, you now don't have to have every version of each gem in your source control for each platform you need (which is quite likely at least two: your dev boxes (e.g. Macs) and your deployment boxes (Linux), but could be even more).

  • Easier, single location, statement of what gems your app requires. By using the geminstaller.yml file, you have a single place to go see what gems and which versions of those gems your app uses. This is much better than trying to look through your vendor directory, and determine what version of a gem you might have.

  • Great for bootstrapping your development environment. Sure, frozen gems usually solve this too (except for the architecture specific ones!). You can just run geminstaller after pulling down the code and it'll go install all the specific version gems you need.

  • Allows for multiple config files, so that you can build common ones you use across projects, etc. Or even cooler, your plugins or whatever can provide a file to specify what they need and you can integrate that into your config!

  • Easy to install and use. In Rails 2 environments, you can simply drop the few lines needed to use it into its own file in config/initializers. In Rails 1.x, you add these lines to your environment.rb.

  • You determine what level of function you want geminstaller doing in your app: e.g. do you want it automatically installing missing gems or just warning you? Should it put them on the load path so you are guaranteed the proper version loads, or do you want to just use it to bootstrap and live dangerously otherwise ;-)

  • Makes it easier to experiment with new versions of gems. Since you'll have to install the gem anyway (or most solutions need that to freeze them in, but not all), you can experiment by simply changing the version number in your geminstaller.yml file. To undo it, just change the number back. No need to copy the gem into vendor or a private gem repo, etc. Easy.

  • GemInstaller can tell you what gems you have on your system, but are not in your config file, as a way to see what you might need.



Check out the GemInstaller page for more details. I highly recommend this, and thanks Chad for creating it.

05 March 2008

Innovation and the New Gauge of a Good Job

Sam Davidson has a great post over on the new-ish Brazen Careerist blog/site, "New Gauge of a Good Job: Freedom." I couldn't agree more. This pretty much sums up why I left Adobe.

There are parts of Adobe, in my opinion, doing interesting things (Air and Lightroom for example), but for the most part it's so corporate, slow, and risk averse, that they are still not in the web app game - they're not just late, they're non-existant (and note that I say that as someone who has shipped network and web apps for Adobe). And to think that Microsoft said they were late to the game 10 years ago! (I don't have the quote from Gates on that, so 10 years give or take :)

I have worked on web apps there, and there are actually a couple out there, but none anybody talks about. It is sad to me, because there are awesome people there! I've worked in the web services & apps groups, Photoshop group, on consumer software, and so on, and there are so many super smart people, and lots of great ideas. I've worked for Adobe twice in my career, so it's not as if it's a bad or evil company. Moving on...

In Mr. Davidson's article, it's mentioned that Fast Company's latest issue says Google is the world's most innovative company. I've been discussing innovation with friends lately, and our (if I may) take is that that's not actually true. It is potentially the most visible and obvious candidate for that, but look at many of Google's "innovations" lately: they're actually acquisitions. The percentage of in-house developed apps and innovation has gone down as they've grown (not surprisingly).

At the core of our discussions is that big companies just aren't the ones doing the bulk of innovation these days, or even that noticeable of a percentage of it. Most innovation is coming from the tiny startups, the "garage" built companies, or much smaller companies. To many people this may not be obvious, because what seems to then often happen is that Google, Yahoo, etc. snatch up those companies. I would argue that Apple is more innovate from within than any other big company right now (iPhone, iPod, MacBook Air). Amazon might be my next candidate (Amazon Prime, their web services). It's of course all debatable and that in and of itself is fun.

The best part of it all to me, is simply that there is a lot of great stuff being done! Lots of cool web apps, interesting hardware bits, intriguing business models, and so on. So, here's to all the innovation going on out there, regardless of where it's being done! And, for folks sitting there in a non-creative, or constrained environment, take a serious look outside. It can be a bit scary to leave that cushy, well paying, great benefits job, but there's a lot more to life, and having made the jump myself, I find I'm constantly saying I wish I'd done it sooner!

02 March 2008

Beautiful Dining Table, Chairs, and Mirror For Sale

DiningSet-1

Normally I don't post eBay or Craigslist stuff I'm selling on my blog, but this is a special item (items). We have an amazing dining room table, chairs, and mirror that we're selling, because it very unfortunately doesn't work (color wise) in our new house. We've held out nearly a year (since moving) and are finally ready to part with it :)

DiningSet-2

Check out the ad on Craigslist, as well as the bigger/better photo gallery.

25 February 2008

Git: Start As a Superior SVN, then Leverage Even More

Git has been getting a fair bit of attention lately. I am relatively new to Git, but am definitely a convert and big fan after on a short time using it. I'm to the point where I really don't want to use anything else. I have existing projects using SVN, and also have extensive experience with Perforce, both of these being centralized version control systems.

So, why Git, why as a superior SVN, and so on? If you are using Subversion, or for that matter, many other choices, it is worth a serious look at Git, if at least to provide a superior solution to existing centralized version control. You can ignore the distributed version control aspects to start out. I am a strong proponent of using developer "sandboxes." My definition of this stems from our use of version control at Adobe. Put simply, a sandbox is really a developer's private branch. Those familiar with Git, Mercurial, or other distributed SCM's will immediately see the parallel. With team development, each team member works in a sandbox, and then when they have completed some amount of work that they deem suitable for the main line, or that follows with their team's checkin policies, etc., they merge their branch into the mainline (aka trunk). Doing this in SVN is fairly painful (svnmerge.py helps, but it's still weak; SVN 1.5's merge abilities may help, but it's still not even up to what SVK does). Perforce has great support for this, but it's not all that fast, and setup isn't quite as easy as Git. Also, Perforce has a locking model (i.e. to edit files, you must check them out first), which annoys me to no end after having also used SVN, etc.

A sandbox is like your own private repository, and while I don't recommend ever checking in code that doesn't compile, etc., you can if you want, and thus gain the security of your code at least being backed up/in a second location, check pointing it as much as you want, and leveraging version control, all without hosing your teammates. On larger projects at Adobe, like Photoshop, we even took this a level further, and had a sandbox for the sub-team, so you would merge your sandbox to that, your sub-team's QA would test that, and then that got pushed to main, etc.

With Git however, this "sandbox" model would be had for free, due to the distributed/decentralized model. But, do not fear, you can/do still have a central repository that is the official mainline/trunk of code! The mainline is set up as a repository, then each developer to begin work, "clones" that mainline, which creates a FULL repository on their machine (as in, not just the latest version of the code, but all history, etc.). Now, said developer can simply do their work, committing changes at will, taking full advantage of the version control system. Then, when they are ready to push their changes to the mainline/rest of the team, they simply do a push and all their changes get merged into the main repository. Very much like working on a branch/in a sandbox model, but the beauty is that you aren't having to set up a branch, you don't have to manage your branch (more painful in SVN, fairly easy in P4), and it's all VERY fast (the speed is crazy fast compared to both SVN and P4 for all these operations).

What's also cool, is that you can create branches off your own repository to do experiments or sub-projects, or isolate changes for say a bug fix, or whatever. Creating branches is so dang easy in Git that there is no reason not to do it for even the smallest thing.

Thus, it's not that you can't do any of this without Git, but Git simply makes it far easier and far faster to do this, lowering the barriers to great use of source control, and making management of your code that much better.

And, leveraging this further, you can use Git to collapse a bunch of checkins down into one. So, in this sandbox model, say you were doing a bunch of really small incremental commits, you could "squash" some or all of those prior to pushing your code into main. Here's one blog entry on this kind of thing.

Now, Git does offer one feature that I find really cool, that is not in SVN or P4 (nor any other system that I'm aware of, but of course there are many I haven't used either). This is the "stash" (see git-stash).

You can probably guess what it does. The stash allows you to take some work, and stash it away (without checking it in!) while you then work on something else in the mean time. Maybe you are trundling along on a new feature, and then something quick comes up that you need to make your top priority. Just stash your existing work away, do that new work, then apply the stash back when ready to work on that code again. The stash is like a temporary holding spot - allowing you to keep track of work, but without having to check it in. Sure, you could simply whip up a branch and check it in to that, and that certainly works, but the stash is great when appropriate.

Some of this might seem small, but as we developer's know, some of these small things can make a huge impact on your efficiency and make your day that much nicer. As said, I'm totally sold on Git, and have been converting my SVN projects to Git. I've been using GitHub as my "central" repository, or rather, the way I look at it, it's my offsite copy, or backup. But, setting up your own on a server is relatively simple as well, and you can use gitosis to manage access control and so on. Garry Dolley has a great writeup of the entire process (which is really rather short).

It might seem like a pain to change source/version control systems, but Git has tools to import an SVN repository, including all history, etc. I've used this on a relatively simple SVN repo and it worked fine - I haven't tried it on one with a slew of branches and tags, etc. Regardless, I would highly recommend checking out Git.

19 February 2008

Tour de Cafe - SF Espressos, food/restaurants, wine bars

I'm in San Francisco this week to assist with the RailsQuickStart seminar on Wed and Thurs. But, in the mean time, I'm enjoying the food and drink scene. Monday I spent with my good friend Matt, who recently moved from SF to Silverton, CO, but was back in town for a while selling some houses. Matt used to go out essentially every night, and really knows the scene. He picked me up at the airport at 9:30a, and we began our tour.

First stop, and top priority, was the new Blue Bottle cafe in the Mint district. Luckily the line was minimal, an we ordered our cappuccinos and poached eggs & toast breakfast. The cap was awesome of course, and the food was quite good too. Blue Bottle is just really good with capuccino - superb micro-foam and great afteraste. It was also cool to see their new siphon bar in action. It's a trippy, chem-lab looking setup. I don't know if I'll have a chance to try it on the trip, but will do so if I'm in the cafe again this trip. We almost ordered another drink, but decided the line was a bit long by now, and so headed over to Ritual Roasters.

Arriving at Ritual, we were greeted with a huge line out the door. But, it moved fairly quick, and we each ordered a doppio. However, we were extremely disappointed (which is saying it nicely)... the espresso was crap. Straight up, they should have been embarrassed to serve us those totally sour shots of under-extracted, under-temperature espresso! This was surprising to both of us, although it sounds like Matt has found them declining for a while (maybe they need to go back to using Stumptown - go Oregon! ;-) Yuck, we left them on the bar and skedaddled.

As the lunch hour arrived, we both didn't want to pass up the opportunity to hit the taco truck(s) over by Best Buy. Good authentic, simple tacos. I went with carnitas, Matt had carne asada and ate all his jalapenos. Back to his friend Jeff's house to meet Jeff, do a quick email session, and check the status of the day's Tour of California stage. Now it was time for me to check in to my hotel and then head out to dinner with some of Matt's friends.

We hit Chow in the Castro. I'd say this part of the day was just average. Food was decent, but nothing special, average atmosphere, etc. We were joining his friend's who have small kids (2 and 4), so I get that aspect completely (Chow was kid friendly), but since I'm traveling and in SF, I'm of course wanting to go to the really great places, or different or unusual or whatever. From there we split from his friends, picked Jeff up, and headed out to a wine bar.

Hotel Biron is a small wine bar, tucked away on an alley, sporting just a small "B" sign. Definitely the kind of place I was looking for - somewhere you wouldn't just stumble across, small, great wine, and oh-my-gosh, you could actually hear people talk in there! What a concept! We even sat on a comfy leather couch. The wine was also great. Had a nice bottle of malbec, and some glasses of Chilean (which I skipped, as I'm kind of a lightweight). Oddly enough, earlier in the day we'd seen the owner, Chris, walking across the street carrying a bicycle wheel. He looked like a bike messenger or something - no clue he'd have been a wine bar owner, but that's SF for you (Matt knows him, thus the reason we could spot this).

At this point we thought we were going to call it a night, but it was still fairly early, so as we were in the area, and a lot of other things were closed, we went to A 16. Matt claims A 16 is the second best Italian restaurant in SF. Continuing the small world aspect, as we saddled up to the bar, the bartender turns around and Jeff realizes he knows him. Of course this made for an even better night. Tim is a sommelier, travels the world, and was a fun guy to talk to. He poured us some great wines to go along with the pizza and prosciutto plate we snuck in as a last minute order before the kitchen closed.

After a while we decided an espresso might be in order, and knew it might be ok, as they used Blue Bottle beans :) Of course we needed to have some dessert to go along with that. We ordered the two most interesting looking desserts, which were the chocolate tart with olive oil and sea salt, and the pecorino gelato with buckwheat brownie. I was unsure how well the pecorino gelato would combine, but damn if that dessert wasn't awesome! The buckwheat brownie was superb, and combined with the gelato was just a fantastic dessert. The pecorino was strong, and I think Matt and Jeff were less enthusiastic about it, but if I went back to A 16, I doubt I'd leave without ordering that dessert again! Also, the desserts came paired with dessert wines which were outstanding. Everything there was great, including all the excellent info and wine Tim provided. I can't help but agree that A 16 was a great stop, and definitely an ideal ending to our evening.

Tuesday will be more relaxed as I'll be doing some work, but I'm sure will be heading for Blue Bottle and some other cafes again. Another report to follow...

13 February 2008

Auto-Complete Text Fields in Rails 2

[Note: this entry updated to include required routing, as I failed to mention that the first time.]

In Rails 2, the PrototypeHelper and ScriptaculousHelper code was removed to plugins. This means that doing auto-complete for various fields was no longer part of the base Rails. I didn't find any great, straight-forward docs on how to do this for Rails 2.x, so am sharing what I've done to hopefully help others. Further, the README for this tells you how to do it for standard string fields on a given object, but I needed to do it for an associated object field, so I'll cover that here as well. As you can guess, this only pertains to Rails 2.x...

First you'll need to install the auto_complete plugin:

script/plugin install auto_complete

This plugin will give you the text_field_with_auto_complete view helper method amongst others. It also provides a controller hook ( auto_complete_for) to implement the auto-complete action/method in your controller for you, if that works for the field you need. As per the auto_complete README, it looks like:
class BlogController < ApplicationController
auto_complete_for :post, :title
end

auto_complete_for, as used above, would implement a auto_complete_for_post_title method in your controller class. The parameters are the object and field/method of that object. The method it implements will dig through all the Post records in your database and do a LIKE comparison on the title column, comparing the title to the contents of the post[title] form field. With the results, it will generate the HTML for an unordered list (ul), and return that to the view.

This is pretty slick: you can essentially get auto-complete with a view method name change in your HTML (from calling text_field, to text_filed_with_auto_complete), and a single line added to your controller. Now, what happens if the field you want to auto-complete on doesn't directly correlate to a field on your model object? For example, in my case, I wanted to auto-complete on a field from a belongs_to association, so I couldn't use the pre-built auto-completion method that does a direct SQL query on the field.

This is actually easy to solve. You can just implement the auto_complete_for_object_field method yourlself. And, while you're at it, you might as well leverage some of the other helpful methods in the auto_complete plugin. Or, if you need custom view/HTML output, you can just render as you need within that method. When you do this, do not call the controller hook, simply implement the method yourself. Here's mine for example:
def auto_complete_for_doctor_organization
re = Regexp.new("^#{params[:doctor][:organization]}", "i")
find_options = { :order => "name ASC" }
@organizations = Organization.find(:all, find_options).collect(&:name).select { |org| org.match re }

render :inline => "<%= content_tag(:ul, @organizations.map { |org| content_tag(:li, h(org)) }) %>"
end

Finally, for completeness, here's the field's definition in my view template (Erb):
<label for="organization">Group/Practice/Hospital Affiliation</label>
<%= text_field_with_auto_complete :doctor, :organization, :autocomplete => "off" %>


The one counter-intuitive looking thing there is the :autocomplete => "off" bit. Uh, aren't we doing autocomplete? Well, this particular attribute tells the browser to not do it's auto form completion stuff (so that our code can do it instead). Oh, and of course you need to ensure you are including Prototype and Scriptaculous JavaScript libraries in your views, which you can achieve, rather bluntly, with:
<%= javascript_include_tag :all, :cache => true %>


Finally, you will need to add route(s) to your routing file. I use a sort of wild-card route to cover all my auto-completes:

map.auto_complete ':controller/:action',
:requirements => { :action => /auto_complete_for_\S+/ },
:conditions => { :method => :get }

This will resolve any routes that match an action starting with "auto_complete_for_".

With that, we have auto-completing text fields in Rails 2.x! Cool stuff.

10 February 2008

Facebook and Business

I was, and am still slightly a Facebook skeptic. I did not use it in college (it didn't exist "back then" ;-) And, it's just not something I use much, yet. But, I won't deny the incredible viral effects it has. And, I am actively working on Facebook applications (or integrations with existing sites).

I came across a great blog entry, Collision of the enterprise and web 2.0, which talks about how Facebook is being used for business more and more, and how the lines between your friends, and your "business associates is blurring. I couldn't agree more. Besides, I'm both friends with and business associates with many of the same people, a lot of them in fact.

One of the main things that has prevented more use of Facebook for me is simply the lack of "useful" applications. Zobies and movie quizzes are just not something I have time for, or rather, want to spend my time on. But, as productivity, business, and other such "useful" applications start to enter, it won't surprise me at all if I use Facebook a lot more. It is a good social and network property, and downright easy and nice for doing the social networking aspects. So, here's to seeing how Facebook's application space grows going forward.

08 February 2008

My Dock, Too Many Browsers, Fluid, etc.

As may be obvious to readers of this blog, I've been doing a fair bit of Facebook development work lately. As such, I need to have multiple Facebook (test) users to observe the social net aspects, etc. I thus have to have multiple browsers open so they can all be logged in at the same time. I am really hoping that a future version of Fluid will allow you to have a separate cookie store per Fluid app, so that I can just create Fluid apps for each FB user. But for now, I can't, so I have at least two, if not three, sometimes four different browsers open to do testing.

I also have been somewhat amused that my dock now consumes almost the entire width of my 24" monitor (1920x1200 resolution)!

Fullscreen
Uploaded with plasq's Skitch!

Oddly, this isn't bothering me a bit. I make good use of LaunchBar, but I'm still a Dock fan as well. See the Skitch page for a list of what some of those dock icons are.

And a geeky tip for Screensaver developers: drop your debug and/or release versions of your saver into the documents area of the dock as an easy way to install it (that's what the two swirly white document images are at the far right of my dock).