views:

135

answers:

4

Something like this:

class Category

   SOME_CATEGORY = find_by_name("some category")

end

Category::SOME_CATEGORY
tried without a problem, but want to know if it is a bad idea, and the reasons if any..

thanks

A: 

What do you want to do?

Maybe:

class Category
  def self.some_category
    Category.find_by_name("some category")
  end
end

So you can call:

Category.some_category
=> <Category#2....>
Lichtamberg
This will do a new database call every time you do Category.some_category, whereas the original question idea will not. I'm not saying either is bad, it's just a note.
Mike Trpcic
A: 

It's not a terrible idea, but it's not really a good one either. It doesn't really fall in line with the way Rails does things. For one thing, you'll end up with a lot of ugly constant code. Too many ALL_CAPS_WORDS and your Ruby starts to look like C++. Bleah.

For another, it's inflexible. Are you going to make one of these constants for every category? If you add a new category two months from now, will you remember to update your Rails code, add a new constant, redeploy it and restart your server?

If it's important to you to be able to access categories very easily, and not repeat DB queries, here's a bit of metaprogramming that'll automatically look them up and create static methods like Lichtamberg's for you on first access:

def self.method_missing(category, *args)  # The 'self' makes this a class method
  @categories ||= {}
  if (@categories[category] = find_by_name(category.to_s))
    class_eval "def self.#{category.to_s}; @categories[#{category}]; end" 
    return @categories[category]
  end
  super
end

With this method in place, whenever you first call Category.ham, it'll create a class method that returns the value of find_by_name("ham") -- so that neither the query nor method_missing() runs again the next time you call it. This is pretty much the way the OpenStruct class works, BTW; look it up in the Pickaxe book if you want to learn more.

(Of course you'll still have the risk that, because these are all memoized, your Rails app won't reflect any changes you make to your category objects. This makes the assumption that changes won't happen or don't really matter. It's up to you to determine whether that assumption is valid for your app. You could always put an after_update callback in your code that resets @@categories if that's a problem; but at that point this starts to get complicated.)

SFEley
+5  A: 

If you don't want to hit the database each time you'll have to cache the model. There are several ways to do this, but one quick way is using Memoization. This was introduced in Rails 2.2.

class Category < ActiveRecord::Base
  class << self
    extend ActiveSupport::Memoizable
    def named(name)
      find_by_name(name)
    end
    memoize :named
  end
end

Use it like this.

Category.named("some category") # hits the database
Category.named("some category") # doesn't hit the database

The cache should stay persistent across requests. You can reset the cache by passing true as the last parameter.

Category.named("some category", true) # force hitting the database
ryanb