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.