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?
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).
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
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! 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).
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?