0

I have the following models:

class User < ActiveRecord::Base
  has_many :survey_takings
end

class SurveyTaking < ActiveRecord::Base
  belongs_to :survey

  def self.surveys_taken # must return surveys, not survey_takings
    where(:state => 'completed').map(&:survey)
  end

  def self.last_survey_taken
    surveys_taken.maximum(:position) # that's Survey#position
  end
end

The goal is to be able to call @user.survey_takings.last_survey_taken from a controller. (That's contrived, but go with it; the general goal is to be able to call class methods on @user.survey_takings that can use relations on the associated surveys.)

In its current form, this code won't work; surveys_taken collapses the ActiveRelation into an array when I call .map(&:survey). Is there some way to instead return a relation for all the joined surveys? I can't just do this:

def self.surveys_taken
  Survey.join(:survey_takings).where("survey_takings.state = 'completed'")
end

because @user.survey_takings.surveys_taken would join all the completed survey_takings, not just the completed survey_takings for @user.

I guess what I want is the equivalent of

class User < ActiveRecord::Base
  has_many :survey_takings
  has_many :surveys_taken, :through => :survey_takings, :source => :surveys
end

but I can't access that surveys_taken association from SurveyTaking.last_survey_taken.

Jay Levitt
  • 1,680
  • 1
  • 19
  • 28

2 Answers2

1

If I'm understanding correctly you want to find completed surveys by a certain user? If so you can do:

Survey.join(:survey_takings).where("survey_takings.state = 'completed'", :user => @user)

Also it looks like instead of:

def self.surveys_taken
where(:state => 'completed').map(&:survey)
end

You may want to use scopes:

scope :surveys_taken, where(:state => 'completed')
Kyle Patterson
  • 185
  • 1
  • 10
  • The class method works as well (although I always use class << self), although I'm not sure what the extra `.map(&:survey)` part is for. They're composable like scopes. – Dave Newton Sep 08 '11 at 23:54
  • Well, I can't use scopes for surveys_taken, because that would return survey_takings, not surveys. I can certainly do the `Survey.join` in a controller, but I can't do that in `SurveyTaking.surveys_taken`, because it's a class method, so there is no `@user`. Updating the question as well.. – Jay Levitt Sep 09 '11 at 15:07
  • There are hundreds of ways you can do this. I pointed you in one direction. You didn't describe what exact thing you are trying to accomplish so it is difficult to extrapolate what you want from your broken code. You can use scopes, they don't return arrays and it seems you have an unneeded association. If you are truly just trying to find the completed surveys by a given user you can do that with one line of code, a simple db query, no extra code needed. – Kyle Patterson Sep 12 '11 at 03:04
0

I think what I'm looking for is this:

class SurveyTaking < ActiveRecord::Base
  def self.surveys_taken
    Survey.joins(:survey_takings).where("survey_takings.state = 'completed'").merge(self.scoped)
  end
end

This way, SurveyTaking.surveys_taken returns surveys taken by anyone, but @user.survey_takings.surveys_taken returns surveys taken by @user. The key is merge(self.scoped).

Waiting for further comments before I accept..

Jay Levitt
  • 1,680
  • 1
  • 19
  • 28