Model Best Practices.

Ok, although not entirely new to RoR, myself and a colleague have been
"discussing" some best practices for `find`.

The scenario:
- table of users
- table of companies
- a user can belong to multiple companies
- end result is that the list will be used for a select list.

So, if I want to get a list of companies that a particular user has
been with in the last X time frame, which model should have the
specialized `find` (because we may / may not have to do a "GROUP BY"
as well).

Should it be in the users model, because it's all the companies would
have the user in common?

Or:

Should it be in the companies model, because it will be a collection
of companies?

Thanks in adavance

Woops ... I should've also mentioned that the example I gave is quite
a high level rendition.
The reason I'm not using HABTM or another associate is because there
are quite a few other tables between users & companies and some extra
conditions.
Originally we had it all setup through associations but it was just
just too dang slow so doing SQL by hand to significantly speed things
up - the main reason for this is because of the a GROUP BY, ORDER BY
combo we got going on.

But the same principle question applies: which model should be
returning the result?

Based on what you’ve shared, I’d put the finder on Company, since you want a set of companies.

If you've got:

class User < ActiveRecord::Base
  has_and_belongs_to_many :companies
end

class Company < ActiveRecord::Base
  has_and_belongs_to_many :users
end

All you need to do is:

user = User.find(params[:id])
user.companies

You don't need to write a special finder: it's built for you when you
declare the model relationships.

KumaZatheef wrote:

@Brent: If the join was that straight forward then there wouldn't be
such an issue, but as I mentioned in my immediately follow up to the
original post, the join is actually quite complex ... the optimized
SQL is as such: (as displayed with checks in the function)

    query = "SELECT c.id as company_id, c.name as company_name
              FROM entries as e
                INNER JOIN project_tasks AS pt ON pt.id =
e.project_task_id
                INNER JOIN projects as p ON p.id = pt.project_id
                INNER JOIN jobs as j on j.id = p.job_id
                INNER JOIN companies as c ON c.id = j.company_id
              WHERE e.user_id = "+self.id.to_s+" "

    query += " AND entries.start_timestamp >= "+start_timestamp.to_s
+" " if( !start_timestamp.nil? )
    query += " AND entries.end_timestamp <= "+end_timestamp.to_s+"
" if( !end_timestamp.nil? )

    query += " AND c.active = "+is_active.to_s+"
                GROUP BY c.id ORDER BY max( e.start_timestamp ) DESC"

Obviously this more complex than what I originally started polling
about, but this gives insight into the end desired result.

So the poll question remains: should this be in the User model (since
it's a user's list of Companies), or be in the Company model since
that's what it's actually returning.

KumaZatheef wrote:

So the poll question remains: should this be in the User model (since
it's a user's list of Companies), or be in the Company model since
that's what it's actually returning.

In this case, I think it would belong in both places. I would make a
pair of methods, that work together. One in User that asks Company for
a list for itself, and then the method in Company that takes in the
User's id and returns the list.

You wouldn't want to put the method just in Users, because it's stepping
on Company's toes by pulling data from Company's table. On the other
hand, you still want to be able to call user.companies.

class Company < ActiveRecord::Base
  def self.find_for_user(id)
    # excecute the correct SQL, wrapping the results up the ActiveRecord
way
  end
end

class User < ActiveRecord::Base
  def companies
    Company.find_for_user(self.id)
  end
end

Hmmm ... that's a good way to cover possible scenarios.
So the "real" call would be in Company, but Users would access it ....
is that ok (ex: "standard") to have one Model accessing another's
functions ... for some reason I figured that'd be crossing into each
other's territory ...

KumaZatheef wrote:

is that ok (ex: "standard") to have one Model accessing another's
functions ... for some reason I figured that'd be crossing into each
other's territory ...

It is standard to have the models talk to each other. That's what
"has_and_belongs_to_many" and those sorts of relationship declarations
do; they're just meta-programming tools to create methods like the ones
I'm suggesting.

oh yeah, right .... duh.
That makes sense, thanks a bunch for your feedback, much appreciated!!

I'm curious what others have to say as well ... agree / disagree?

KumaZatheef wrote:

oh yeah, right .... duh.
That makes sense, thanks a bunch for your feedback, much appreciated!!

I'm curious what others have to say as well ... agree / disagree?

On Jan 25, 4:30 pm, Brent Miller <rails-mailing-l...@andreas-s.net>

Disagree:

I would put that nasty looking query in the association itself. Sence
that is something that is probably going to be a maintenance issue all
by itsself, Id make it a module -- esp. because I bet you have more then
one of these functions:

Hmmm ... interesting idea of putting it in a Module, never thought of
that.
I'll take a look at the feasibility of doing it that way, within the
context of our existing code.
You're right, we do have more than one of these functions; however,
the others apply to associations between User and other Models (ex:
jobs of a given company).
Any other thoughts / ideas out there?

I'm liking all of these suggestions so far .... thank you so much to
those that have contributed, very helpful, much appreciated!