Your Own Private WWDC 2011
Now that Apple has released the complete set of WWDC 2011 videos to registered developers, those of us who couldn’t make it to the conference have the opportunity to hear about all the new, shiny stuff coming in Mac OS X 10.7 and iOS 5.
SQL Logging: Gemified and Now for Rails 3
Of the handful of plug-ins I’ve written for Rails over the years, the one I install on new projects almost with thinking is sql_logging. I wrote that plug-in almost three years ago and it continues to work on Rails 2.3 apps today.
That isn’t true on Rails 3, though, so over the last few days I’ve taken the time to reevaluate the plug-in and figure out how to do the same work in Rails 3. The result is the new sql-logging gem. The source is hosted on GitHub and the gem itself is available, like any other, on rubygems.org.
Couldn’t find ‘rspec’ generator?
If you’ve moved on to Rails 3 and RSpec 2, but have older projects that are still on Rails 2.3 and RSpec 1.3.x, you may notice that script/generate no longer shows the RSpec generators in them, even if you’ve frozen the correct version of rspec and rspec-rails into vendor/gems.
I’m not sure of the proper place to report this bug, but a quickie workaround is to uninstall the newer rspec-rails gem so that 1.3.x is the newest in gem list.
Automatic Git Revision Stamping for App Store Projects
Following up on my post about automatic Subversion revision stamping, here is a modified script for use with Git-based projects.
Bundler Simplified
Following up on my previous post about adapting pre-Bundler workflows to current best practices, Yehuda Katz posted his suggestions on how to approach Bundler in terms of what you used to do. It’s a good read.
One thing that caught me up was the deployment suggestion to require 'bundler/capistrano' in deploy.rb. If you installed Rails 3.0 soon after it was released, you may still have one of the Bundler 1.0 release candidates. I had RC3, and it didn’t include the Capistrano file, which led to a load error. Update your gem, and all will be well.
Adding “contrib” Extensions to MacPorts PostgreSQL
PostgreSQL is my database of choice. The main reason is that a standard distribution comes with just about everything one could ask for in a free RDBMS, and if what you want isn’t already installed, it’s often in the “contrib” directory. In particular, now that I’m doing a lot of iOS development, and location awareness is a big part of mobile apps, it’s useful to be able to work with coordinates. PostgreSQL does this using the cube data type and earthdistance functions, both in “contrib”.
Automatic Subversion Revision Stamping for iPhone Projects
Early during development of a new iPhone OS application, I discovered that App Store submissions must use a strict format for the CFBundleVersion key in the Info.plist file. It can only be numbers and dots. This left me a little frustrated with how to version pre-1.0 releases to testers. I didn’t want to use an open-source style “0.9″ and yet it wasn’t 1.0. What I really wanted was “1.0 b1″ or similar.
I stumbled on Daniel Jalkut’s post about automatically stamping the Subversion revision into Info.plist and thought that might be a good way to go. I also created a new key in Info.plist, LYSApplicationDisplayVersion, that I use as a human-friendly version string, which is where I get my preferred “1.0 b1″ form. CFBundleVersion now takes the form of major.minor.revision, where major and minor are the important parts of the human-friendly version (“1.0″) and revision is the Subversion revision number that produced the binary.
I like this method because it solves one problem raised by commenters on Daniel’s post. Because the Subversion revision is the third component, it doesn’t matter that r3999 is a mature 1.2 point release and r4000 is a risky pre-2.0 alpha for testers. Those versions end up 1.2.3999 and 2.0.4000 and it’s clear that they are from two different branches of development. For iPhone Ad Hoc distribution, iTunes also parses the version properly and knows that 1.0.100 is newer than 1.0.95.
Here is the script to paste into your custom script build phase:
# Xcode auto-versioning script for Subversion # by Axel Andersson, modified by Daniel Jalkut to add # "--revision HEAD" to the svn info line, which allows # the latest revision to always be used. use strict; die "$0: Must be run from Xcode" unless $ENV{"BUILT_PRODUCTS_DIR"}; # Get the current subversion revision number and use it to set the CFBundleVersion value my $REV = `/usr/bin/svnversion -n ./`; my $INFO = "$ENV{BUILT_PRODUCTS_DIR}/$ENV{WRAPPER_NAME}/Info.plist"; my $version = $REV; # (Match the last group of digits and optional letter M/S/P): # ugly yet functional (barely) regex by Daniel Jalkut: #$version =~ s/([\d]*:)(\d+[M|S]*).*/$2/; # better yet still functional regex via Kevin "Regex Nerd" Ballard ($version =~ m/(\d+)([MSP]*)$/) && ($version = $1); die "$0: No Subversion revision found" unless $version; die "$0: Modified, switched or partial working copy, you sure about that?" if $2 && ($ENV{"BUILD_STYLE"} eq "Ad Hoc" || $ENV{"BUILD_STYLE"} eq "App Store"); open(FH, "plutil -convert xml1 \"$INFO\" -o - |") or die "$0: $INFO: $!"; my $info = join("", <FH>); close(FH); $info =~ s/([\t ]+<key>CFBundleVersion<\/key>\n[\t ]+<string>\d+\.\d+).*?(<\/string>)/$1.$version$2/; open(FH, "| plutil -convert binary1 - -o \"$INFO\"") or die "$0: $INFO: $!"; print FH $info; close(FH);
There are a couple of other changes to this script for Subversion 1.5 and later, and for iPhone OS targets.
The first is that the regular expression allows for a trailing P in the Subversion revision. This signals a working copy from a sparse checkout, which I never use and therefore may be a problem. I have the script fail if any letter is appended to the revision when the BUILD_STYLE is “Ad Hoc” or “App Store”, which are two new configurations, cloned from Release, that I use for Ad Hoc and App Store distribution, respectively. Especially for modified working copies: I never want to accidentally hand out a build made from a modified working copy. Should the day come that I really do, I can comment this line out, make the build, and uncomment it again.
The second is that iPhone projects convert Info.plist to the binary plist format in the application bundle. In order to extract the existing CFBundleVersion key, it must be converted back to XML. When writing the plist back out, it is again converted to binary. Both of these steps use plutil, which is part of the Developer Tools.
Ruby, Threads and RubyCocoa
If you’re a Rails developer working on Mac OS X 10.6, you may have seen this message:
Ruby threads cannot be used in RubyCocoa without patches to the Ruby interpreter
It is caused by a plug-in or gem requiring osx/cocoa, frequently attachment_fu. AttachmentFu can use CoreImage as an image processor instead of calling out to an image manipulation library such as ImageMagick or GD2.
The warning is harmless, but it can be very noisy. To disable it, you can simply remove CoreImage from AttachmentFu’s list of image processors by creating a new file in config/initializers. Its contents should be this one line:
Technoweenie::AttachmentFu.default_processors.delete('CoreImage')
Image resize operations will now use one of the other image processors, which you can install from MacPorts. I never deploy to a Mac OS X server for production, and I prefer to run as much of the same code as possible in development, so this isn’t a problem for me.
Remember HTTP Password in Mobile Safari
In iPhone OS 3.0, Apple allowed Mobile Safari to save usernames and passwords in web forms. Unfortunately, Safari does not offer to do the same thing for HTTP Basic and Digest authentication. I’ve become fond of using HTTP authentication because it is very easy to set up, either in your Apache virtual host configuration or within a Rails application. There are many times that a full-fledged user database is unnecessary for a simple administration back-end.
There is a work-around, though it does mean storing your user and password in plaintext in your device’s bookmarks. HTTP allows you to supply authentication credentials as part of the URL, in the form http://username:password@example.com/.
Use content_for to Put Markup In Its Place
Here is a useful trick for ensuring that you keep your partial templates well organized without sacrificing page-load times or duplicating your layouts unnecessarily.
You can use content_for to capture some markup, but have it emitted into the page from somewhere else.
Two places this is immediately useful: adding additional tags inside <head> and placing in-line Javascript near </body>, while keeping the code itself right next to the DOM elements it works upon.
content_for works by capturing whatever appears inside the block and storing it for later use. You emit whatever is stored using yield. What’s more, content_for doesn’t clobber the previous captured text if you use it more than once with the same key.
Say you have a fancy Javascript control that replaces a standard <select> element in forms. You can do this in your partials:
<select ...> <option>...</option> <option>...</option> ... </select> <% content_for :body_close do %> <script type="text/javascript"> // code that does something with that <select> </script> <% end >
And then in your layout, just before the </body>:
<%= yield :body_close %>
Because most browsers will run in-line Javascript upon encountering it, this would delay the execution of the code until all of the page content was loaded, making your page appear to load faster.
What’s more, if that fancy Javascript control also requires a CSS file, but you don’t want to require the browser to fetch it on all of the other pages that don’t need it, you can conditionally add it to <head> by defining another content_for key that accumulates additional markup to go there:
<% content_for :head do %> <%= stylesheet_link_tag 'fancy_control' %> <% end %>
And in your layout:
<head> ... <%= yield :head %> </head>
This is also useful for keeping per-page markup like <title> and meta tags in the template and out of your controller.
