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).

07 February 2008

Asynchronous Tasks in Rails

On the blog Play/Type, there's a great article on how to do asynchronous tasks in a Rails app. Furthermore, they've built a Rails plugin, Workling, that encapsulates asynch tasks and provides a wrapper around Starling, Spawn, and others, making these easy to use.

I came across this tonight as I was figuring out a solution for spawning off Facebook profile FBML updates in the background since these can do a fair bit of data gathering. It is also useful in controllers used in a Facebook app, because while you never want your apps' actions to take more than 8 seconds regardless, if they do for Facebook, Facebook will simply fail to load the page. Therefore, if you have an expensive operation that can occur asynchronously, but is initiated by a controller, this is another place to leverage something like Workling.

For my own needs, I didn't wind up using Workling, as Spawn was sufficient for my needs at this time. Before I go to production, I may switch to Workling simply to provide future growth in case this Facebook app takes off, etc. This will be one to watch for sure.

04 February 2008

Testing Facebook Applications With Test Facebook Users

Facebook's terms of service don't allow "fake" users, or user's who aren't associated with a human. This makes it harder to test Facebook applications which aren't published. However, they do provide a mechanism to mark an account as a test user account (see the Test Accounts wiki page). It falls under a bunch of different rules, some of which make things slightly challenging. For example, non-test user's cannot be friends with test users. This means in order to use a test user, you will have to create at least two of them, so they can be friends. Doesn't exactly parallel real life does it? :)

So, the next problem that crops up, is that you will want to register your developer app using a real account, but if real accounts and test accounts can't be friends, how do you share that app? This is not obvious, but you can extract how to do it from the questions at the bottom of that Test Accounts wiki page. Here's how I set it up (these are streamlined instructions as I had to futz around a bit before seeing how to work with the limitations):


  1. Create two new user accounts on Facebook. You'll obviously need a valid email to go with this. I run various domains so just setup users in that, but you could use a Yahoo account or whatever.

  2. Add the Developer application to each of the test user accounts. This is required in order for them to use your application which is still in development and not public.

  3. Have these users become friends now if you like (they can become friends later as well). Also, have them be friends of the user you have that registered your Facebook application.

  4. In your real user account that has the registered app, make the test users developers.

  5. In one of the test user accounts, add the application. I start with just one, as I often want to test the Invite mechanism for the application.

  6. Now mark the test accounts as test accounts using the URL provided on the Test Accounts wiki page.



Now you're set to have your test user's be what you use to test your application, allowing you to do invites, post messages to the mini-feed, and so on, and see how this works across friends. Hopefully Facebook will improve this in the future, as per various suggestions on the wiki page.

03 February 2008

Take the Red Book

The latest issue of Wired has an article by Clive Thompson titled, Take the Red Book. The article, available on Mr. Thompson's web site struck a chord with me. I have not been a big reader of SciFi, but I've noticed that over the last couple years, I've been reading less fiction, and amazingly (to myself), a lot of non-fiction. I think his article resonated because I realized that I have been a bit bored with some of the fiction as well. Further, his article got me to read Cory Doctorow's, After the Siege, which is a great short story. It is also not crazy out-there SciFi.

I've read William Gibson's two latest books, er, scratch that, I'm still in the middle of his latest, and honestly, I don't think I'll finish it. Granted, these are a departure for him, and are not SciFi, but IMHO, aren't that good. I liked some of his earlier work, and I know he's highly regarded, but a lot of it was just too far out there, too much tech. I've read Snow Crash, various Heinlein, Asimov, Douglas Adams, etc. And actually, while I haven't read all of his works, Bruce Sterling's Heavy Weather is an absolute favorite, that was right up my alley. And now, having read After the Siege, and based on the Take the Red Book article, I'm thinking I need to get back to some SciFi, and just try some different authors. Recommendations?

Delete an S3 Bucket Containing Thousands of Files

A backup bucket I had on Amazon S3 (via Jungle Disk) had gotten out of control and was costing me too much. I decided to kill it off completely and take a different approach. So, I wanted to delete the bucket. I thought, easy, I'll just use Interarchy, pick the bucket, hit delete and be done. Nope. Interarchy kept choking on some of the files. I tried a few times. Turned to Transmit, failed as well. So, instead, a few lines of Ruby, via the aws-s3 gem, and I was done, with one caveat.

Assuming you have the aws-s3 gem installed, I just used IRB to do it. First there was setting up the connection, and then finding the bucket in question:


require 'aws/s3'

AWS::S3::Base.establish_connection!(:access_key_id => 'put-yer-key-here', :secret_access_key => 'put-ye-ole-secret-key-here')

# Find the bucket the blunt way, by getting a list of the few I have, then picking the particular one (for me it was the second one in the array):

buckets = AWS::S3::Service.buckets
evil_bucket = buckets[1]


Then, you need to blow everything away. According to the docs, you should just be able to delete the bucket, passing the ":force => true" option, but that didn't work for me - it complained that the bucket didn't exist. So, instead I decided I'd delete everything in the bucket using the Bucket#delete_all call. That appeared to work, but it wasn't actually empty. Then I found that the library only pulls down 1000 files at a time (this is a standard S3 limitation in a listing call - although you'd think the library would realize this and loop until it was truly done), so it was only deleting 1000 files. So, the trick then was to do:


while !evil_bucket.empty?
puts "."
evil_bucket.delete_all
end
evil_bucket.delete


I have the puts in there, simply to observe progress (and it's also sort of fun to see how many files there really were). Obviously this is quick and dirty, but it wound up being far more effective, and nearly as simple as I had expected Interarchy to be. One note, if you do have a lot of files, this is not something that goes quick - it was taking about a minute per 1000 files (i.e. per delete_all call) on my system.