Simple caching


#1

I’m quite a newbie to Ruby and I need some help. I’ve got a database
table called agents that looks something like this:

create table agents {
id mediumint(8) unsigned not null,
surname varchar(60),
forename varchar(60),
manager_id mediumint(8) unsigned,
primary key(id),
constraint fk_manager(manager_id)
references agents(id)
on delete restrict
on update restrict
}

This simple table represents a sales structrue. I’ve generated the model
and I have an Agent Ruby class and added associations has_many agents
and belongs_to agent. I’ve added also a method called full_name that
just concatenates :surname and :forename.

I would now like to have the generated list view besides the surname and
forename of each agent also show the name of the manager ( manager is an
agent where id = agent.manager_id). There are some agents that don’t
have a manager aboven them.

I’ve added a method called manager_full_name to the Agent Ruby class to
avoid too much logic in the view. The code looks something like this:

def manager_full_name
manager = self.agent
manager_full_name = ‘’
manager_full_name = manager.full_name unless manager == Nil
manager_full_name
end

I use the <%= agent.manager_full_name => in my list view but it always
prints out nothing.

How can I print out the name of the manager in the view? Can I use
simple instance variable like @manager_full_name to cach the name of the
manager? I want to avoid an extra query for each time an agent is
printed out.

The ideal way is to set the @manager_full_name instance variable at
creation time (I doesn’t go with the initialize method) and set a new
value when the agent is updated.

Another thing is how to display a structure (tree) of agents with each
agent on the right level. Something like this:

A1
|
A2 A7 A4
| |
A9 A5 A7

It will quickly get ugly in the view… :frowning:

Any suggestions are welkom!

Thans in advance,

Mark


#2

I think you will need to use acts_as_tree (or some other method like
nested_set?) rather than using a circular belongs_to in the same class.
Try this:

class Agent < ActiveRecord::Base
acts_as_tree :foreign_key => “manager_id”

def manager_full_name
manager = self.parent
manager_full_name = ‘’
manager_full_name = manager.full_name unless manager == nil
manager_full_name
end

end

Note: the default foreign_key column to get the parent is parent_id but
we can override that since you called it manager_id. Also, self.agent
in your example becomes self.parent.

There were a couple of typos in your example that you are probably
aware of but here they are anyway: Nil should be lowercase nil and <%=
agent.manager_full_name => should be <%= @agent.manager_full_name %>.

To retrieve the agent AND the parent from the database with a single
query use the :include parameter as follows: @agent = Agent.find(2),
:include => :parent). This will retrieve the agent row for id 2 along
with it’s parent at the same time.

I don’t have a good UI solution for displaying these in heirarchical
order. Maybe someone else has tried it.

-Paul


#3

Thanks a lot Paul, your solution with the acts_as_tree works great. I’ve
found a also a nice article about it on
http://brianfox.wordpress.com/2006/09/08/parent-and-child-assosiations-aka-acts-as-tree/#comment-3693

This code is just enough to get the manager’s name:

manager_full_name = ‘’
manager_full_name = manager.full_name unless manager

I’ve added the ‘:include => :parent’ to the ‘edit’ and ‘show’ action in
the agents_controller. But I don’t know how you can add it to the ‘list’
action when you do a Agent.find(:all). How can you get a list of agents
and at the same time load the parent of each agent.

I think you would have to write a custom SQL select statement if you
want to avoid a query for getting the parent of each agent in the list.

Code for displaying a tree structure in the GUI with ruby must exist
somewhere. Let’s hope I can find it.

Thanks,

Mark


#4

But I don’t know how you can add it to the ‘list’
action when you do a Agent.find(:all). How can you get a list of agents
and at the same time load the parent of each agent.

Just do Agent.find(:all, :include => :parent).

-Paul