UPDATE – 17 June: My .vimrc file caused my backspace to stop working under gVim. The code snippet has been updated to fix that now.
RailsCamp5 was awesome, but it left me a little depressed. Watching all the folks using their Macs and TextMate, I couldn’t help but thing “Hell! Look how super efficient and productive these people are! What kind of second rate environment am I working in!?”. It left me a little despondent to think that, regardless of anything else, these folks were going to be more productive than me just due to their setup.
On the Saturday evening, we held a Vim BOF session – a chance for Vim users to share their tips and tricks, and to attempt to convert some of the Mac crowd into the cult that is Vim. I fully expected to go in there and pick up only a few cool key combos that I’d missed, or a few cool syntax files. How wrong I was.
From that session, I now feel entirely better about my dev environment of choice (Gentoo Linux and Vim). In fact, just the things I picked up from that session have me feeling orders of magnitude more productive.
The first awesome tool I had been missing was NERD Tree, which is a plugin for Vim which gives you the project pane / directory style window on the side showing all of the source code in your project. This feature, in fact, was the only reason why I had recently adopted GEdit in preference to Vim when working with Rails apps; it is just so handy to have a list of all of your files available.
To set it up, simply download it (or check out the git repo), and drop the NERD_tree.vim file into your ~/.vim/plugin directory.
To make it easier to access (rather than typing :NERDTreeToggle all the time), I’ve added a mapping for Ctrl+D to my .vimrc file :
:map <C-d> :execute 'NERDTreeToggle ' .getcwd()<CR>
Then, it’s as simple as selecting the file I want, and pressing “t” (to open in a new tab), “o” to open in a new vertical panel, or “i” to open in a new horizontal panel. Combined with “m” (to handle file operations like delete, copy, move, etc.) it’s as feature rich as any other project panel.
(Note for those who forget – Ctrl+W and direction to navigate between panels)
The second thing that has helped has been rails.vim, which is a plugin to make Vim more Rails aware. Not only does this fix a bunch of syntax issues, but it also provides some helpers to jump around your project quickly. For example .gf will look for a controller or model which matches whatever is under the cursor. This allows you the quickly jump to the Model from a Controller (for example).
I’ve yet to play with the integration with script/* and the partial extraction stuff, but it looks cool.
One slight tweak I’ve made is because I’m under the impression that Rik and I are the only two people in the Ruby community who actually prefer using TABs over spaces in our source code (likely stemming from the fact that I like to tweak my tabs at 2 or 3 spaces, depending on what I’m doing, whereas Rik likes his at 4 or 8). As rails.vim forces the Rails “2 space indentation”, I’ve added a hack to my .vimrc allowing preventing TABs from being expanded.
autocmd User Rails set no expandtab
Suck it, space-lovers! Do what you like in your projects, I’ll do what I like in mine!
And, to make sure I can tell when there are TABs vs. Spaces (something I was missing from e), I have set them to display as characters rather than whitespace :
set list listchars=tab:>-,trail:.
:highlight SpecialKey ctermfg=darkgrey
This means that \t characters are now replaced with a grey >- or >-- depending on my indentation setting (using set ts). It also adds grey ”.” characters for any trailing spaces at the end of the line – handy for trimming up garbage left when moving code around.
The final tool I’ve added is the fuzzy_finder plugin, which acts similarly to TextMate’s fuzzy finder and allows you to type in parts of a filename and it attempts to find it for you. For example typing “a/m/contr” will search for files matching this sort of structure. In my case, it suggests files including “app/models/contract.rb” – the file I was looking for.
I got most of the information about this from Jamis Buck’s blog post Coming home to Vim . Suffice to say, you need fuzzyfinder.vim AND Jamis’ fuzzyfinder_textmate code.
Again, I’ve added some easier keybindings so allow me to use the finder:
" Ctrl+T to find a file and open it in this buffer
:map <C-t> :FuzzyFinderTextMate<CR>
" Ctrl+B to search the buffers currently open
:map <C-b> :FuzzyFinderBuffer<CR>
" Because I don't want to find my log files
let g:fuzzy_ignore_limit = 70
For reference, my complete .vimrc file is :
set ts=2
setlocal spell spelllang=en_au
set noexpandtab
" gvim backspace breaks unless I provide this, so...
set bs=2
" enable syntax by default, ensure we're using a meaningful scheme
colo slate
syn on
" make sure that bottom status bar is running
set ruler
set laststatus=2
set mouse=a
set list listchars=tab:>-,trail:.
:highlight SpecialKey ctermfg=darkgrey
" Stop rails.vim from fucking with my expandtab settings.
" I don't care if everyone else likes spaces, TABs all the way baby!
autocmd User Rails set noexpandtab
:map <C-n> <ESC>:tabnew<RETURN>
:map <C-t> :FuzzyFinderTextMate<CR>
:map <C-b> :FuzzyFinderBuffer<CR>
:map <C-d> :execute 'NERDTreeToggle ' . getcwd()<CR>
let g:fuzzy_ignore = "*.log"
let g:fuzzy_matching_limit = 70
These tools, combined with the incredible power that Vim already has (regex substitution, I’m looking at you!), means that I am now feeling more comfortable with my environment of choice. Combined with my other tools – screen, XFCE – I feel that I can get anything done quickly that I need to.
(Reference, gt to swap tabs, or Ctrl+PageUp and Ctrl+PageDown)
Well, it’s been a long time between drinks at this blog. To be honest, that’s partially because not much noteworthy has been happening.
Let me rephrase that. In actual fact, I’ve been busy with lots of interesting things, none of which I’ve had anything interesting to say. So, a brief run-down.
That bottom point is the biggest one, and the reason for this post.
A few years ago when I was working with insilico (or, as it was still known, o’neill software), Paul introduced me to a guy who I immediately got along with. Paul’s comfy office chairs made it far too easy to sit around and chat, and our discussions ranged across a variety of topics – our shared love of Bucky, new technology, business, the environment, and sometimes simply inane conversations about nothing.
Anyhu, fast forward to 2008, and Rik and I started throwing around the idea of coming together to form a business; at the time we were both finding our respective freelance gigs a little isolating, and noticing the problems of flying solo – needing to do everything ourselves, not having colleagues to throw ideas around with, and being overlooked for larger jobs where the risk of a solo developer was just too great.
So, we started working it all out – looking at what we were passionate about, how we wanted to do business, and formulating the very best environment in which two geographically disparate developers could work together closely. We learned a lot in this process – a lot about ourselves, a lot about each other, and a lot about dealing with a 2-3hr time difference across flakey Internet connections. In short, we had a blast.
Finally, almost 3 weeks ago – the 9th of March 2009 – we launched Aurora Software to the world.
Rik and I are both passionate about Ruby, and as such, Aurora Software is focussing on Web Development with Ruby on Rails. Furthermore, we’re openly embracing all of the best practices we love – test driven development, pair programming (a post on this soon, hopefully), keeping things agile, and writing elegant code.
So, that’s my news…
Please head over to the Aurora Software website and check things out.
Super special thanks to George Atherley (of TMIAS fame) who worked with us over the past few months to sort out a kick-ass design for the site and logo. I can only imagine that we were probably the worst client he has had to deal with for a while, but he handled it admirably – he never once told us our ideas were stupid (even when they were), but rather he just smiled and waited for us to realise that, yes, he really did know best!
A little over 3 years ago – not long into my adventures with Rails – I posted an article titled Dynamic Routes in Ruby on Rails. This article has been bugging me for a while lately for one big reason – it’s one of the most popular articles on my blog, and it’s woefully outdated and a terrible guide to just what you can do with Rails’ routes.
So, this post is going to look at what’s changed since then, and how I would solve the same problem today. I’m also going to update a few of the other points I made in that post – in particular updating my somewhat unjustified stance on using method calls to produce markup.
First off, Rails’ routing system is a lot smarter than it was 3 years ago. It now easily handles non-ID “human” URLs with much less hassle than I went through, and supports many other powerful features that we would never have expected back then.
In the case of my original post, I wanted a URL structure like :
/documents-and-services/agreements
to be resolved to a request such as :
{ :controller => 'categories', :action => 'list', :name => 'agreements' }
In the old example, I put the responsibility on the application load-up by making routes.rb load up all my categories and generate the routes. Now, this might work OK in development mode, or in an application that doesn’t change often. However, in a production application, any time that you wanted to add a category you would have needed to restart the application to “refresh” the routes. Not a good solution.
So how would we do this now? Well, there’s a few ways.
Firstly, we can do the same thing as above much simpler using the routing system’s support for regular expressions. Effectively, we would put the responsibility on the action to find the category that we want to display – it’s going to need to find the row anyway, so this isn’t any different to a normal Rails action. In this case, we would rewrite our routing rule as :
map.connect 'documents-and-services/:name', :controller => 'categories', :action => 'list', :name => /[A-Za-z0-9\-_]/
This rule tells the routing system that the :name parameter can match the given regular expression (any letter, number, a dash or underscore). The controller/action parameters are exactly like any typical routing rule, and say that it should route through to that particular method.
In the action, we would then find our category, exactly the same as normal (but using the :name parameter).
@category=Category.find_by_name(params[:name])
Problem solved without any nasty code in the routes.rb file.
An alternative solution, and one which I’m particularly fond of, is setting a custom ID parameter method that will be used in the URL. I find this is often useful in places where it wouldn’t be feasible to have a 1-to-1 relationship between the data in the URL and your rows.
For example, let’s assume that the document system we were talking about above allows multiple categories with the same name – perhaps differentiated only by the link the visitor clicked. In these cases, a :name parameter would be insufficient to find the single category to display.
Another instance where the non-ID parameter can become a problem is when the data you need to display can not easily be indexed, and therefore will cause a slow database request while it finds the matching row. Although this is uncommon, it does sometimes happen.
In these cases, I like to change the model’s to_param method in order to change the id parameter produced by url_for(), and therefore link_to().
A brief rundown – when generating an :id for a URL, url_for calls the to_param() method of the object provided. Unless you override this, it’s provided by ActiveRecord::Base to return just the integer ID of the row. However, to_param gives us a heap of flexibility.
My usual trick is to change to_param to something like this :
def to_param
"#{self.id}-#{self.name_for_url}"
end
def name_for_url
self.name.gsub(/^A-Z0-9\-_/i,'')
end
The above code is fairly self explanatory – we’re overriding the to_param() method to output the id and an encoded version of the model’s name. We need to encode it, as we don’t want “bad” characters like apostrophes, punctuation marks or other special characters messing up the URL.
Given a routing rule like :
map.connect 'documents-and-services/:id', :controller => 'categories', :action => 'list', :id => /[0-9]+-[A-Za-z0-9\-_]/
link_to will generate nice looking URLs like :
/documents-and-services/14-agreements
The important thing to note here is that when reading the details back in, you don’t have to worry about the string portion of the value. When the string is coerced to an integer, the “-agreements” is simply ignored, leaving the ID value as just 14. In this way, the name after the ID is purely for vanity purposes, and can easily be changed and rejigged without any risk of the data changing. It’s also a lot more robust, as changing the name of the model doesn’t instantly kill all of your links.
Incidentally, this is the way that we currently neaten the URLs on the recently re-launched World Stock Exchange site. The Company model has a to_param similar to the above and this allows us to show nice URLs which actually show the name of the company, rather than just a plain ID.
One final thing I wanted to address from the original post concerns my former dislike for “writing HTML as method calls”. Part of this was angst from all of the shitty frameworks I’ve worked with in the past, and certainly with the crufyness of some of the Rails helpers 3 years ago. Now, however, it’s a whole different ball game.
A clever combination of routing and link_to ensures that you can easily make wide scale “neating” or structural changes to your URL scheme with trivial ease – change the routes.rb configuration and all of your links magically change to match. Not to mention that named routes can dramatically neaten up your views. What’s nicer?
link_to('Your Profile', :controller => 'users', :action => 'show', :id => @user)
or
link_to('Your Profile', profile_url(@user))
Similarly, start_form_tag and end_form_tag once left nasty tastes in my mouth. Now, however, I’m deeply in love with the form_tag block – not only does it include XSRF protection out of the box in a nice, block based method, but it is logically neater than disconnected method calls for what is effectively an element wrapping content.
I think the sensible use of method calls to generate code (eg. helpers) makes writing content much easier, and much more maintainable. As for CGI.pm’s start_html/end_html functions that I bitched about in my original post? Yup – I still hate them. It’s messy. Use a template system – whether it’s something like erb or HAML, or one of the huge number of other template systems.
There’s a lot of things that I’ve learnt in the past 3 years since I wrote that post, and there’s even more that’s changed since then. We’ve seen Rails make a 1.0 release, a 2.0 release, and development is still continuing.
So… I’ll probably have to come back and revise this post in 3 years…
Skip the waffle – take me to the script
If you’re anything like me, as soon as you heard that the 2nd part to Phusion’s mod_rails release was Ruby Enterprise Edition you were probably getting a little excited. The prospect of a version of Ruby that is COW friendly and lowers the amount of memory required to host Rails applications is indeed very interesting.
Now that Phusion have released Ruby Enterprise Edition , I set about having a play to set it up.
The installation is incredibly simple, as it does everything for you – compiles itself, installs itself into a private location and all of those good things. Once it’s set up, it is a completely isolated version of Ruby that will happily co-exist with your existing Ruby installation.
However, this has a catch. Gems are not shared between the multiple installations (nor should they be!) and this means that any gems that you are using under your normal Ruby install will need to be installed under REE. Otherwise, changing mod_rails to use REE will likely cause lots of applications to be broken without the correct Rails version for example.
I have a whole heap of gems installed on my server currently, spanning across at least 4 different versions of Rails. As such, there were a lot of gems to install under REE. So, I wrote a script to do it automatically.
The Install Script
It’s not perfect. It knows nothing about gems, and so it may re-install a gem a couple of times before it finishes. This means it may take longer that if it were “gem aware”. It does, however, do it all for you so as that you don’t have to. What do you want for nothing?a rubber biscuit?
#!/usr/bin/ruby
# The command to run for your vanila Ruby 'gem' command
OLD_GEM='gem'
# The command to run for REE's 'gem' command
NEW_GEM='/opt/ruby-enterprise-1.8.6-20080507/bin/ruby /opt/ruby-enterprise-1.8.6-20080507/bin/gem'
output=`#{OLD_GEM} list`
new_list=`#{NEW_GEM} list`
output.each do |line|
# Skip lines that don't look like a gem version
matches=line.match(/([A-Z].+) \(([0-9\., ]+)\)/i)
if matches then
gem_name=matches[1]
versions=matches[2]
versions.split(', ').each do |ver|
cmd="#{NEW_GEM} install #{gem_name} -v #{ver} --no-rdoc --no-ri --backtrace -y"
# See if this gem is already installed
if new_list =~ /#{gem_name} \(.*#{ver}.*\)/i then
puts "#{gem_name} #{ver} is already installed. Skipping"
else
puts cmd
system(cmd)
end
end
end
end
NOTE: You may need to tweak the NEW_GEM and OLD_GEM constants at the top if you’ve installed either your vanilla Ruby or REE into different locations. Also check that your shebang line at the very top is right for your particular Ruby installation.