Ruby


In a Rails system with a pretty standard user model, views, and controller, I recently found that the users_controller was getting pretty large. There was a pretty significant amount of code that I wanted to add to deal with the users using a different set of views under some circumstances — and I didn’t want to add all of that to the users_controller.rb. What I wanted was to move these functions to a staff_controller.rb, use restful routing on it and just treat the user object differently.

This didn’t work out so well initially. Trying to use form_for in a view always reflects on the type of object you have and sends the results right back to the users_controller — not what I wanted. I tried a bunch of stuff to force the form_for to do what I wanted but no joy. What I wanted I thought was a way to alias the user model.

class Staff < User
end

Problem solved.

The User model had some nice named scopes for finders, for example:

  named_scope
          :recent,
          :conditions => ["created_at > ?", 2.weeks.ago]

And now I can do all of the following

  Staff.first
  Staff.recent
  Staff.find(params[:id])
  @staff = Staff.recent.find(:all,
              :conditions => ['name = ?',"jim"])
  @staff.last_login_at = Time.now
  @staff.save!

.. which since reflection now says they are staff objects, causes
all the view helpers that reflect on stuff to work properly and doesn’t
break any of the ActiveRecord stuff.

The typical Ruby on Rails flash area is only drawn when there is a message to flash. I think something like this is fairly common (this example is haml, but that’s not the point) usually done in a partial, something like app/views/shared/_flash.html.haml:

- %w(info notice error).each do |type|
  - if flash[type.to_sym]
    .warning.flash_message{:class => "#{type.to_s}"}
      %span= h(flash[type.to_sym])
      %small
        %a{:href=>"#", :onclick=>"$$('div.flash_message')[0].hide();return false"} Close this message
 

The problem with this is that if you are Ajaxing a bunch of stuff you could use the flash area for user interaction and highlighting messages, but if it isn’t there, that could be a problem. So how about doing this instead so that the flash area is always drawn, just not displayed:

- %w(info notice error).each do |type|
  .warning.flash_message{ :class => "#{type}", :id => "flash_#{type}", :style => "display: #{flash[type.to_sym] ? 'block' : 'none'}" }
    %span
      - if flash[type.to_sym]
        = h(flash[type.to_sym])
    %small
      %a{:href=>"#", :onclick=>"$$('div.#{type.to_s}')[0].hide();return false"} Close this message

The flash closer is still in there so we don’t have to worry about the complexity of building that again every time, and the message is in a separate span element so it is easy to fill in. So then we can add a little bitty application helper (app/helpers/application_helper.rb)

  def flashnow(page,msg)
    page.select("#flash_notice span").first.replace("#{msg}")
    page.select("#flash_notice").first.show
  end

You might need to change the replace method for replaceHtml
depending on the version of prototype you are working with.
and call it from any view that is building an rjs response:

  flashnow(page,'Yeeha, cowboy, it worked!')