Question

I am getting a NoMethodError undefined method `questions' for nil:NilClass. It's pointing to the create action on the line @question = @conversation.questions.build(params[:question])

Questions controller:

  respond_to :js, :html

   def index
      @questions = Question.all
      respond_with(@questions)
  end

  def show
    @question = Question.find(params[:id])
    @questions = Question.order("created_at DESC")
    respond_with(@questions)
  end

  def new
    @question = Question.new
    respond_with(@question)
  end

  def create
      @question = @conversation.questions.build(params[:question])
      if @question.save
        @message = current_user.messages.new(:subject => "You have a question from #{@question.sender_id}",
                               :notification_id => @question.sender_id,
                               :receiver_id => @question.recipient_id,
                               :body => @question.question)

        @question.message = @message
        @question.save
        redirect_to questions_path, notice: 'Your question was saved successfully. Thanks!'
      else
        render :new, alert: 'Sorry. There was a problem saving your question.'
      end
    end
  end

Conversations controller:

       helper_method :mailbox, :conversation
        before_filter :conversation, only: :show

     def index
        @conversations ||= current_user.mailbox.inbox.all
        end

      def reply
        current_user.reply_to_conversation(conversation, *message_params(:body, :subject))
        redirect_to conversation
      end

      def trash_folder
        @trash ||= current_user.mailbox.trash.all 
        end

      def trash
        conversation.move_to_trash(current_user)
        redirect_to :conversations
        end

      def untrash
        conversation.untrash(current_user)
        redirect_to :conversations
        end

        def empty_trash
          current_user.mailbox.trash.each do |conversation|    conversation.receipts_for(current_user).update_all(:deleted => true)
          end
         redirect_to :conversations
        end
      end

  private

  def mailbox
   @mailbox ||= current_user.mailbox
  end

  def conversation
   @conversation ||= mailbox.conversations.find(params[:id])
  end

  def conversation_params(*keys)
   fetch_params(:conversation, *keys)
  end

  def message_params(*keys)
   fetch_params(:message, *keys)
  end

  def fetch_params(key, *subkeys)
   params[key].instance_eval do
     case subkeys.size
     when 0 then self
     when 1 then self[subkeys.first]
     else subkeys.map{|k| self[k] }
     end
   end
  end
end

Messages controller:

 def index
      redirect_to conversations_path(:box => @box)
    end

  # GET /message/new
  def new
    @message = current_user.messages.new
  end

   # POST /message/create
  def create
    @recipient = User.find(params[:user])
    current_user.send_message(@recipient, params[:body], params[:subject])
    flash[:notice] = "Message has been sent!"
    redirect_to :conversations
  end

Questions model:

  attr_accessible :answer, :question, :sender_id, :recipient_id
  belongs_to :user

  belongs_to :sender,
    :class_name => 'User',
    :foreign_key => 'sender_id'
    belongs_to :recipient,
    :class_name => 'User',
    :foreign_key => 'recipient_id'

    belongs_to :message

end

User model:

 acts_as_messageable
  has_many :notifications
  has_many :questions, foreign_key: :recipient_id
  has_many :sent_questions, class_name: 'Question', foreign_key: :sender_id


    def mailboxer_email(object)
        if self.no_email
          email
        else
            nil
        end
    end
end

development log:

Started POST "/questions" for 127.0.0.1 at 2014-05-29 12:32:46 -0400
Processing by QuestionsController#create as HTML
  Parameters: {"utf8"=>"✓", "authenticity_token"=>"YWtv+TixScaYsXpJ6F47uBHkNvcruyHV7cyOtU6pWnQ=", "question"=>{"question"=>"This question should have an conversation id", "sender_id"=>"2", "recipient_id"=>"1"}, "commit"=>"Add Question"}
  User Load (0.3ms)  SELECT `users`.* FROM `users` WHERE `users`.`auth_token` = 'Mqy5_1kyb4hAsrmB9Q0fug' LIMIT 1
   (0.2ms)  BEGIN
  SQL (0.4ms)  INSERT INTO `questions` (`created_at`, `question`, `recipient_id`, `sender_id`, `updated_at`) VALUES ('2014-05-29 16:32:47', 'This question should have an conversation id', 1, 2, '2014-05-29 16:32:47')
   (0.5ms)  COMMIT
WARNING: Can't mass-assign protected attributes for Message: notification_id, reciver_id
    app/controllers/questions_controller.rb:23:in `create'
    app/controllers/application_controller.rb:13:in `user_time_zone'
   (0.2ms)  BEGIN
  User Load (0.5ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 ORDER BY `users`.`id` ASC LIMIT 1
  SQL (0.3ms)  INSERT INTO `notifications` (`body`, `created_at`, `sender_id`, `sender_type`, `subject`, `type`, `updated_at`) VALUES ('This question should have an conversation id', '2014-05-29 16:32:47', 2, 'User', 'You have a question from 2', 'Message', '2014-05-29 16:32:47')
  SQL (0.3ms)  UPDATE `questions` SET `message_id` = 164, `updated_at` = '2014-05-29 16:32:47' WHERE `questions`.`id` = 135
   (0.5ms)  COMMIT
Redirected to http://localhost:3000/questions
Completed 302 Found in 308ms (ActiveRecord: 10.2ms)

This new code is preventing the question from being created in the Questions table. I made the changes to the code because I had the questions submitting to the database but it was not creating a conversation_id with the mailboxer gem inside the Notifications table. Below is the original code that created the question inside the Questions table, but had NULL for conversation_id.

def create
    @question = Question.new(params[:question])
    if @question.save
      @message = current_user.messages.new(:subject => "You have a question from #{@question.sender_id}",
                             :notification_id => @question.sender_id,
                             :reciver_id => @question.recipient_id,
                             :body => @question.question)

      @question.message = @message
      @question.save
      redirect_to questions_path, notice: 'Your question was saved successfully. Thanks!'
    else
      render :new, alert: 'Sorry. There was a problem saving your question.'
    end
  end

So I need help with fixing the undefined method and having the question submit to the database with a conversation_id. I need the conversation_id set so the Question can be sent to the recipients inbox (this is where the user answers their questions).

Was it helpful?

Solution

Your @conversation variable is never set to anything, so it is nil. You need to initialize it to something, either by setting it to Converation.new or retrieving a conversation from the database (which appears to be what you want to do in this case).

OTHER TIPS

Why don't you use a before_create callback in your model:

#app/controllers/conversations_controller.rb
Class ConversationsController < ActiveRecord::Base
    def create
         @conversation = Conversation.new(conversation_params)
         @conversation.save
    end

    private

    def conversation_params
        params.require(:conversation).permit(:conversation, :params, questions: [])
    end
end


#app/models/conversation.rb
Class Conversation < ActiveRecord::Base
    before_create :build_questions

    private
    def build_questions
         self.questions.build
    end
end

You have a @conversation that holds a nil value in your create action of your QuestionsController,because it not set to nothing. Try giving it like this

def create
    @conversation = Conversation.find(params[:id])
    @question = @conversation.questions.build(params[:question])
    if @question.save
      #scoping to the current user is the right thing to do here
      @message = current_user.messages.new(:subject => "You have a question from #{@question.sender_id}",
                             #Original code :sender_id
                             :notification_id => @question.sender_id,
                             #Original code :recipient_id
                             :receiver_id => @question.recipient_id,
                             :conversation_id => @cnversation.id
                             :body => @question.question)

      @question.message = @message
      @question.save
      redirect_to questions_path, notice: 'Your question was saved successfully. Thanks!'
    else
      render :new, alert: 'Sorry. There was a problem saving your question.'
    end
  end

Hope it helps!

Update

I believe you should have a Conversation model to relate the Question model.Create a Conversation model by generating this query

rails g model Conversation

Add a relation has_many questions to the Conversation model

Class Conversation < ActiveRecord::Base

has_many :questions

end

And also You should be adding a belongs_to conversation in your Question model.

Try updating your Question model

Class Question < ActiveRecord::Base

attr_accessible :answer, :question, :sender_id, :recipient_id

belongs_to :user

belongs_to :sender,:class_name => 'User',:foreign_key => 'sender_id'

belongs_to :recipient,:class_name => 'User',:foreign_key => 'recipient_id'

belongs_to :message

belongs_to :conversation

end

As others have said, the error results from never setting the @conversation variable at all.

However, I believe no one who has answered so far realizes that Conversation is a model inside the mailboxer gem: Mailboxer::Conversation. Conversation instances are available from the result of the send_message call. You'd have to say:

receipt = send_message ....
conversation = receipt.conversation
# Now you can save the conversation of this message 
# in some useful place so it can be found later.

The other place conversations are available is in the messageable. I gather that this is your User model and appears as current_user.

You can get all the conversations of the current user with

current_user.mailbox.conversations

You can perform some filtering by choosing a single message queue instead, for example:

current_user.mailbox.inbox

Of course you probably want to get the last conversation. For this, you want

current_user.mailbox.conversations.last

So my wild guess is you might want

@conversation = current_user.mailbox.conversations.last

Another inconsistency in your code is that the basic Mailboxer::Conversation model created by the generator has no questions relation, yet you seem to be assuming there is one when you say

@conversation.questions

To relate conversations to questions, you will have to monkey patch with something like:

class Mailboxer::Conversation < ActiveRecord::Base
  has_many :questions
end

in your own file app/models/conversation.rb. Additionally, this makes sense only if there is a belongs_to :conversation and foreign key conversation_id in the Question model. You don't have either.

In summary, two observations:

  1. You'll need to spend some time learning how mailboxer works by studying its source code because the documentation is sparse.

  2. There is at least the fatal inconsistency in your models described above. Others are likely. You'll have to fix these before you have any hope of success. The way to do this is test the models before moving on to the controllers. Success in Rails is all about getting the models exactly right.

From the mailboxer documentation, I can see that any message has a conversation_id. You claim that the question's conversation_id is not set, so I think your question model also has a link to a conversation?

Yet I do not see that belongs_to in your question model?

So I see a few options:

  • either you should access the conversation through the message, e.g. question.message.conversation
  • either you want the conversation_id of the question to be the same as that of the message, do something like

    @message = current_user.messages.new(...) @question.message = @message @question.conversation_id = @message.conversation_id

You do save the @question but not the @message? Is that the problem?

  • either your @message is not created with a conversation, but maybe you should send it, like you do in your MessagesController :

    @message = current_user.send_message(.... @question.update_attributes :message_id => @message.id

Hope this helps.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top