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).
undefined method `questions' for nil:NilClass
-
06-07-2023 - |
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).
Solution
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:
You'll need to spend some time learning how
mailboxer
works by studying its source code because the documentation is sparse.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 shouldsend
it, like you do in yourMessagesController
:@message = current_user.send_message(.... @question.update_attributes :message_id => @message.id
Hope this helps.