ActiveRecord is awesome. Its dynamic finder methods, scopes, and effortless joins make working with a database a snap. But there are always those times when writing raw sql makes more sense. In my case, I was joining across several tables, and then sorting by a value from the joined table. The problem was that my views were written to handle an ActiveRecord object, not a simple hash, which is what a find_by_sql query returns.
Before:
#controller: @posts = Post.find(:all) #view: < % @posts.each do |post| %> < %= post.title %> < %= post.body %> < %= post.author %> < %= post.date %> < % end %>
After:
#controller: @posts = ActiveRecord::Base.connection.execute("SELECT * FROM posts") # @posts.class #=> Hash # @posts[0].title will return a method missing error
For the view you have two choices.
1)Rewrite the method calls:
< % @posts.each do |post| %> < %= post['title'] %> < %= post['body'] %> < %= post['author'] %> < %= post['date'] %> < % end %> #this is tedious
or
2) Hack the hash to make keys act as methods. There are several ways to do this. From a stackoverflow question I asked, someone showed me a module in the Ruby standard library called OpenStruct:
#controller require 'ostruct' @posts = OpenStruct.new(ActiveRecord::Base.connection.execute("SELECT * FROM posts")) #view < % @posts.each do |post| %> < %= post.title %> < %= post.body %> < %= post.author %> < %= post.date %> < % end %>
Other ways to do this (from stackoverflow answerers):
- define the method_missing function for the
Hashclass - define the method_missing function for just the hash instance you’re dealing with
You can try these out with a really cool pastebin that actually runs the code:
It’s interesting that all of these solutions used about 5mb of memory according to the ideone.com.
Comments 2
Hi,
What version of Rails are you using? I’ve never known find_by_sql to return anything other than an array of model objects. I don’t even see how it’s possible @posts could be a hash as they would be unordered and you also subsequently access it via an integer key (which is possible but unusual for a Hash).
Regards,
Posted 27 Jan 2010 at 7:46 am ¶Andrew
2.3.3–but that shouldn’t have anything to do with it.
You’re absolutely right, any finder method will return an array of objects. And you’re right again about find_by_sql. I was probably thinking of ActiveRecord::Base.connection.execute(’SELECT * FROM users’) (source=http://www.rabbitcreative.com/2007/06/08/executing-raw-sql-in-rails/) which returns mysql objects whose attributes are returned using a hash.
But thanks for the correction–I updated the post.
Posted 28 Jan 2010 at 1:10 am ¶Post a Comment