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.

0 responses so far ↓
There are no comments yet...Kick things off by filling out the form below.
Leave a Comment