Caching database data that doesn't change

what is the best way to cache database data that doesn’t change?
I was caching it in a constant that I set in a file in
config/initializers/ . More concretely, I have a file
config/initializers/db_cache_constants.rb that contains the following
line:

CATEGORIES = Category.find :all

But I have 2 problems:

  • If I do a rake db:drop, then rake db:create doesn’t work because it
    tries to run that file, and it fails because there’s no database.

  • when from a template I try to get the number of products of one of the
    cached categories (a category :has_many products),

<% for category in CATEGORIES %>
<%= category.name %> (<%= category.products.size %>)
<% end %>

it works the first time, but when I reload the page, a NoMethodError
occurs…
undefined method ‘products’ for #<Category id: 1, name: “category 1”>

Why is this happening?

Both problems are solved if I move the definition of the constant to the
beginning of the app/controllers/application.rb file, just before the
definition of the ApplicationController class, but…

is there a better place to do this type of caching?

Thanks in advance.

xavi

Since the definition of a ‘class’ in Ruby is actually a method that
runs in the context of that class, you can declare the constant on the
class. It will already have inherited the :find method from
ActiveRecord.

class Category
CATEGORIES = self.find(:all)

end

Any time you need CATEGORIES after that you simply ‘prefix’ it through
the class (Category::CATEGORIES).

AndyV

On Feb 16, 11:59 am, Xavi C. [email protected]

Thanks for your answer, but…
I tried defining the constant in the Category class like you said, but
then for some reason caching doesn’t work: each time I reload the page
that loops through the Category::CATEGORIES array I can see in the logs
that the database is hit.

Category Load (0.000365) SELECT * FROM categories
Category Columns (0.002079) SHOW FIELDS FROM categories

The app is running in the development environment.

xavi

have you tried running in production mode? in development mode all
your models are reloaded with each request, so it will of course
reload the categories. See what happens in production.

On Feb 18, 10:25 am, Xavi C. [email protected]

Thank you all for your answers.

I changed the config.cache_classes option to true in the development
environment (it’s set to false by default) and now the cache works, so
you’re right that the cache didn’t work because of the default class
reloading in development mode.

Fred, using a Category.all_categories method, how would you store the
cached data? in a class variable? in a constant?

Thanks again.

xavi

Frederick C. wrote:

On 16 Feb 2008, at 16:59, Xavi C. wrote:

But I have 2 problems:
<% end %>

it works the first time, but when I reload the page, a NoMethodError
occurs…
undefined method ‘products’ for #<Category id: 1, name: “category 1”>

Why is this happening?

My hunch is that this is because of class reloading in development
mode: after the first request, rails will unload all of your models
(so that changes you make take effect immediately). It will basically
remove all the methods, constants etc… from for example the Category
model. Via your CATEGORIES constant you are still holding onto
instances of that old class, that no longer has any methods. You could
get round this if it was defined in your category.rb file

Personally I’d probably have a Category.all_categories method which
would return cached data

Fred

On 16 Feb 2008, at 16:59, Xavi C. wrote:

But I have 2 problems:
<% end %>

it works the first time, but when I reload the page, a NoMethodError
occurs…
undefined method ‘products’ for #<Category id: 1, name: “category 1”>

Why is this happening?

My hunch is that this is because of class reloading in development
mode: after the first request, rails will unload all of your models
(so that changes you make take effect immediately). It will basically
remove all the methods, constants etc… from for example the Category
model. Via your CATEGORIES constant you are still holding onto
instances of that old class, that no longer has any methods. You could
get round this if it was defined in your category.rb file

Personally I’d probably have a Category.all_categories method which
would return cached data

Fred

On Feb 18, 2008, at 16:27 , Xavi C. wrote:

I changed the config.cache_classes option to true in the development
environment (it’s set to false by default) and now the cache works, so
you’re right that the cache didn’t work because of the default class
reloading in development mode.

Fred, using a Category.all_categories method, how would you store the
cached data? in a class variable? in a constant?

(Salut! :slight_smile: Just in case you are waiting for this, an idiom I normally
use something like:

def self.all_categories
@@all_categories ||= find(:all, :order => ‘name ASC’)
end

So, you always use the class method in client code, and the method
caches the result in a class variable via de ||= trick. Written this
way category loading is delayed until needed (just a remark, it does
not imply it is better).

– fxn

On 18 Feb 2008, at 15:27, Xavi C. wrote:

I would do what Xavier said.