Method_missing error when caching an ActiveRecord model

I have a simple singleton cache that stores ActiveRecord model objects
for me so I’m not constantly hitting the DB for static data. This was
working great until I tried to add a method (any method really) to the
model, e.g:

class Tag << ActiveRecord::Base
def is_main?
logger.debug(“is_main?”)
return some_attribute && some_other_attribute
end
end

This method works fine when called from a unit test:

def test_is_main
tags = Tag.find_menus("__none")
tags.each {|t|
# Hit an accessor works fine
assert t.is_menu?
# Hit my method works fine
assert t.is_main?
}
end

But if I pull the tag from the cache, the attributes are all there, but
the is_main? call gives method_missing, eg:

def index
home_page = MenuCache.home_page
home_page2 = Tag.find_by_id(14)
logger.debug(“ConvsController.index(#{home_page.inspect})”)
logger.debug(“ConvsController.index(#{home_page2.inspect})”)

home_page2.is_main?
home_page.is_main     # <= line #37

end

Yields (before logging edited):

Tag Load (0.010000) SELECT * FROM tags WHERE (tags.id = 14 )
LIMIT 1

ConvsController.index(#<Tag:0x6a69f30 @attributes={“name”=>“Home”,
“updated_at”=>“2006-08-19 15:11:56”, “a_key”=>"__home",
“parent_menu”=>"__none", “created_by”=>“1”, “a_controller”=>"/pages",
“is_login_needed”=>“0”, “id”=>“14”, “list_view”=>“no_pref”,
“show_view”=>“no_pref”, “an_action”=>“index”, “deletable”=>“0”,
“inherit_security_from”=>nil, “ordering”=>“root_id DESC, lft”,
“position”=>“1”, “is_menu”=>“1”, “created_at”=>“2006-08-19 15:11:56”}>)

ConvsController.index(#<Tag:0x6796150 @attributes={“name”=>“Home”,
“updated_at”=>“2006-08-19 15:11:56”, “a_key”=>"__home",
“parent_menu”=>"__none", “created_by”=>“1”, “a_controller”=>"/pages",
“is_login_needed”=>“0”, “id”=>“14”, “list_view”=>“no_pref”,
“show_view”=>“no_pref”, “an_action”=>“index”, “deletable”=>“0”,
“inherit_security_from”=>nil, “ordering”=>“root_id DESC, lft”,
“position”=>“1”, “is_menu”=>“1”, “created_at”=>“2006-08-19 15:11:56”}>)

is_main?()

NoMethodError (undefined method `is_main?’ for #Tag:0x6a69f30):

C:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/active_record/base.rb:1792:in
method_missing' /app/controllers/pages_controller.rb:37:inindex’

So where did my method disappear to?

Thanks in advance, Brittain

Hi~

On Aug 20, 2006, at 4:02 PM, Brittain wrote:

end
assert t.is_main?
logger.debug(“ConvsController.index(#{home_page.inspect})”)

“updated_at”=>“2006-08-19 15:11:56”, “a_key”=>"__home",
NoMethodError (undefined method `is_main?’ for #Tag:0x6a69f30):

C:/ruby/lib/ruby/gems/1.8/gems/activerecord-1.14.4/lib/
active_record/base.rb:1792:in
method_missing' /app/controllers/pages_controller.rb:37:inindex’

So where did my method disappear to?

Thanks in advance, Brittain

Can you show your caching code? How are you storing items in the

cache? Are you using marshal? Also if you run more then one mongrel
or fcgi how will you make sure that you always get the same cache
object to work with?

-Ezra

Ezra Z. wrote:

class Tag << ActiveRecord::Base
tags.each {|t|

Yields (before logging edited):
“position”=>“1”, “is_menu”=>“1”, “created_at”=>"2006-08-19

So where did my method disappear to?

Thanks in advance, Brittain

Can you show your caching code? How are you storing items in the
cache? Are you using marshal? Also if you run more then one mongrel
or fcgi how will you make sure that you always get the same cache
object to work with?

Thanks for the response Ezra. I’ve attached the cache below, they are
simple hashes with object references. We’ve been using a couple others
of similar design, this is the first we’ve ever added methods to the
stored models.

We just discovered a short while ago when we started using
mongrel_cluster (which you appear already aware of) that we CANNOT get
a handle to a specific cache. This is only an issue when we want to do
a cache reload (happens infrequently), otherwise this data is truly
static; however, if you’re a solution for that, we’d welcome hearing
it.

Code below, edited for clarity:

===

require ‘singleton’

class MenuCache
include Singleton

Hash to hold settings

@@MENU_HOME_PAGE = nil
@@MENUS = {} # Hash of all menus

def self.home_page
@@MENU_HOME_PAGE = @@MENU_HOME_PAGE ||
get_menu_by_key(Pref.get_s(“THEME_HOME_PAGE”))
end

protected
def self.get_menu_by_key(a_key)
load_cache if @@MENUS.blank?

return @@MENUS[a_key]

end

Load and cache table data

def self.load_cache
@@MENU_HOME_PAGE = nil

@@MENUS.clear

tags = Tag.find_menus()
tags.each {|t|
  # Store in the global hash
  @@MENUS[t.a_key] = t

  ...
}

end
end

Tried the restart many times without success. I’ll give the marshal
solution a shot and post back.

We’re implementing BackgroundRB for a number of other features so I’ll
make sure to use it to solve the in process problem.

In the meantime, if anyone else has any ideas, I’d welcome hearing
them.

Thanks.

On Aug 20, 2006, at 5:04 PM, Brittain wrote:

to do

end
@@MENU_HOME_PAGE = nil
end
end

Just to get the obvious out of the way, did you restart your

webserver after you made changes to the cached models? That could
make this happen. Other then that I would think this should work
fine. If you need to have a cache like this that gets updated during
the app running you need to put it into its own process that all your
rails backends can talk to. I have caching built into my backgroundrb
plugin[1] that may be useful to you. But your solution should work as
it is. If its still doesn’t work even after a server restart then
perhaps you need to serialize the models before storage and
unserialize them on the way out. Use Marshal.dump(obj) and
Marshal.load(obj) for this.

Cheers-
-Ezra

[1] http://backgroundrb.rubyforge.org