Information Technology Dark Side

Struggles of a Self-Taught Coder

Information Technology Dark Side header image 2

Useful Way to Automagically Add Controller/Action Selectors to an App

February 4th, 2013 · No Comments

Not Invented Here
I swiped this idea from the work Craig Ulliott did on bodyshopbids.com. Thanks Craig!

Don’t hard-code selectors for action and controller specific CSS and Javascript
Sometimes you need a bit of CSS or javascript that only applies to a specific action, or maybe to every action on a controller. So, you go in and add a class or an id to every view that controller supports.

Then you sigh.

You can do something like this instead:

  %body{:id => body_id, :class => body_class}

where body_id and body_class are helper methods. For TroopTrack, these methods look like this:

def body_id
    body_id = controller.class.name.sub(/Controller$/, '')
      .sub(/::/, '').underscore+'_page'
    body_id.sub!(/^devise_/, "devise_#{controller.scope_name}_") 
     if body_id.match(/^devise_/)
    
    return body_id
  end

  def body_class
    if @body_class.nil?
      @body_class = ""
    end
    
    @body_class << " #{controller.action_name} "
    
    @body_class << " #{controller.class.name.split("::").first.downcase}" 
      unless controller.class.name.index("::").nil?

    @body_class << " unit_type_#{@troop.unit_type_id}" if @troop

    return @body_class
  end

I think TroopTrack’s helpers are a bit overly complicated, but that’s because many of them exist in a namespace and I want a class for the namespace as well as the controller. I also need to adjust my CSS based on the type of unit they are, so that’s in there too. Here’s an example body tag from TroopTrack:

<body class=' index  share unit_type_3' id='share_share_page'>

Index is the action. Share is the name space. Unit_type_3 means it’s a cub scout pack. The id, share_share_page, means it’s on the share controller in the share namespace.

Sometimes when I write a blog post and share my code I see opportunities to dry things up or refactor them. This is one of those moments.

A more reasonable place to start would be something like this:

  def body_id
    controller.class.name.sub(/Controller$/, '').sub(/::/, '').underscore
  end

  def body_class
    controller.action_name
  end

As usual, I’m suspicious that there is a ruby or rails idiom out there that can make that code better. I’m all ears.

With this in place, it’s very easy to override CSS or write javascript that is specific to a given page. If you are following the Rails 3 convention for assets, you could just add this scope to the beginning of every controller-specific CSS and javascript file so that you are forcing the behaviors in those files to only apply to the controllers they relate to. I’m not a big fan of this per se, but I don’t condemn it as a practice either.

Don’t Overdo It!
It’s easy to let this get out of control and start putting every new bit of CSS you write inside a selector so that you don’t accidentally mess up something else. Please don’t fall into this trap. It only makes your CSS worse and will eventually lead to inconsistency in your site styling. Instead, if you can, take the time to make sure you are building a clean and consistent set of styles for your site and use the selectors only when you truly need to override something.

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