You are currently browsing the monthly archive for October, 2008.
It seems that a lot of the Rails applications I’ve been working on have a User model that has a many-to-many relation with a Role model, so that users can have a role like “admin” or “manager”, or even multiple roles. Along with this comes the necessity to conditionally show things in views. Recently I’ve started moving away from having explicit @current_user.has_role(”whatever”) calls in the views, to some code in the application_helper.rb file:
module ApplicationHelper
def admin_only(&block)
role_only("administrator", &block)
end
def manager_only(&block)
role_only("manager", &block)
end
private
def role_only(rolename, &block)
if not @current_user.blank? and @current_user.has_role?(rolename)
block.call
end
end
end
Now in views I can easily mark off sections that should be displayed to only one user type:
<% admin_only do %> <!-- administrator content goes here --> <% end %> <% manager_only do %> <!-- manager content goes here --> <% end %>
It would be easy to extend the role_only method to take an array of roles and check for membership in any of them, but I haven’t had need of that yet.
Hat tip to Aaron Longwell for introducing me to this technique.
The problem with code review jobs is that you can end up reviewing some pretty sad code.
Looks like I’ve got another bit of new business spinning up. This is good, though I’ve still got hours free.
Braid 0.5 - This tool for vendoring git and svn repos is moving along. (via RubyFlow)
The Rails Envy guys, Gregg Pollack and Jason Seifer, are back with another of their “Envycast” screencasts: 45 minutes about Ruby on Rails 2.2, which is scheduled to be released realsoonnow. They were kind enough to send me a review copy (which means, yes, I didn’t pay for it), and here are some impressions.
You certainly get enough for your money when you purchase their “Package Deal” for $16: the screencast itself in QuickTime, iPod/iPhone, and Ogg Theora formats, a 120-page PDF ebook by Carlos Brando in English and Portuguese, and a set of code samples (you can also buy the video or the ebook separately for $9 each).
Let’s take the pieces one at a time. Unlike some other screencasts, the Rails Envy guys are using green-screen technology to insert themselves in front of the slides and code samples. This adds some action to what might otherwise be a pretty dry pile of information though (fortunately, I think) the cheesy jokes are not as non-stop as in their classic “Ruby on Rails vs.
” commercials. They pack a pretty good amount of information into the screencast, touching on dozens of new features in the time they have; in some areas, they’re more comprehensive than the official release notes.
The PDF is even more comprehensive than the screencast; it covers a ton of stuff out of the changelogs, with code samples and explanations (in fact, it includes the changelogs as an appendix). If you want a complete overview of the changes coming down the pike for Rails 2.2, the PDF is the single best resource I’ve seen so far.
The code samples parallel the ebook; they’re set up as a series of tests, so you can run rake against it (this will be useful as 2.2 proceeds from RC1 to release, to see what might have changed), or dig in to test cases to see the new functionality in action.
So, is it worth the money? Well, you could get all of this information elsewhere: the Rails changelogs and source code and git repository are all public. In theory, it’s easy enough to look at the git logs going back from now to the 2.1 timeframe to see everything. But as someone who’s done just that (to help compile the 2.2 release notes), I can tell you it’s a non-trivial exercise. There’s a lot of stuff in the commit log that doesn’t make immediate sense, and there are older commits that have been changed by newer ones. This set of resources does all that work for you.
But, one caution: Rails 2.2 just reached the RC1 point, and while the code is mostly locked down, it’s still changing. The odds are pretty good that some part of this material will be obsolete when Rails 2.2 actually ships - and while it should be easy enough for Rails Envy to update the code and ebook, I’m not sure what they’ll do if there ends up being a bug in the screencast. Hopefully they have some plan for that.
If you’re considering whether to upgrade to 2.2, these resources will give you an early look at how you stand to benefit (as well as listing which 2.1 bugs have been fixed). Personally, I think the video is overkill, but I’m not all that much of a screencast fan; I can scan and even read written material much faster. But the ebook is definitely a valuable addition to the written material on Rails, and at $9, it should be a trivial amount of money for any developer to spend to get up to speed.
Looks like I’m looking for another consulting project; one major one just wrapped, so I have some pretty substantial hours open going into November.
There are a few guides floating around about how to contribute your own code to Rails. But none of them (or at least none of the ones that I found) walk you through every step of the way. So, here’s an attempt to fill that gap.
Step 0: Learn at least something about Ruby and Rails. If you don’t understand the syntax of the language, common Ruby idioms, and the code that already exists in Rails, you’re unlikely to be able to build a good patch (that is, one that will get accepted). You don’t have to know every in-and-out of the language and the framework; some of the Rails code is fiendishly complex. But Rails is probably not appropriate as the first place that you ever write Ruby code. You should at least understand (though not necessarily memorize) The Rails Way
and The Ruby Programming Language.
Step 1: Install git. You won’t be able to do anything without the Rails source code, and this is a prerequisite. The git homepage has installation instructions. If you’re on OS X, use the Git for OS X installer. Everyday Git will teach you just enough about git to get by. The PeepCode screencast on git ($9) is easier to follow.
Step 2: Get the Rails source code. Don’t fork the main Rails repository. Instead, you want to clone it to your own computer. Navigate to the folder where you want the source code (it will create its own /rails subdirectory) and run:
git clone git://github.com/rails/rails.git
Step 3: Set up and run the tests. All of the Rails tests must pass with any code you submit, otherwise you have no chance of getting code accepted. This means you need to be able to run the tests. For the tests that touch the database, this means creating the databases. With MySQL:
mysql> create database activerecord_unittest;
mysql> create database activerecord_unittest2;
mysql> GRANT ALL PRIVILEGES ON activerecord_unittest.*
to 'rails'@'localhost';
mysql> GRANT ALL PRIVILEGES ON activerecord_unittest2.*
to 'rails'@'localhost';
If you’re using another database, check the files under activerecord/test/connections in the Rails source code for default connection information. You can edit these files if you must on your machine to provide different credentials, but obviously you should not push any changes back to Rails.
Now if you go back to the root of the Rails source on your machine and run rake with no parameters, you should see every test in all of the Rails components pass. After that, check out the file activerecord/RUNNING_UNIT_TESTS for information on running more targeted tests.
Step 4: Fork Rails. You’re not going to put your patches right into the master branch, OK? Think of a name for your new branch and run
git checkout -b my_new_branch
It doesn’t really matter what name you use, because this branch will only exist on your local computer.
Step 5: Write your code. You’re on your branch now, so you can write whatever you want (you can check to make sure you’re on the right branch with git branch -a). But if you’re planning to submit your change back for inclusion in Rails, keep a few things in mind:
Step 6: Sanity check. You know at least one other Rails developer, right? Show them what you’re doing and ask for feedback. Doing this in private before you push a patch out publicly is the “smoke test” for a patch: if you can’t convince one other developer of the beauty of your code, you’re unlikely to convince the core team either.
Step 7: Update your copy of Rails. It’s pretty likely that other changes to core Rails have happened while you were working. Go get them:
git checkout master git pull
Now reapply your patch on top of the latest changes:
git checkout my_new_branch git rebase master
No conflicts? Tests still pass? Change still seems reasonable to you? Then move on.
Step 8: Create your patch. Still in your branch, run
git commit -a git format-patch master --stdout > my_new_patch.diff
Sanity check the results of this operation: open the diff file in your text editor of choice and make sure that no unintended changes crept in.
Step 9: Create a Lighthouse account. You will need one to send in a ticket with your patch. You can do this at the free signup page.
Step 10: Create a ticket with your patch. Go to the Rails Lighthouse page. Sign in if necessary. Click on “Add New Ticket.” Fill in a reasonable title and description, remember to attach your patch file, and tag the ticket with the ‘patch’ tag and whatever other subject area tags make sense.
Step 11: Get some feedback. The Contributing to Rails page suggests using the rubyonrails-core mailing list or the #rails-contrib channel on IRC freenode for this. You might also try just talking to Rails developers that you know.
Step 12: Lather, rinse, release. It’s entirely possible that the feedback you get will suggest changes. Don’t get discouraged: the whole point of contributing to an active open source project is to tap into community knowledge. If people are encouraging you to tweak your code, then it’s worth making the tweaks and resubmitting. If the feedback is that your code doesn’t belong in the core, you might still think about releasing it as a plugin.
And then…think about your next contribution!
Rails 2.2RC1 is out. Here’s the official announcement. And here are the Ruby on Rails 2.2 Release Notes - of which I am rather fond, because I wrote the bulk of them.
Some other Rails 2.2 stuff:
And more general links:
NoMethodError.new instead. - My first commit to the core Rails code. Hopefully not the last.
There’s a mismatch between the most recent Rails releases (2.1.2 and 2.2RC1) and older versions of RubyGems. Judging by what I’ve seen in various discussion fora, this is well on the way to being a FAQ. It’s made more fun by the facts that
If you’re affected, you’ll see this message when you try to run script/generate in a Rails application:
undefined method empty?' for /_generator$/:Regexp
If you see this, don’t panic. It just means you need a newer RubyGems release. I’m not sure how current you need to be, but 1.1.1 is definitely too old. As I write this, 1.3.0 is current.
Now, in theory, to update to the latest version of RubyGems, you just need to run
sudo gem update --system
But - depending on what version of RubyGems you have installed, running that command may lie and tell you that you have nothing to update. If you’re currently on RubyGems 1.1 or 1.2, you need to run a different set of commands to update RubyGems:
sudo gem install rubygems-update sudo update_rubygems
You can check your current RubyGems version with
gem -v
If it reports 1.3.0 or later, you should be good to use the recent Rails releases.
There’s a small change in Active Record behavior that’s going to bite some existing applications under Rails 2.2: private methods on an association proxy are actually private. To illustrate, this code would work in Rails 2.1:
user.rb:
class User < ActiveRecord::Base has_one :account end
account.rb:
class Account < ActiveRecord::Base
belongs_to :user
private
def secret_code
"ABCDE"
end
end
Controller code:
@u = User.first @code = u.account.secret_code
With Rails 2.1, you’ll actually get the secret code in @code, even though it comes from a private method of the account object. Rails 2.2 is stricter about this: that same code will fail with a NoMethodError. If you want to access a private method across an association proxy now, you’ll need to use @code = u.account.send(”secret_code”).
Directly accessing private methods on an Active Record instance itself was already blocked in Rails 2.1, but Rails 2.2 updates that failure to give back a NoMethodError as well.
I’ve been nibbling away at how to write the code in Rails controllers recently, and I’m at a point where I’m fairly happy with what I’m doing. The two design requirements for me were:
- Write as little code as possible
- Get decent SEO-friendly URLs
Rails already supports friendly URLs, sort of, with to_param. Consider the canonical Post model from a blogging application:
class Post < ActiveRecord::Base
def to_param
"#{id}-#{title}".gsub(" ","-").downcase.gsub(/[^a-z0-9-]/,"").gsub(/(-)*$/,"")
end
end
Armed with that definition, it’s possible to write a controller that works with URLs like /blogs/6-my-latest-blog-entry:
class PostsController < ApplicationController
def index
@posts = Post.find(:all)
end
def show
@post = Post.find(params[:id])
end
def new
@post = Post.new
end
def edit
@post = Post.find(params[:id])
end
def create
@post = Post.new(params[:post])
if @post.save
flash[:notice] = 'Post was successfully created.'
redirect_to(@post)
else
render :action => "new"
end
end
def update
@post = Post.find(params[:id])
if @post.update_attributes(params[:post])
flash[:notice] = 'Post was successfully updated.'
redirect_to(@post)
else
render :action => "edit"
end
end
def destroy
@post = Post.find(params[:id])
@post.destroy
redirect_to(posts_url)
end
end
OK, works fine. But there are a couple of things that annoy me. First is repeating the code to turn things into a slug in every model (though, with Inflector.paramterize coming in 2.2, this is going to get easier). Second is having to include a numeric ID at the start of the parameter. Third is those repeated calls to find(params[:id]).
Enter a couple of plug-ins: finder_filter, which gets rid of some of the duplication, and from_param, which handles the URLs a bit better. With those installed, I can rewrite the code this way:
class Post < ActiveRecord::Base
def to_param
title.urlize
end
end
class PostsController < ApplicationController
finder_filter :only => [:show, :edit, :update, :destroy]
def index
@posts = Post.find(:all)
end
def show
end
def new
@post = Post.new
end
def edit
end
def create
@post = Post.new(params[:post])
if @post.save
flash[:notice] = 'Post was successfully created.'
redirect_to(@post)
else
render :action => "new"
end
end
def update
if @post.update_attributes(params[:post])
flash[:notice] = 'Post was successfully updated.'
redirect_to(@post)
else
render :action => "edit"
end
end
def destroy
@post.destroy
redirect_to(posts_url)
end
end
Perfect? Naw, not really. But to my eyes, better. And getting better a little bit at a time is good enough for me.
The originals of those two plugins are not mine: finder_filter comes from Matthew Bass, and from_param comes from Michael Bleigh. But I’ve tweaked them both to work better together, which is why I linked to my forks of the projects.
I seem to be developing more expertise than I really wanted in liquid templates.
Yesterday was all about frustration. Hopefully today will be better.
Spent part of yesterday scheming over new business venture. I believe every consultant should always have at least one scheme coming soon.
Weekends are always so much better for writing code than Mondays are.
Delegates are a useful feature that I haven’t seen used in that many Rails codebases - perhaps I’ve just been looking at the wrong codebases. Active Support makes them available for Modules generally, but the use case I find myself most often exercising is with Active Record classes. Delegates let you take some methods and send them off to another object to be processed.
For example, suppose you have a User class for anyone registered on your site, and a Customer class for those who have actually placed orders:
class User << ActiveRecord::Base belongs_to :customer end class Customer << ActiveRecord::Base has_one :user end
If you’re hanging on to a Customer instance, you can get their User information with methods like @customer.user.name and @customer.user.email.
Delegation allows you to simplify this:
class User << ActiveRecord::Base belongs_to :customer end class Customer << ActiveRecord::Base has_one :user delegate :name, :email, :to => :user end
Now you can refer to @customer.name and @customer.email directly. That’s all been around and works in the current release version of Rails.
Delegate prefixes just appeared in edge Rails and will work in Rails 2.2. If you delegate behavior from one class to another, you can now specify a prefix that will be used to identify the delegated methods. For example:
class User << ActiveRecord::Base belongs_to :customer end class Customer << ActiveRecord::Base has_one :user delegate :name, :email, :to => :user, :prefix => true end
This will produce delegated methods @customer.user_name and @customer.user_email. You can also specify a custom prefix:
class User << ActiveRecord::Base belongs_to :customer end class Customer << ActiveRecord::Base has_one :user delegate :name, :email, :to => :user, :prefix => :account end
This will produce delegated methods @customer.account_name and @customer.account_email.
Just mass-unfollowed a bunch of people on Twitter. Sorry, folks, but something had to give.
Rails 2.2 won’t be all about new features. The Rails team is also taking advantage of this release to deprecate some existing features. Here’s a list of what will be going away in the 2.2 release:
render_component is deprecated. If you need it, grab the render_components plugin.
Tonight I’m demonstrating Rails to the local .NET user group. I may survive.
The week started out with a bang: I completed the first draft of Getting Started with Rails and got started on a new client project as well.
You’ll be seeing plenty about the big new Rails 2.2 features soon enough, I’m sure. But there is also a sprinkling of new helpers of one sort or another coming in this version. Here’s a selection that you might like:
It was a fairly productive weekend for me: a new version of db_populate, a minor update to from_param, the first complete draft of The Rails Initialization Process, and a big chunk of work on Getting Started with Rails.
I was complaining a few days ago that I was having trouble with authentication in cucumber stories in conjunction with RSpec. It turned out I was making a very stupid mistake (”administrator” and “admin” are not, in fact, the same thing). But in the interests of helping out anyone who ends up here via search engine, here’s how I’ve got it sorted out for now.
Here’s one of our current cucumber scenarios:
Scenario: See all vendors Given I am logged in as a user in the administrator role And There are 3 vendors When I go to the manage vendors page Then I should see the first 3 vendor names
And here’s the corresponding part of the step file that handles the login:
Given /i am logged in as a user in the (.*) role/i do |role|
@user = Factory.create(:user, :name => "the user",
:login => "the_login",
:password => "password",
:password_confirmation => "password")
@role = Factory.create(:role, :rolename => role)
@user.roles << @role
visits "/login"
fills_in("login", :with => "the_login")
fills_in("password", :with => "password")
clicks_button("Log in")
end
Note that I’m using Factory Girl to instantiate objects in this application’s tests.
There may be a better pattern for this - I’m just getting started with cucumber. But for now, it works for me.
- webrat_story_steps - Looks useful for webrat-based testing, though it’s a bit out of sync with the latest webrat release.
- Captor: A Capistrano GUI - Interesting, though unfinished.
- zena - New Rails-based CMS just going into beta after extensive alpha testing.
For the most part, routing is unchanged in the upcoming Rails 2.2 release. But there are two changes that add a little extra syntactic sugar to your routes.
First, as Ryan Daigle covered last month, there’s a new :shallow option for nested routes. This change goes a long way to answer the classic objections to deeply-nested routes. What shallow nesting does is give you additional route helpers. For example, with this declaration:
map.resources :rooms, :shallow => true do |room|
room.resources :shelves do |shelf|
shelf.resources : books
end
end
Any of these routes will be recognized:
/rooms/1/shelves/2/books/3 ==> room_shelf_book_path(1,2,3) /shelves/2/books/3 ==> shelf_book_path(2,3) /books/3 ==> book_path(3)
You can also combine shallow nesting with :has_one or :has_many for a more compressed syntax:
map.resources :rooms, :has_many => { :shelves => :books }, :shallow => true
The second change is that you can now supply an array of methods for new member or collection routes on resources. With Rails 2.1, you had to either pick a method or use the wildcard :any method. With Rails 2.2, you can specify just the verbs that a route really needs:
map.resources :books, :collection => { :search => [:get, :post] }
Now I know that getting to the Rapleaf API from Rails is trivial. I may never need this knowledge again.
- Tunnel 1.1 - Nothing to do with Rails, really; just a cute game for MacBooks that uses the accelerometer as a control device.
- Is the Hourly Model Broken? - Musings from RedMonk’s Stephen O’Grady. He’s writing from the analyst perspective, but this applies just as much to developers.
- Release 0.8.0 - Of the Mack framework, that is.
- Splitting models into several smaller files in Rails - I’m personally not convinced big models are a problem (I’d rather look in one large file than 6 small ones when tracing code). But if you disagree, here’s a solution for loading the pieces.
- Can’t activate rubyforge (=0.4.5) problems - Adding a bit of Google juice to this solution (uninstall and reinstall) since I ran smack into the problem tonight.
With the imminent release of Rails 2.2, I’ve been spending a lot of time looking at the source code and git checkins. One area where developers will appreciate some small tweaks is migrations.
If you’re not working on a multi-developer team, the default (UTC-based) numbering for migrations is a nuisance. You’ll be able to change that by setting a configuration variable:
config.active_record.timestamped_migrations = false
That will give you the old, integer prefixes instead of the new fancy ones. Once you have those, it becomes easier to edit and re-run a specific migration, thanks to the fact that rake db:migrate:redo now takes an optional VERSION to specify the target migration to redo.
Finally, Rails 2.2 is getting transactional migrations - if you use PostgreSQL. Historically, multiple-step Rails migrations have been a source of trouble. If something went wrong during a migration, everything before the error changed the database and everything after the error wasn’t applied. Also, the migration version was stored as having been executed, which means that it couldn’t be simply rerun by rake db:migrate:redo after you fix the problem. Transactional migrations change this by wrapping migration steps in a DDL transaction, so that if any of them fail, the entire migration is undone.
I’m too lazy to build from source if there’s not a good reason, and for running MySQL on OS X, I’ve yet to come across a good reason. So, I just use the MySQL Community downloads. Over the weekend I upgraded from 5.0.37 to the latest 5.1 download, because I was hard up against a MySQL bug that was fixed in later builds.
Unfortunately, while just running the installer for 5.1 worked great, it pointed the new server at a brand new set of databases - orphaning the couple of dozen databases I was working with. This was Not Good. The fix comes in two parts.
First, create /etc/my.cnf, with a single entry pointing to the old database files:
[mysqld]
datadir=/usr/local/mysql-5.0.37-osx10.4-i686/data
Second, tell MySQL to upgrade the files:
sudo mysql_upgrade -u root
My latest work in progress: The Rails Initialization Process.
- Custom Tags in Liquid - A useful tutorial for an area where there isn’t much available.
- cucumber-tmbundle - Another TextMate bundle for cucumber.
- rspec 1.1.8 - A quick revision of 1.1.5, which was just released.
- default_value_for Rails plugin: declaratively define default values for ActiveRecord models - A plugin from Phusion.
The default merging behavior of git is pretty good - until it blows chunks and litters your merged file with dozens of >>>> markers. I had this happen to me again this weekend, and was finally motivated to do something about it.
To change the default merging behavior in git, you run
git config –global merge.tool toolname
To see the list of available tools, type
git mergetool
Most of the tools that git understands are unix-y. There is one OS X tool on the list: opendiff, which launches the FileMerge tool. But honestly, I’ve never been that impressed with FileMerge’s merge algorithm.
Fortunately, xxdiff is a pretty good tool, and it’s available as a Darwin port. So, assuming you already have your OS X box set up to install ports, you can have this as your default git merge user interface by running:
sudo port install xxdiff
git config –global merge.tool xxdiff
Now files needing a merge will open in the xxdiff user interface - which is an ugly XWindows thing, but it’s powerful. I’d love to see git hook up to something like Araxis Merge or Changes (when their 2.0 version with 3-way merge comes out), but I’m not nearly motivated enough to hack around in the git source yet.
So far October is shaping up to be a pretty interesting month.
- GitX - A gitk clone for OS X. Looks nice.
- Testing Fragment Caching - How to do it in Rails.
- ExtSQL - MySQL and PostgreSQL fork that adds database activity auditing and statistics.
- Simple Ruby on Rails Full Text Search Using Xapian - One more full-text search engine that I hadn’t run across before.
- Helpify, the Omni Help Emitter - Tool to build Helpbook files from OmniOutliner documents.
Anyone had to manage cucumber/webrat stories that include logging on to a session via restful authentication? How’d you do it?
- rspec plain text stories + webrat = chunky bacon - I’m scouring the net to figure out best practices for cucumber stories; there’s not much out there. This was helpful.
- Easy PlainText Stories in Ruby on Rails Using WebRat - More work on smart stories.
- RubyGems How-To: Preventing Catastrophe - Some notes on versioning.

