Monday, August 9, 2010

Searching Over Multiple Columns

I am developing an application where I collect system information from various hosts within our network and need to search those hosts. I would like to be able to search across multiple columns using 'or' but then return what matched.

The setup is pretty simple, I have a Host model that has_one OperatingSystem



For each model, I implement a 'search' class method, and for the OperatingSystem model, I implemented it as such:



What this does is construct a query using each value in match_on that says if that field matches the term show that otherwise show null. I then use MySQL's CONCAT_WS method to take each of the fields and combine them separated by a space and name it matched_on. Finally, I use a HAVING clause to filter out results that have a blank value for matched_on.

I then use a simple map to set the accessor on the host with what was matched and return the hosts that were matched.

I realize that this will only work with MySQL (probably), but since I am using it for development and production on this project that's not an issue, and it feels better than looping through the results again and setting matched_on

Thanks to this answer on Stack Overflow for pointing me in the right(?) direction.

Wednesday, August 4, 2010

Sharing Sessions Between Rails 2 and Rails 3 Applications

I have been working on writing new applications in Rails 3 at work, but up until this week, they weren't at a point where I would actually interact with my existing applications since they were just in development. However, this week, I was at a point where I could start working on the integration.

The main thing that needs to happen between applications is I have a central authentication/authorization application that I share the session secret key and the user database with my other applications to serve as a sort-of single sign-on set up. It has worked well enough to this point, but I had always been running applications at approximately the same version of Rails 2 on all of the applications.

The first step is to configure the applications to use the same domain and secret key.

On my rails2 app, I had a config like in config/initializers/session_store.rb:



And then in config/environments/development.rb



Originally in my Rails 3 app, I had something in the config/initializer/session_store.rb like this:



But, I found that it just wasn't taking the session at all. After much combing through the Rails 2 and 3 source to see why, I finally found this in the Changelog:

*Rails 3.0.0 [beta 3] (April 13th, 2010)*

* Renamed config.cookie_secret to config.secret_token and pass it as env key. [JV]


*Rails 3.0.0 [beta 2] (April 1st, 2010)*

* Session store configuration has changed [YK & CL]

    config.session_store :cookie_store, {:key => "..."}
    config.cookie_secret = "fdsfhisdghfidugnfdlg"


OK, that was a pretty quiet, but major change.. and it only caused me a few hours of grief. But, I made those changes..



And in the RC for Rails 3, they have moved the secret token into its own initializer at config/initializers/secret_token.rb. Since I created the application using beta4, I didn't see this.

First issue fixed. When I got that I was able to see the session being passed, but now ran into a new problem from the Rails 2 app.

I got the very useful error message:

ActionController::SessionRestoreError in User sessionsController#new

Session contains objects whose class definition isn\'t available.
Remember to require the classes for all objects kept in the session.
(Original exception: #{const_error.message} [#{const_error.class}])

Some more time spent with irb and I found that it was passing the Rails 3 flash class of ActionDispatch::Flash::FlashHash and Rails 2 didn't know about that since it uses ActionController::Flash::FlashHash, and abstract_store.rb has code to check to make sure that a class that is set in the session is a known, and since the classes are different for flash messages bewteen Rails 2 and Rails 3, this alert was generated.

OK, rather than monkey patching Rails 2 to know about ActionDispatch, I just decided to go the easier route and just not set the flash message. I did get a similar problem with my Rails 2 application setting the flash and getting a similar (but fixed) error message about not having ActionController::Flash::FlashHash, but easily enough fixed by just removing the flash messages.

Ultimately, the solution is going to be to get everything upgraded to Rails 3, but hopefully someone who is going through similar pains will find this useful since the solutions aren't always obvious with error messages and stack traces.

Update: Another solution instead of just removing flash messages, is to use flash.now, which shouldn't put the flash messages in the session.