Zedrian
Zedrian

Reputation: 909

Add record to a model upon create used in many models

I have a survey and I would like to add participants to a Participant model whenever a user answers to a question for the first time. The survey is a bit special because it has many functions to answer questions such as Tag words, Multiple choices and Open Question and each function is actually a model that has its own records. Also I only want the Participant to be saved once.

The Participant model is fairly simple:

class Participant < ActiveRecord::Base
  belongs_to :survey
  attr_accessible :survey_id, :user_id
end

The Survey model is also straightforward:

class Survey < ActiveRecord::Base
  ...
  has_many :participants, :through => :users
  has_many :rating_questions, :dependent => :destroy
  has_many :open_questions, :dependent => :destroy
  has_many :tag_questions, :dependent => :destroy
  belongs_to :account
  belongs_to :user

  accepts_nested_attributes_for :open_questions
  accepts_nested_attributes_for :rating_questions
  accepts_nested_attributes_for :tag_questions
  ...
end

Then you have models such as rating_answers that belong to a rating_question, open_answers that belong to open_questions and so on.

So initially I thought for within my model rating_answers I could add after_create callback to add_participant

like this:

class RatingAnswer < ActiveRecord::Base
   belongs_to :rating_question
   after_create :add_participant
   ...
   protected
   def add_participant
     @participant = Participant.where(:user_id => current_user.id, :survey_id => Survey.find(params[:survey_id]))
     if @participant.nil?
       Participant.create!(:user_id => current_user.id, :survey_id => Survey.find(params[:survey_id]))
     end
   end
end

In this case, I didn't know how to find the survey_id, so I tried using the params but I don't think that is the right way to do it. regardles it returned this error

NameError (undefined local variable or method `current_user' for #<RatingAnswer:0x0000010325ef00>):
  app/models/rating_answer.rb:25:in `add_participant'
  app/controllers/rating_answers_controller.rb:12:in `create'

Another idea I had was to create instead a module Participants.rb that I could use in each controllers

module Participants
  def add_participant
    @participant = Participant.where(:user_id => current_user.id, :survey_id => Survey.find(params[:survey_id]))
    if @participant.nil?
      Participant.create!(:user_id => current_user.id, :survey_id => Survey.find(params[:survey_id]))
    end
  end
end

and in the controller

class RatingAnswersController < ApplicationController
  include Participants
  def create
    @rating_question = RatingQuestion.find_by_id(params[:rating_question_id])
    @rating_answer = RatingAnswer.new(params[:rating_answer])
    @survey = Survey.find(params[:survey_id])
    if @rating_answer.save
      add_participant
      respond_to do |format|
        format.js
      end
    end
  end
end

And I got a routing error

ActionController::RoutingError (uninitialized constant RatingAnswersController::Participants):

I can understand this error, because I don't have a controller for participants with a create method and its routes resources

I am not sure what is the proper way to add a record to a model from a nested model and what is the cleaner approach.

Ideas are most welcome!

Upvotes: 0

Views: 63

Answers (2)

Zedrian
Zedrian

Reputation: 909

In the end I ended up using the after_create callback but instead of fetching the data from the params, I used the associations. Also if @participant.nil? didn't work for some reason.

class RatingAnswer < ActiveRecord::Base
   belongs_to :rating_question
   after_create :add_participant
   ...
   protected
   def add_participant
    @participant = Participant.where(:user_id => self.user.id, :survey_id => self.rating_question.survey.id)
    unless @participant.any?
      @new_participant = Participant.create(:user_id => self.user.id, :survey_id => self.survey.rating_question.id)
    end
  end
end

The cool thing with associations is if you have deeply nested associations for instead

Survey has_many questions
Question has_many answers
Answer has_many responses

in order to fetch the survey id from within the responses model you can do

self.answer.question.survey.id

very nifty!

Upvotes: 0

Satya Kalluri
Satya Kalluri

Reputation: 5214

current_user is a helper that's accessible in views/controller alone. You need to pass it as a parameter into the model. Else, it ain't accessible in the models. May be, this should help.

Upvotes: 1

Related Questions