posts by date

09/2007
Tabbed navigation tip for Rails

UPDATE see the refactored version here

A very common pattern in a lot of web applications is a tabbed menu with the current tab highlighted. Last night I spent some time refactoring the navigation in Golf Trac, and I thought I would post what I did, just in case someone is new to this and goes overkill on the implementation.

First of all, Rails in its entirety thrives off of conventions. It’s inspirational to say the least. I’ve embraced a conventional way of thinking in my tabbed menu implementation, so let’s get down to business.

Tabbed navigation for actions only

For this example, I’ll use a public_controller, which contains a few actions that aren’t restricted behind a login. Just to get you excited, here’s what the end result will be:

navigation ['login','register','about','tour']

You might immediately think that each link passed in is the title of an action and the route is generated by appending _path on the end. Actually, that’s pretty close. The problem I had with that quick-and-dirty implementation was the ambiguity of the named routes. For example, in Golf Trac a user can create a New course and a New round. Well, that means I have two “new” things. I couldn’t generate a named route such as new_path because it’s ambiguous.

To alleviate this problem, I’m using controller.controller_name to make the routes unique. I’ll just give the implementation (I made a pastie, as well) and mention a thing or two about it afterward.

def navigation(links)
  returning html = "<ul>" do
    links.each do |link|
      html << "<li class='#{css_for(link)}'>#{build_link_for(link)}</li>"
    end
    html << "</ul>"
  end
end

You could use content_tag instead of explicitly writing out the <ul> and <li> stuff, but sometimes I find this easier on the eyes. The css_for(link) method just pulls out a lengthy if condition (better readability) to determine if it’s the selected tab, but for completeness, here are the details:

def css_for(link)
  controller.action_name.downcase == link.downcase ? 'current' : 'plain'
end

The build_link_for(link) method simply generates the link with the appropriate named route dynamically, but again, here are the details:

def build_link_for(link)
  link_to link.capitalize, send("#{link.downcase}_#{controller.controller_name.downcase}_path")
end

Now, that’s how to select the current tab for a list of actions within a controller, but what if you wanted to select the current controller, too (i.e. nested tabs)? It’s simple. All you have to do is check if the controller.controller_name.downcase instead.

Modified for controllers and actions

In Golf Trac, I have a sidebar which has a menu that highlights the current controller, then in the corresponding content area, a set of tabs for each major action within that controller, which also get highlighted upon selection. So my navigation helper is more complex, but not by much:

def navigation(links, from_layout = false)
  returning html = "<ul>" do
    links.each do |link|
      html << "<li class='#{css_for(link, from_layout)}'>#{build_link_for(link, from_layout)}</li>"
    end
    html << "</ul>"
  end
end

Also, you’d have to modify the css_for(link) method to accept the from_layout parameter so it would know if it was supposed to check the controller or action name. Here’s how I’m currently doing that:

def css_for(link, from_layout)
  controller.send("#{from_layout ? 'controller_name' : 'action_name'}").downcase == link.downcase ? 'current' : 'plain'
end

It’s basically the same thing, only I’m determining if I need controller_name or action_name based on the from_layout parameter. And the build_link_for(link) method needs updated, too:

def build_link_for(link, from_layout)
  controller_path, action_path = "#{link.downcase}_path", "#{link.downcase}_#{controller.controller_name.downcase}_path"
  link_to link.capitalize, send("#{from_layout ? controller_path : action_path}")
end

It’s the same deal here. If it’s an action, then I need the route that has the controller name appended to it. The nice thing is the more frequent situation (view templates) would not require a true or false parameter (since it’s defaulted to false), so it keeps the API nice and clean. And you’d only have to add it in your layout once, like so:

# views/layouts/[whatever].rhtml
navigation ['login','register','about','tour'], true

Here are the routes that would work with the above example:

# config/routes.rb
map.with_options :controller => 'public' do |path|
  path.login_public '/login',       :action => 'login'
  path.register_public '/register', :action => 'register'
  path.about_public '/about',       :action => 'about'
  path.tour_public '/tour',         :action => 'tour'
end

TODO: determine a way to not require an extra parameter (from_layout), but have the helper know where it’s being called from and act accordingly.

Conclusion

Currently, I’m using this implementation to navigate around five controllers (in the sidebar) and 13 or so total actions (in the content areas). I personally like passing the text that I want to display as tabs, but that’s just one of the hundred ways to dynamically construct a tabbed menu in Rails. Whether or not you do something like I’ve shown above, I strongly recommend you setup some sort of convention to base it on. Be smart about the design and you can get so many things for free.

Good quotes worth reading

Here are a few quotes I’ve come across [from scientists, designers, photographers, programmers, marketers, etc] over the last few days, or weeks, or months. I found them to be short and insightful, so I figured I’d keep track of them here.

If everything is important, nothing is. —Unknown

Very often people confuse simple with simplistic. The nuance is lost on most. —Clement Mok

However beautiful the strategy, you should occasionally look at the results. —Winston Churchill

Reflection is like refactoring. If you can measure how much you are doing, you aren’t doing enough. —Unknown

Computer Science is the only discipline in which we view adding a new wing to a building as being maintenance. —Jim Horning

We are what we repeatedly do. Excellence, then, is not an act, but a habit. —Aristotle

The best way to predict the future is to invent it. —Alan Kay

We don’t know where our first impressions come from or precisely what they mean, so we don’t always appreciate their fragility. —Malcolm Gladwell

If you can talk brilliantly about a problem, it can create the consoling illusion that it has been mastered. —Stanley Kubrick

Buying a Nikon doesn’t make you a photographer. It makes you a Nikon owner. —Unknown

Instinct is the gift of experience. —Malcolm Gladwell

Selling to people who actually want to hear from you is more effective than interrupting strangers who don’t. —Seth Godin

You don’t need to win every medal to be successful. —Jason Fried

In many cases, the more you try to compete, the less competitive you actually are. —Kathy Sierra

If I had eight hours to chop down a tree, I’d spend six sharpening my axe. —Abraham Lincoln

Amidst all the attention given to the sciences as to how they can lead to the cure of all diseases and daily problems of mankind, I believe that the biggest breakthrough will be the realization that the arts, which are conventionally considered “useless,” will be recognized as the whole reason why we ever try to live longer or live more prosperously. The arts are the science of enjoying life. —John Maeda

Childish behavior

Today was a bad day. It was another one of those days where nothing seemed to go right. At work I’ve just been switched to a C# project, which pretty much sucks. But it wasn’t necessarily the code that bothered me. Sure, the syntax is ugly, but that simply wasn’t it. I don’t agree with a lot of the .NET methodologies, but again, it wasn’t .NET that ruined my day—it was my childish behavior.

All day I spent my time on simplistic things, yet managed not to complete a single task. I got so frustrated because I couldn’t do the things I knew were easy. It was one right after another. By lunch I was definitely ready to go home.

Unfortunately, there was a guy working with me who is relatively new to .NET and OO programming, and was trying to learn a thing or two. I say unfortunately because I was too frustrated with myself to teach/explain anything. Having someone watch the simpler things go badly aggravated me even more, since I’d normally have everything I tried today finished in 30 minutes.

Now I could complain write all day about how much I don’t like this and that, but I’d just be ignoring the real issue: my poor attitude. The truth is there are tons of people who write successful .NET applications and really do enjoy it, I’m just not one of them.

I honestly am annoyed with the thought of being back at the beginning again; especially with a language I have no desire to learn. As a result of my annoyances, though, I ruined mine and the “paired programmer’s” day today. From now on, I hope to quit being so childish when I get stuck on things, and do my best to keep a positive attitude no matter what the situation.

Of course, talk is cheap, so we’ll see how it goes.

Golf Trac was flagged tonight

Literally. The past couple of nights I’ve had some time to work on Golf Trac. I spent most of the time coding, but I did stop to visit a few spots that were begging for attention. One of these spots was the dull header image. I shrunk the green a bit and added a flag. It doesn’t have anything to do with “tracking” a golf game, but I believe it was a needed adjustment. I did consider something corny, like making the flag stick the “L” in GOLF, but it just didn’t look right. Maybe that’s because I can’t make a nice flag stick in Photoshop. At any rate, here’s what I came up with…

Curiosity

A natural inquisitive human behavior that lends itself to exploration and investigation.

I’m a very curious person in regard to a lot of things. I enjoy looking at someone else’s work, and sometimes comparing it to my own. I’d say many others are curious in the same way. When I posted about redesigning this site, for a couple of days, the traffic rose from (an average of) 60 visits per day to 110 or more. I’d say curiosity was the reason.

Golf Trac currently has 68 signups. I’m betting at least half of them aren’t by golfers, and I bet over half have no intentions of actually using it. They’re just curious. Plus, anonymity takes curiosity to a whole new level.

Fortunately, this site and Golf Trac serve a purpose above how many visitors/users I can attract. Honestly, I would do nothing different if I had 0 visits and 0 sign-ups. For me, being able to build anything I want is gratifying enough. But I do believe curiosity is one reason why it’s so important to keep software evolving. Change keeps those curious minds coming back. Plus, applications that neglect change often fall out of favor and become obsolete.

Dead software is about as attractive as dead batteries.

Making an icon is difficult

Typically, if I need (or want) a certain icon, I’ll dig through the tons and tons of free icon libraries to find what I’m looking for. Usually I can alter it from there if need be. But starting from scratch is a different story. I don’t have the graphic talents needed to make icons myself, or really any image that requires working at the pixel level.

Earlier this week I wanted to put an arrow-ish series of images for a “bread crumbs” trail at the top of a page. Getting the appropriate CSS classes on the list items went smoothly (is it the first? is it the first with nothing trailing? is it the last? is it a middle item? etc). But concerning the graphics, nothing looked right. I eventually just accepted my inability to create nice-looking icon-esque images. I’m not satisfied with them, but I’m afraid I’ve done the best I can.

I won’t even go into logos. Not only are they demanding graphically, but defining a logo design is near impossible [for me]. Well, a logo that has actual meaning that is.

So I tip my hat to icon and logo designers—it truly is an extremely challenging part of design.

Checking email in batches

Email used to be a sure fire way to get in touch with me quickly. I used to be really efficient and on top of it. Anymore, though, I seem to ignore it for a while and check it in batches. It’s kind of odd, I see the preview pop up, I just choose to wait as if it’s a major task or something. I think it’s a combination of gmail slowing down a bit and Firefox being a memory hog that has instilled this behavior (of course, Firefox could be the culprit to gmail being slow, but the difference is the same). No matter the reason, now that I’m in email batch mode, I like it better. It’s far less distracting. The downside is being ignorant to what’s there and, for example, not knowing there’s a meeting until there’s a meeting. Or not realizing an email that I may need to respond to quickly.

So, no more what felt like constant email reading for me. I choose to be inefficient on this one.

Offline web applications

I think Slingshot (for Rails) was the first time I had heard about offline web applications. Right off I didn’t get it, and now, I still don’t get it. Am I the only one who thinks this is dumb? I thought maybe it was a phase, but then Google Gears came out (around when Google Reader was capable of going offline), as well as a few other libraries (or whatever you want to call them). I just read today that there are actually people wanting offline gmail. I don’t know what kind of lifestyle you’d have to have to be in a place where 1) you didn’t have internet access (and can’t get to it) and 2) absolutely had to do something online right then and there.

I mean, it’s cool that it can be done and all, but far too many things are done out of coolness (I’m guilty of that, as well). But really, just because you can doesn’t always mean you should.

Spam is extremely annoying

After 529 comments spam-free, it finally happened (3 comments worth). With this new release I changed the spam protection to use a different method, but alas, it has failed me. So the myth that you can stop spam via CSS is false. I should have known better than to fool with something that was working perfectly.

For the record, I just may hate spam more than anything else in my life—it’s so frustrating.

Revamped and absolutely positioned

A long time ago I decided I wanted this site to represent my interests, creativity, and expertise. I believe I’ve yet to achieve that until now. So, to that end, I’m unveiling yet another redesign. As you’ll notice, I’ve kept a few things from the last design (mainly the colors), but I believe it’s more aesthetically pleasing and somewhat easier to navigate.

Relocating the posts

The first order of business was to relocate the posts. Having the posts basically make up the site made me feel a bit guilty if I didn’t post frequently. Also, I began to realize how uncomfortable it was having the posts blatantly hog the site’s entrance. It didn’t quite hold true to what I wanted the site to represent, so good riddance to that idea.

Just the right width

I finally decided to ignore the 800×600 screen resolution. The last design had a fluid center column, which dealt with smaller resolutions quite nicely. However, a maximized browser on a wide screen monitor was horrendous. Google Analytics told me quite a few visitors have wide screens (and one or two, if any, used 800×600). For this design, I chose a width for each section (left, center, right), and it just so happened to be 900+ pixels. Oh well. Anyone still sporting an 800×600 resolution probably wouldn’t realize the horizontal scroll bar, anyway.

Web designer’s curse

Of course, I could come up with a ton of reasons for another redesign, but none of that really matters. A lot of people can recognize when a site goes stale and needs a new look. But to a web designer, his/her personal site goes stale much quicker than any other. It’s rather bitter sweet. I love the refreshed feeling of a new design, but quite frankly, a new design is a lot of work and a lot of time. However, it can’t be ignored until a new design is realized, hence, the curse.

Lately, I’ve been asked on several accounts to show prior work (read: portfolio), so in that regard, it feels more appropriate to put a little more emphasis on the portfolio and past experience. This is a far cry from anything professional, but I’d say it’s a step in that direction.

WVU "sketching up" Morgantown

It appears that West Virginia University is re-creating the city of Morgantown using Google SketchUp. And here’s proof.

Writeboard lost my data

I’ve been wanting to try my luck with writing a certain Firefox extension1 for a while now, and I may have just found the motivation to do so.

Last night before bed, I threw in a “Google Reader rollback” reminder in writeboard and saved it. But to my dismay, only that title was saved, meaning everything else (20+ titles) was gone. Sure, they have versions, but you can’t edit an old version (read: can’t get the textile for a previous version), so all of the formatting and links were lost (sometimes I draft them out, too).

Of course, losing data is typically not something you want to happen, but fortunately for me, I survived. It reminds me of when I somehow accidentally hit “mark all as read” in reader—at first, it’s disheartening, but then it’s kind of refreshing. Anyway, in the end, none of this crapola really matters, I’m just saying, writeboard lost my data. And now my Firefox extension motivation is gone again…

1 When I’m working or reading, I’ll often realize something I may want to post about. I don’t always have the time to write up a post, so the extension would allow me to quickly save the idea for a later date. Similar to the deli.icio.us extension, it’d do nothing more than pop-up a window with one text box for an idea. FYI: I’m currently using writeboard to save post reminders (titles, basically).

Google Reader rollback

Last night before bed, I checked reader to see if there was anything pressing… there wasn’t. However, I noticed a few changes to the reader itself:

  • the ajax loading block was no longer in the middle of the screen, but was at the top, similar to their other orange notifications
  • there was a way to open/close the sidebar (by clicking rather than pressing “u”)
  • the feed counts bumped up an order of magnitude, and would display the actual count up to 1,000 (so instead of 100+ it would show 192, but over 1,000 would show 1,000+, which is much better)
  • it felt at least 2x as fast

I didn’t thoroughly check for more features, those were just the apparent one’s. I figured I’d give it a more in-depth run this morning, but it’s back to normal now. Personally, I think they had to rollback. I say this because my photography feeds reported 1,000+ unread and I know I don’t have over 1,000 unread posts just for photography. Either way, I’m guessing a few minor updates are just around the corner.

Perfect example of online collaboration

ConceptShare. There’s a difference between collaboration and social networking. To me, collaboration serves an explicit purpose, where social networking is an implicit bonus. To some degree, though, often “social networking” is not a bonus. It carries a negative connotation because so many new applications thrive on the network itself, rather than treat it as the implicit bonus that it is (or should be). I mean, social networks aren’t products, after all. Then there’s collaboration, which is something to build an application around, in my opinion.

Just recently I redesigned the front-end of a site according to the demands of a client. In order to show the changes, I had to upload it to a test space on the server and email a few people the link with a description of the changes I had made. Then I got several emails back with likes/dislikes and more changes to be made. It’s tedious to collect emails that have gone through several people regarding thoughts on the same concept. That’s not really collaboration, but more of a pain in the ass. It was slightly more convenient than meetings, but by the same token, ended up being much less effective.

I don’t know how long ConceptShare has been around, but it appears to solve a big problem (remote collaboration) in a polished way. I won’t list out the features, here, but rather suggest you watch their demo video.

Everything around one concept should remain in one place. As much time as I spend reading online, I’m surprised I haven’t stumbled upon more applications that attempt to put a halt to the endless hunting for emails/attachments when collaborating. Personally, I’d much rather see applications that serve a purpose over those that do nothing but waste domain space, but maybe that’s just me.

2008 by Ryan Heath | Get In Touch

flickr

DesolateInfinityLooking upDazedBlurred