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

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.

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?

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

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 Cheung wrote:

(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

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?

I would do what Xavier said.