Information Technology Dark Side

Struggles of a Self-Taught Coder

Information Technology Dark Side header image 2

Conventional Rails

June 27th, 2013 · No Comments

Otherwise known as…
This post could also be called “Stuff I’ve figured out about Rails but didn’t want to blog about because I suspect everyone else already knows it and will figure out that I’m a moron”.

A code picture is worth a thousand words
Here’s some code. I’ve got some routes in there to set the stage, followed by pairs of roughly equivalent code demonstrating some conventions I’ve learned about and have grown to appreciate. I’ve not seen these conventions documented anywhere, but that doesn’t mean they aren’t. It just means I learned them the hard way.

# Routes
resources :users do
  resources :addresses do
    resources :phone_numbers #NEVER
  end
end

# users
# users/6
# users/6/edit
# users/new
# users/6/addresses
# users/6/addresses/5/edit
# users/6/addresses/5
# users/6/addresses/5/phone_numbers/11
# etc...

resource :tricks
namespace :magic do
  resources :tricks do
    get :secret, on: :member
  end
end

# magic/tricks
# magic/tricks/new
# magic/tricks/6
# magic/tricks/6/edit
# etc...

######### Example 1 - in a view #########
= render 'users/user', user: @user
= render partial: 'users/user', locals: {user: @user}
# Partial in ./_user.html.haml

# is roughly equivalent to...
= render @user
# Partial in users/_user.html.haml

######### Example 2 #########
= render partial 'address', collection: @user.addresses, as: address
# ./_address.html.haml

= render @user.addresses
# addresses/_address.html.haml

######### Example 3 - controller redirects #########
redirect_to show_user_path(@user)
# OR how about this?
redirect_to @user

redirect_to users_path
# OR how about this?
redirect_to :users

######### Example 4 - It works in links too #########
# In a view
link_to :users

# Some equivalents...
link_to edit_user_path(@user)
# Is the same as
link_to [:edit, @user]

link_to edit_user_address_path(@user, @address)
# Is the same as
link_to [:edit, @user, @address]

# Namespaced index view
link_to magic_tricks_path
# Is the same as
link_to [:magic, :tricks]

# Regular edit action in a namespace 
link_to edit_magic_trick_path(@trick)
# Is the same as
link_to [:edit, :magic, @trick]

# Custom action in a namespace
link_to secret_magic_trick_path(@trick)
# Is the same as
link_to [:secret, :magic, @trick]

######### Example 5 - rendering partials with a namespace #########
# In a view
= render [:magic, @tricks]
# Partial in views/magic/tricks/_trick.html.haml

= render @tricks
# Partial in views/tricks/_trick.html.haml

= render [:user, current_user.tricks]
# Partial in views/user/tricks/_trick.html.haml

######### Example 6 - the challenge #########
# What does this redirect to?
class AddressesController < ApplicationController
  def destroy
    @user = User.find(params[:user_id])
    @address = @user.find(params[:id])
    @address.destroy
    redirect_to user_path(@user)
  end
end

# Whatever you said is probably wrong. It actually redirects to a the user destroy action. WAT!?! Or, at least I think that's what I observed... 

# I THINK THIS IS A RAILS BUG!!!!

# Which brings up a point... how do you create a path to delete stuff using this convention?
link_to [:magic, @trick], method: :delete, confirm: "Are you sure?"

README
These conventions are all about two things for me: readability and the principle of least surprise. I don’t think the readability comment needs to be explained, but maybe the principle of least surprise does. When you render partials in this way, there is no question about where the partial lives in the file structure, even when you are rendering an object in a view outside of its normal view folder.

I also like the way this forces you to think about all those partials you create as you build an app. Every time you find yourself in a situation where you need to render an object and you can’t just do ‘render @thingy’ in your view, you should take a minute and think. Are you really doing the right thing? Do you really need to render a thing six different ways? This isn’t just about clean, dry code – it’s also about good UI design. The choice to create a new partial should be a deliberate one.

But, hey, you can have your cake and eat it too. Sometimes. For example, on TroopTrack I have 5 major functional areas: plan, manage, communicate, achieve, and share. Three of these functional areas have different renderings of users, but I can still follow this convention using namespaces, like this:

# In a view

= render [:manage, @user]
# partial: app/views/manage/users/_user.html.haml

= render [:achieve, @user]
# partial: app/views/achieve/users/_user.html.haml

= render [:communicate, @user]
# partial: app/views/communicate/users/_user.html.haml

Happy, happy, happy
I really lourve this convention for rendering. It changes my approach to the user interface in ways that encourages goodness and drives consistency, but that’s not the only reason. When the time comes to start worrying about performance, it makes russian doll cacheing a lot easier.

Ugh. I can’t seem to wrap this post up, so I’m just gonna stop typing.

If you enjoyed this post, make sure you subscribe to my RSS feed!
Stumble it!

Tags: Uncategorized

0 responses so far ↓

  • There are no comments yet...Kick things off by filling out the form below.

Leave a Comment