Wednesday, November 3, 2010

Lost in Transaction

I am working on an issue tracking system at work. I built in an API so other systems can create events, but I wanted to make sure to minimize duplicate events, and opted to just add a comment in case the same issue was opened twice (or more.)

I started with this test



Then wrote this code:



But it kept failing, which was weird to me, because it seemed right.. it wasn't until I tried it from the development environment and checked out the logs that I saw what was happening..



The whole request was being wrapped in a transaction and being rolled back.. A little google searching later, I found after_rollback, and wrote a new callback



And now everything is working as intended. So if you run into a similar situation, I hope that this will help out.

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.

Tuesday, July 20, 2010

Redcar

I have been using Redcar this morning after hearing about it on the Ruby5 podcast.

I am pretty impressed so far. It has good support for Textmate bundles and works cross-platform. This was an important detail for me since I use OSX at home and Linux at work and it would be nice to use the same editor everywhere. I also like the fact that my editor is written in Ruby and is open source.

Using rvm, I was able to set it up pretty easily:


And then since I don't usually use jruby, I set up a quick bash function to call it:


From there I can just call rc [dir] from the command line and it opens the project.

I did have one small issue with installing the ruby-shoulda bundle. The wiki says that if you put bundles in  ~/.redcar/textmate/Bundles and delete the cache, then they should show up. However, I wasn't able to get it to work unless I put in in the system bundle directory at:  ~/.rvm/gems/jruby-1.5.1/gems/redcar-0.3.8.1/textmate/Bundles

I am pretty excited where they can take this project, and possibly even contribute to it in the future.

Monday, July 5, 2010

LDAP Authentication With Devise

I have been meaning to give Devise a try for quite a while. This weekend I was finally able to try it out. One of my requirements was to be able to authenticate against an LDAP server. There was a module already written to do this for Devise, but it was only for Rails 2.3.8 and I wanted to start new projects in Rails 3, so I set out to fork the project and re-write it as a Rails 3 gem.

Since this was my first experience with Devise, to write a gem, I spent a lot of time in the ruby debugger going line by line in Devise and Warden to see how things worked and why things were breaking, which usually turned out with me making a stupid mistake.

Eventually I was able to finish up, luckily I had already went through the process of creating a gem with a generator, so I was able to add some basic generators to make the setup process easier.

I made a quick screencast on how to setup a new Rails 3 application and use LDAP authentication:


There's still a lot to do, including locking, registrations, password changes, testing, making a single sign-on solution, and a lot more. Hopefully this is enough to get people up and running for now, though.

You can find it on the rails3 branch on github at: 

Friday, July 2, 2010

Stubbing the User Model

I've been working a lot these days on testing. I try my best to follow a TDD/BDD workflow when developing applications, but I still have bad habits left over from the PHP days where I will start working on something, realize I need something else, and then something else, and before you know it, I am way behind on tests.

Before, I was relying completely on testing with Cucumber, but after speaking to a lot of people at Railsconf, it seemed that a lot of people were using Test::Unit and Shoulda. I decided to give them a try here. I also used Factory Girl as a fixture replacement, like I had done in the past with some other problems.

The project I am working on is a host tracking system. Basically the main model is Host, and the frontline controller is HostsController.

For the Host model, I didn't do anything too outside of what is in the Shoulda README, but the controller took some work. I have a custom written authentication gem that basically just includes the recommended methods (require_user, current_user, logged_in?, etc..) into ApplicationController or whichever controller that needs them, since I am aiming for a single-sign-on type system, I use the same session secret for all applications, and the User model reads from a central database using a separate "establish_connection" call at the top of the model. The database is for the User model that managed through another application.

app/models/user.rb


I want the entire application to be protected, so I use the before filter to require a user (which looks at the current_user method), if current_user is not set to a valid User, they're redirected to the other application that will log them in and set the proper cookies using LDAP and Authlogic (this will require another post sometime.)

app/controllers/application_controller.rb


But since I call an external database, the User isn't getting created properly when setting up the databases for testing. I also have my own tests on the other application to validate that people are logging in properly and that everything is working there. I'd like to just make the assumption that the user is logged in. The absolute easiest way to do this, is to just use mocha to make current_user always return the Factory that was created for my user.

test/factories/users.rb



test/functional/hosts_controller_test.rb



From there, I will be able to start building my functional tests, using different User factories (admin user, normal user, read-only user) and make sure that I am getting the proper result.

Friday, June 18, 2010

Open Source Contributions & MongoDB

I recently attended RailsConf 2010 in Baltimore and the 3 major takeaways I got form it were:
  • The Rails community is awesome. I spent half of my time there just talking to other developers and found the experience to be as rewarding as any of the sessions.
  • Everyone was really pushing for more people to get involved in open source projects. There was a pretty heavy focus on "anyone can contribute."
  • MongoDB and other NoSQL solutions were all the rage. I tried to attend a few talks on it myself, since I was considering this for a project at work
I am really going to try to push myself to write more here about what I've been working on. John Nunemaker made a point in his presentation that writing helps him decompress what he's learned.

I am also going to try to push myself to contribute to more open source projects. Like I said, there was a big focus on this. The first day of the conference I randomly met up with Dr. Nic, and without realizing who he was, asked him if he contributed to any open source projects. He did walk with me and gave me some insight that I shouldn't be afraid or intimidated to contribute. The keynote by Yehuda Katz (who has been one of my heroes lately with not only his work on the Rails core, but his frequent updates to Rails Dispatch and some other screencats he's put out there) also focused on how anyone can and should contribute. So, hopefully, in the very near future I'll have some stuff up on github for people to take a look at.

Lastly, I've been working with the stuff I learned from the presentations (here and here), as well as the Birds of a Feather session, where a small group of us sat and peppered John Nunemaker with questions about MongoDB and MongoMapper, which was very helpful.

I've been working with both MongoMapper and Mongoid and have found them to be both easy and powerful to use. I hope to have an app that's using it in production soon, but there's still more reading I have to do to optimize it.