Breadcrumbs in Rails
I’ve recently been pondering the best practices of implementing breadcrumbs in a Rails app. Plugins and gems would be superfluous to such a trivial task, which leaves us to our own devices. I’ve read somewhere that specs can be helpful in designing a system, so I made some up:
- Each crumb should be a link, except for the last one, which denotes the current page
- It has to be flexible, allowing for many use cases
- The implementation should be simple and brief, but mostly simple
- The calls should be simple and brief, but mostly brief
- Brevity may decrease as the complexity of need increases, and as probability of need decreases
The following is how I mostly satisfied these. If you’re searching like I was, maybe you’ll find this helpful, or even perfect. Or maybe you’ll be horrified and post incendiary comments. “No, you cretin! Put the responsibility on the controllers, not the views!” or “Go back to PHP where you came from, like a dog returning to its vomit!” Ouch, guys. Be more constructive with your feedback.
Displaying breadcrumbs
Here’s part of my application.html.erb. (Tags have been removed for readability.) @breadcrumbs is just an array of arrays like ['History', '/about/history']. This differs little from other solutions, such as require ‘brain’ . We’ll get something like Home » About » History.
1 2 3 4 5 6 7 8 9 10 11 |
# If no breadcrumbs have been set, this calls drop_crumbs without # arguments, which causes it to parse request.path into @breadcrumbs drop_crumbs if @breadcrumbs.nil? # Print out the breadcrumbs as links, excepting the last one @breadcrumbs[0..-2].each do |text, path| link_to(h(text), path) + "»" end # Show the current page's crumb, but not as a link h(@breadcrumbs.last.first) # Force double arrows if we're on the root page "»" if @breadcrumbs.size == 1 |
Creating breadcrumbs
Believing that a system which automagically figures out all breadcrumbs all the time is impossible, and believing that a system which requires you to always specify them is stupid, I tried to write drop_crumbs with useful defaults and a mixture of implicit and explicit arguments.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
# Append one or more crumbs def drop_crumbs(*args) @breadcrumbs ||= [] args = [request.path] if args.empty? args.each do |crumb| crumbs = make_crumb(crumb) if crumbs[0].instance_of? Array @breadcrumbs = @breadcrumbs | crumbs else @breadcrumbs << crumbs end end end private # Returns a crumb array like ['Home', '/'] def make_crumb(crumb) return ['Home', '/'] if crumb.empty? if crumb.instance_of? String break_off_crumbs(crumb) elsif crumb.instance_of? Array return crumb if crumb.size > 1 return ['Home', '/'] if crumb[0].empty? title = crumb[0].split('/').last.split(/_|\s/).each { |word| word.capitalize! }.join(' ') unless crumb[0].nil? [title, crumb[0]] end end # Break a uri into an array of crumbs def break_off_crumbs(uri) crumbs = [] split_uri = uri.split('/') split_uri.each_with_index do |crumb, i| crumbs << make_crumb([split_uri[0..i].join('/')]) end crumbs end |
Useful defaults
drop_crumbs
If we’re at /contact/electronic/fax, this will automagically get us Home » Contact » Electronic » Fax. In some apps, that could cover most cases.
Simple implicit path arguments
drop_crumbs('/store/checkout')
This will give us Home » Store » Checkout. You could get the same result by passing ['Home', '/'], ['Store', '/store'], ['Checkout', '/store/checkout'], but that’s a waste unless you want to specify different names for the links.
Flexible mixed implicit and explicit arguments
drop_crumbs('/human_resources/employees', [@employee.first_name, employee_path(@employee)], ['Edit'])
Let’s say you’re at /human_resources/employees/24/edit. Maybe you want to give number 24 a raise. Maybe you want to fire him. Pick a scenario that makes you happy. This will give us Home » Human Resources » Employees » Alastair » Edit. If instead we had relied on the defaults, we would have seen 24 in place of Alastair, which is less meaningful.
Note that the final Edit crumb doesn’t have a path. Since it’s the last crumb and won’t be a link, we can safely ignore it.
I will admit this looks complicated for just replacing an id with a name. You may want to break it up into multiple drop_crumbs calls or write a drop_employee_crumbs wrapper that takes an employee for an argument. Or maybe there’s something much better I haven’t thought of yet.
This is my first shot ever at breadcrumbs, and I’m sure I’ll find improvements as I continue to explore the uses. Currently, my drop_crumbs and associated methods are helpers in ApplicationHelper. When I need to explicitly call drop_crumbs, I do so in my views. I can see an argument for moving those calls into controllers, but I’m still mulling that over.
In the meantime, If you’ve got a Breadcrumbs Blue Ribbon pinned to your chest, please share your secret.
June 21, 2010 at 2:09 PM
WbtOGx <a href="http://ugbzahsngesr.com/">ugbzahsngesr</a>, [url=http://mmqflmhdeypt.com/]mmqflmhdeypt[/url], [link=http://adrketuyzcju.com/]adrketuyzcju[/link], http://nrahotlzideo.com/
July 19, 2010 at 2:08 AM
[URL=http://zgstdlbt.com]fyfyloqu[/URL] wimghxcc http://imushypl.com apojenad mjcpkxad <a href="http://nncinqvl.com">szfdvzxh</a>