Forum: Ruby on Rails can this be done without resorting to sql?

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Giuseppe B. (Guest)
on 2007-01-03 01:47
Hello and happy new year :)

My application has users, memberships, projects, and entries.
Briefly, a project has many entries and users are granted access to such
entries based on a project membership (a full-blown model, not a simple
join table).

class User < ActiveRecord::Base
  has_many :memberships
  has_many :projects, :through => :memberships
  ...

class Membership < ActiveRecord::Base
  belongs_to :user
  belongs_to :project
  ...

class Project < ActiveRecord::Base
  has_many :memberships
  has_many :users, :through => :memberships
  has_and_belongs_to_many :entries
  ...

class Entry < ActiveRecord::Base
  has_and_belongs_to_many :projects
  belongs_to :content, :polymorphic => true
  ...


Now I would like, for example, to get the title of all entries to which
the current user has access to, from all projects she may be part of.
Although each entry can belong to any number of projects, and users can
be members of any number of projects, each entry should be retrieved
only once.
I believe that this can be done in SQL with something like (assuming
that the current user's id is 1):

SELECT DISTINCT title
FROM entries
INNER JOIN entries_projects ON entries.id=entries_projects.entry_id
WHERE project_id IN
(SELECT project_id FROM memberships WHERE user_id=1);

But my question is whether the same can be accomplished in idiomatic
rails style through the AR facilities, something along the lines of
@current_user.projects.entries (which obviously won't work)

Thanks in advance for any advice.
Cheers,
Giuseppe
Giuseppe B. (Guest)
on 2007-01-03 15:06
Hi again... still hoping that someone might have some insight.

As a newbie, I would also appreciate any feedback regarding the post
itself, as in "it's too vague, too obscure, too specific, out-of-place,
etc."

I'll gladly take any well-deserved hammering! :-)

Ciao,
Giuseppe
Paul C. (Guest)
on 2007-01-04 01:45
(Received via mailing list)
Hi Guiseppe,

I found your post well written. I think posts that are more difficult
to respond to tend to languish. I couldn't think of a good Rails way to
do this other than the following. You could add your own find_titles
method to Entry like this:

class Entry < ActiveRecord::Base
  has_and_belongs_to_many :projects
  belongs_to :content, :polymorphic => true

  def self.find_titles(user_id)
    entries = find(:all,
      :select => "distinct(title)",
      :joins => "inner join entries_projects ON
entries.id=entries_projects.entry_id",
      :conditions => [" project_id IN(SELECT project_id FROM
memberships WHERE user_id= ?)", user_id])
    entries.map {|e| e.title}
  end
end

And then you could call it like this:

Entry.find_titles(1)

It makes your code cleaner. Better yet, you could put this code in a
new instance method in User called "titles" and then just call
@user.titles although in the titles method you would want to say
Entry.find_titles(self.id).

-Paul
Giuseppe B. (Guest)
on 2007-01-05 02:00
Hi Paul,
many thanks for the feedback.
I am still trying to get a feel for the language and the framework.
In this case, the way 'find' is used looks a lot like sql broken down in
several pieces, so I wonder what is the advantage over using straight
find_by_sql.

Meanwhile, preliminary testing shows that the following might just work,
although some more testing is needed.

@entries = @current_user.projects.map{|g| g.entries}.flatten.uniq

The flattening trick was inspired by:
http://www.rubyonrailsblog.com/articles/2006/10/04...

Putting together this rather obscure line of code was fun, even though I
still need to develop a *pragmatic* sense of whether it was the right
kind of effort in the first place.

Buona notte,
Giuseppe
Tom L. (Guest)
on 2007-01-11 05:05
(Received via mailing list)
On 1/4/07, Giuseppe B. <removed_email_address@domain.invalid> wrote:
> I am still trying to get a feel for the language and the framework.
> In this case, the way 'find' is used looks a lot like sql broken down in
> several pieces, so I wonder what is the advantage over using straight
> find_by_sql.

One advantage would be that if you use ActiveRecord's with_scope
method (which facilitates many cool DB tricks), and your find is made
within its block, the scope will automatically be applied to it. I
don't think that this happens with find_by_sql, looking at the
with_scope code.

There might be other benefits.

> kind of effort in the first place.
If you know @current_user.projects won't return "a lot" of results,
then this is fine. If this line slows down your page though, you'll
know it's because you're making Ruby do too much of the database
server's work and moving back to the SQL solution will probably help.

> Buona notte,
> Giuseppe

Sincerely,

Tom L.
http://AllTom.com/
http://GadgetLife.org/
This topic is locked and can not be replied to.