ActiveModel::UnknownAttributeError in MessagesController#create - ruby-on-rails

Good day, I am trying to create a chat section on my website but I keep on getting ActiveModel::UnknownAttributeError in MessagesController#create
unknown attribute 'user_id' for Message.
I don't know what I am doing wrong here,
my route is
resources :conversations do
resources :messages
end
Here is my conversations model
class Conversation < ApplicationRecord
belongs_to :sender, foreign_key: :sender_id, class_name: 'User'
belongs_to :recipient, foreign_key: :recipient_id, class_name: 'User'
has_many :messages, dependent: :destroy
validates_uniqueness_of :sender_id, scope: :recipient_id
scope :between, -> (sender_id, recipient_id) do
where("(conversations.sender_id = ? AND conversations.recipient_id =?) OR (conversations.sender_id = ? AND conversations.recipient_id =?)", sender_id, recipient_id, recipient_id, sender_id)
end
end
And in the Message model, app/models/message.rb: I have
class Message < ApplicationRecord
belongs_to :conversation
belongs_to :user
validates_presence_of :body, :conversation_id, :user_id
end
In the Conversations controller, app/controllers/conversations_controller.rb: I have
class ConversationsController < ApplicationController
before_action :authenticate_user!
def index
#users = User.all
#conversations = Conversation.all
end
def create
if Conversation.between(params[:sender_id], params[:recipient_id]).present?
#conversation = Conversation.between(params[:sender_id], params[:recipient_id]).first
else
#conversation = Conversation.create!(conversation_params)
end
redirect_to conversation_messages_path(#conversation)
end
private
def conversation_params
params.permit(:sender_id, :recipient_id)
end
end
I think the main issue is in my conversation index page on the line that links to a message page #message = #conversation.messages.new(message_params)
In the Message controller, app/controllers/messages_controller.rb: I have
class MessagesController < ApplicationController
before_action do
#conversation = Conversation.find(params[:conversation_id])
end
def index
#messages = #conversation.messages
#message = #conversation.messages.new
end
def new
#message = #conversation.messages.new
end
def create
#message = #conversation.messages.new(message_params)
if #message.save
redirect_to conversation_messages_path(#conversation)
end
end
private
def message_params
params.require(:message).permit(:body, :user_id)
end
end
In app/views/conversations/index.html.erb
<h1>My Inbox</h1>
<h1>All Conversations:</h1>
<% #conversations.each do |conversation| %>
<% if conversation.sender_id == current_user.id || conversation.recipient_id == current_user.id %>
<% if conversation.sender_id == current_user.id %>
<% recipient = User.find(conversation.recipient_id) %>
<% else %>
<% recipient = User.find(conversation.sender_id) %>
<% end %>
<h3><%= link_to recipient.email, conversation_messages_path(conversation)%></h3>
<% end %>
<% end %>
<h1>All Users:</h1>
<% #users.each do |user| %>
<% if user.id != current_user.id %><h3>
<%= link_to user.email, conversations_path(sender_id: current_user.id, recipient_id: user.id), method: "post"%></h3>
<% end %>
<% end %>
In app/views/messages/index.html.erb I have
<% #messages.each do |message| %>
<% if message.body %>
<% user = User.find(message.user_id) %>
<%= user.email %><%= message.message_time %>
<%= message.body %>
<% end %>
<% end %>
<%= form_for [#conversation, #message] do |f| %>
<%= f.text_area :body %>
<%= f.text_field :user_id, value: current_user.id, type: "hidden" %>
<%= f.submit "Send Reply" %>
<% end %>
I would appreciate if anyone can guild me on what to do right and is this a good way to setup a chat system?

Related

Add notification to RoR messaging inbox system

I have a rails app with intern messaging system. User can send message to other user. Example : User 1 send message User 2 can respond viceversa.
All works perfectly. But I want to upgrade this system with notfications functionnality. I want to type of notifications :
1) On navbar
2) By mail
Problem : I dont know how i can do this.
Can you help me ?
Conversations table
class CreateConversations < ActiveRecord::Migration
def change
create_table :conversations do |t|
t.integer :sender_id
t.integer :recipient_id
t.timestamps
end
end
end
Messages table. In this code I have a boolean :read. I think solution can be here. What do you think about this ?
class CreateMessages < ActiveRecord::Migration
def change
create_table :messages do |t|
t.text :body
t.references :conversation, index: true
t.references :user, index: true
t.boolean :read, :default => false
t.timestamps
end
end
end
conversation.rb
class Conversation < ActiveRecord::Base
belongs_to :sender, :foreign_key => :sender_id, class_name: 'User'
belongs_to :recipient, :foreign_key => :recipient_id, class_name: 'User'
has_many :messages, dependent: :destroy
validates_uniqueness_of :sender_id, :scope => :recipient_id
scope :between, -> (sender_id,recipient_id) do
where("(conversations.sender_id = ? AND conversations.recipient_id =?) OR (conversations.sender_id = ? AND conversations.recipient_id =?)", sender_id,recipient_id, recipient_id, sender_id)
end
end
Message.rb
class Message < ActiveRecord::Base
belongs_to :conversation
belongs_to :user
validates_presence_of :body, :conversation_id, :user_id
def message_time
created_at.strftime("%m/%d/%y at %l:%M %p")
end
end
conversations_controller.rb
class ConversationsController < ApplicationController
before_action :authenticate_user!
# GET /conversations
# GET /conversations.json
def index
#users = User.all
# Restrict to conversations with at least one message and sort by last updated
#conversations = Conversation.joins(:messages).uniq.order('updated_at DESC')
end
# POST /conversations
# POST /conversations.json
def create
if Conversation.between(params[:conversation][:sender_id], params[:conversation][:recipient_id]).present?
#conversation = Conversation.between(params[:conversation][:sender_id], params[:conversation][:recipient_id]).first
else
#conversation = Conversation.create!(conversation_params)
end
redirect_to conversation_messages_path(#conversation)
end
private
# Use callbacks to share common setup or constraints between actions.
def conversation_params
params.require(:conversation).permit(:sender_id, :recipient_id)
end
end
messages_controller.rb
class MessagesController < ApplicationController
before_action do
#conversation = Conversation.find(params[:conversation_id])
end
def index
#messages = #conversation.messages
if #messages.length > 10
#over_ten = true
#messages = #messages[-10..-1]
end
if params[:m]
#over_ten = false
#messages = #conversation.messages
end
if #messages.last
if #messages.last.user_id != current_user.id
#messages.last.read = true;
end
end
#message = #conversation.messages.new
end
def new
#message = #conversation.messages.new
end
def create
#message = #conversation.messages.new(message_params)
if #message.save
redirect_to conversation_messages_path(#conversation)
end
end
private
def message_params
params.require(:message).permit(:body, :user_id)
end
end
/conversations/index.html.erb
<div class="ui segment">
<h3>Mailbox</h3>
<div class="ui list">
<div class="item">
<% #conversations.each do |conversation| %>
<% if conversation.sender_id == current_user.id || conversation.recipient_id == current_user.id %>
<% if conversation.sender_id == current_user.id %>
<% recipient = User.find(conversation.recipient_id) %>
<% else %>
<% recipient = User.find(conversation.sender_id) %>
<% end %>
Conversation avec <%= link_to recipient.prenom, conversation_messages_path(conversation)%>
<% end %>
<% end %>
</div>
</div>
</div>
<div class="ui segment">
<h3>All Users</h3>
<div class="ui list">
<% #users.each do |user| %>
<% if user.id != current_user.id %>
<div class="item">
<%= user.prenom %> <%= button_to 'Message me', conversations_path(conversation: { sender_id: current_user.id, recipient_id: user.id }), class: 'btn btn-primary m-t' %>
</div>
<% end %>
<% end %>
</div>
</div>
messages/index.html.erb
<% if #over_ten %>
<%= link_to 'Show Previous', "?m=all" %>
<% end %>
<div class="ui segment">
<% #messages.each do |message| %>
<% if message.body %>
<% user = User.find(message.user_id) %>
<div class="item">
<div class="content">
<div class="header"><strong><div class="imageavatarmessage"><%= image_tag user.avatar(:thumb), class:"imageavatarmessage" %></div><%= user.prenom %></strong> <%= message.message_time %></div>
<div class="list">
<div class="item">
<i class="right triangle icon"></i>
<%= message.body %>
</div>
</div>
</div>
</div>
<% end %>
<% end %>
</div>
<%= form_for [#conversation, #message], html: {class: "ui reply form"} do |f| %>
<div class=”field”>
<%= f.text_area :body, class: "form-control" %>
</div>
<%= f.text_field :user_id, value: current_user.id, type: "hidden" %>
<div>
<%= f.submit "Add Reply", class: "ui blue labeled submit icon button" %>
</div>
<% end %>
I would recommend you to go through this tutorial to build nice notification system
https://www.devwalks.com/lets-build-instagram-part-6-notifications/
Basically, you have to create new model, set dependencies with your Message model and integrate to the controller
For the email notifications, it's even simpler.
Just create new mailer and fire it on create action in messages_controller
def create
#message = #conversation.messages.new(message_params)
if #message.save
SendMessageMailer.new_message(#message).deliver_later
redirect_to conversation_messages_path(#conversation)
end
end
EDIT:
To create mailer, you should do something like this:
rails g mailer SendMessage
Go to /app/mailers/send_message_mailer.rb and add action, same type of building as controllers
def new_message(message)
#message = message
mail(to: #message.user.email, subject: 'Hey! Here is what you missed')
end
Also, create an view (email template) and code with erb code
app/views/send_message_mailer/new_message.html.erb
Im not going deep into this, I guess you can figure out how to pass interval (let's say don't send if user is online or have read the message) and differentiate with receiver/sender users
Mailer, once again, is just same type of controller. You can pass as many params as you need and use model nesting inside mailer controller and views
mailer
def new_message(message, sender, receiver)
end
controller
SendMessageMailer.new_message(#message, #message.user, params[:receiver]).deliver_later

ArgumentError in Messages#index while building conversation + messages model

For a marketplace that I'm working on I'd like to add messaging-functionality between users.
I found this tutorial that matches my requirements and I'm trying to replicate it.
When trying to send a message to another user however, I get the following error:
ArgumentError in Messages#index
Showing /home/ubuntu/workspace/marketplace/app/views/messages/index.html.erb where line #9 raised:
First argument in form cannot contain nil or be empty
From the Message view file it's relating to:
<%= form_for [#conversation, #message] do |f| %>
<%= f.text_area :body %>
<%= f.text_field :user_id, value: current_user.id %>
<%= f.submit "Add Reply" %>
A conversation_id was made, but in Message view file it goes wrong.
Does anybody have a clue what I'm missing? I'm quite new to Rails and I'm blind-staring on this for 2 nights now. Your help is much appreciated!
Routes.rb
resources :conversations do
resources :messages
end
Conversation Model:
class Conversation < ApplicationRecord
belongs_to :sender, :foreign_key => :sender_id, class_name: "User"
belongs_to :recipient, :foreign_key => :recipient_id, class_name: "User"
has_many :messages, dependent: :destroy
validates_uniqueness_of :sender_id, :scope => :recipient_id
scope :between, -> (sender_id, recipient_id) do
where("(conversations.sender_id = ? AND conversations.recipient_id = ?) OR (conversations.sender_id = ? AND conversations.recipient_id = ?)", sender_id, recipient_id, recipient_id, sender_id)
end
end
Message Model:
class Message < ApplicationRecord
belongs_to :conversation
belongs_to :user
validates_presence_of :body, :conversation_id, :user_id
def message_time
created_at.strftime("%_d %b %Y at %k:%M")
end
end
Conversations Controller:
class ConversationsController < ApplicationController
before_action :authenticate_user!
def index
#users = User.all
#conversations = Conversation.all
end
def create
if Conversation.between(params[:sender_id],params[:recipient_id]).present?
#conversation = Conversation.between(params[:sender_id], params[:recipient_id]).first
else
#conversation = Conversation.create!(conversation_params)
end
redirect_to conversation_messages_path(#conversation)
end
private
def conversation_params
params.permit(:sender_id, :recipient_id)
end
end
Messages Controller:
class MessagesController < ApplicationController
before_action do
#conversation = Conversation.find(params[:conversation_id])
end
def index
#messages = #conversation.messages
end
def new
#message = #conversation.messages.new
end
def create
#message = #conversation.messages.new(message_params)
if #message.save
redirect_to conversation_messages_path(#conversation)
end
end
private
def message_params
params.require(:message).permit(:body, :user_id)
end
end
Conversation index page
<h3>Mailbox</h3>
<% #conversations.each do |conversation| %>
<% if conversation.sender_id == current_user.id || conversation.recipient_id == current_user.id %>
<% if conversation.sender_id == current_user.id %>
<% recipient = User.find(conversation.recipient_id) %>
<% else %>
<% recipient = User.find(conversation.sender_id) %>
<% end %>
<%= link_to recipient.firstname, conversation_messages_path(conversation)%>
<% end %>
<% end %>
<h3>All Users</h3>
<% #users.each do |user| %>
<% if user.id != current_user.id %>
<%= user.firstname %> <%= link_to "Message me!", conversations_path(sender_id: current_user.id, recipient_id: user.id), method: "post"%>
<% end %>
<% end %>
Messages index page
<% #messages.each do |message| %>
<% if message.body %>
<% user = User.find(message.user_id) %>
<p><%= user.firstname %> and <%= message.message_time %></p>
<p><%= message.body %></p>
<% end %>
<% end %>
<%= form_for [#conversation, #message] do |f| %>
<%= f.text_area :body %>
<%= f.text_field :user_id, value: current_user.id %>
<%= f.submit "Add Reply" %>
<% end %>
You did not define the #message variable in your MessagesController#index action. Please define it like this
def index
#messages = #conversation.messages
#message = #conversation.messages.new
end

Ruby on rails - display error message for an associated object

I have the below form written for a movie review. Review is associated with movies. I am not able to display error message for this form. I have written similar code to display error message for validation on movies and that works well.
Code for the 'add new review' form -
<%= form_for([#movie, #movie.reviews.build]) do |f| %>
<% if #reviews.errors.any? %>
<div id="error_explanation">
<h3>
<%= pluralize(#reviews.errors.count, "error") %> prohibited
this review from being saved:
</h3>
<ul>
<% #reviews.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :reviewer %><br>
<%= f.text_field :reviewer %>
</p>
<p>
<%= f.label :comment %><br>
<%= f.text_area :comment %>
</p>
<p>
<%= f.submit %>
</p>
<% end %>
Models -
MOVIE -
class Movies < ActiveRecord::Base
has_many :reviews, dependent: :destroy
validates_associated :reviews
validates :title, presence: true
validates :rating, presence: true, numericality: { greater_than: 0, less_than_or_equal_to: 10 }
end
REVIEW -
class Review < ActiveRecord::Base
belongs_to :movies
validates :comment, presence: true
end
Adding code for reviews_controller.erb
class ReviewsController < ApplicationController
def create
#movie = Movies.find(params[:movie_id])
#review = #movie.reviews.create(review_params)
redirect_to movies_path(#movie)
end
def destroy
#movie = Movies.find(params[:movie_id])
#review = #movie.reviews.find(params[:id])
#review.destroy
redirect_to movies_path(#movie)
end
private
def review_params
params.require(:review).permit(:reviewer, :comment)
end
end
Here is my code for movies_controller.erb
class MoviesController < ApplicationController
def new
#movie = Movies.new
end
def index
#movies = Movies.all
end
def show
#movie = Movies.find(params[:id])
end
def edit
#movie = Movies.find(params[:id])
end
def create
#movie = Movies.new(movie_params)
if #movie.save
redirect_to #movie
else
render 'new'
end
end
def update
#movie = Movies.find(params[:id])
if #movie.update(movie_params)
redirect_to #movie
else
render 'edit'
end
end
def destroy
#movie = Movies.find(params[:id])
#movie.destroy
redirect_to movies_path
end
private
def movie_params
params.require(:movie).permit(:title,:year,:rating,:description)
end
end
Please help me identify what is going wrong and how to be able to get the validation message working.
Thanks in advance.
You are trying to display error of #review with <% if #reviews.errors.any? %>
But You do not have #reviews in <%= form_for([#movie, #movie.reviews.build) do |f| %>
Better solution, through controller assigning #review = Review.new. In this case it should show you errors, if comment field is empty.
def new
#review = Review.new
end
def create
#movie = Movie.find(params[:movie_id])
#review = #movie.reviews.build(review_params)
if #review.save
else
render 'new'
end
end
In Form
<%= form_for ([#movie, #review) do |f| %>

undefined method `first' for #<Article:0x5b0a4d8>

I am creating a blog with tags. Using Mongodb.
I got this error when submitting a new article with tags. And new artcile is not created. If I do not put any tags in the form, the article is created.
NoMethodError in ArticlesController#create
undefined method `first' for #
Rails.root: c:/Sites/blogK
Application Trace | Framework Trace | Full Trace
app/models/article.rb:18:in all_tags='
app/controllers/articles_controller.rb:22:innew'
app/controllers/articles_controller.rb:22:in `create'
article.rb
class Article
include Mongoid::Document
has_many :comments, dependent: :destroy
field :title, type: String
field :text, type: String
embeds_many :taggings
embeds_many :tags
def all_tags=(tags_string)
tag_names = tags_string.split(",").collect{|s| s.strip.downcase}.uniq
new_or_found_tags = tag_names.collect { |name| Tag.find_or_create_by(name: name) }
self.tags = new_or_found_tags
end
def all_tags
self.tags.collect do |tag|
tag.name
end.join(", ")
end
validates :title, presence: true,
length: { minimum: 5 }
end
tag.rb
class Tag
include Mongoid::Document
field :name, type: String
embeds_many :taggings
embeds_many :articles
end
tagging.rb
class Tagging
include Mongoid::Document
embedded_in :article
embedded_in :tag
end
article_controller.rb
class ArticlesController < ApplicationController
http_basic_authenticate_with name: "dhh", password: "secret", except: [:index, :show]
def index
#articles = Article.all
end
def show
#article = Article.find(params[:id])
end
def new
#article = Article.new
end
def edit
#article = Article.find(params[:id])
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
def update
#article = Article.find(params[:id])
if #article.update(article_params)
redirect_to #article
else
render 'edit'
end
end
def destroy
#article = Article.find(params[:id])
#article.destroy
redirect_to articles_path
end
private
def article_params
params.require(:article).permit(:title, :text, :all_tags)
end
end
_form.html.erb
<%= form_for #article do |f| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= f.label :title %><br>
<%= f.text_field :title %>
</p>
<p>
<%= f.label :text %><br>
<%= f.text_area :text %>
</p>
<p><%= f.text_field :all_tags, placeholder: "Tags separated with comma" %></p>
<p>
<%= f.submit %>
</p>
<% end %>

Polymorphic uninitialized constant

having a lot of trouble setting up polymorphic. I have Comments which works fine, but the Links which should work exactly the same has NameError in Links#index. If I try to forum_posts/1/comments all is good but not forum_posts/1/links.
I was following the railscast tutorial http://railscasts.com/episodes/154-polymorphic-association?view=asciicast
Appreciate the help.
Error
NameError in Links#index
uninitialized constant ForumPost::Link
Extracted source (around line #4):
1: <h1>Links</h1>
2:
3: <ul id="links">
4: <% #links.each do |link| %>
5: <li><%= link.display_name %></li>
6: <% end %>
7: </ul>
routes
resources :forum_posts do
resources :comments
resources :links
end
Models
*models/forum_posts.rb*
class ForumPost < ActiveRecord::Base
attr_accessible :content, :display_name, :section, :user_id
has_many :comments, :as => :commentable
has_many :links, :as => :linkable
end
models/comments.rb
class Comment < ActiveRecord::Base
attr_accessible :commentable_id, :commentable_type, :content, :user_id
belongs_to :commentable, :polymorphic => true
end
models/comments.rb
class Links < ActiveRecord::Base
attr_accessible :description, :display_name, :inamge, :linkable_id, :linkable_type, :user_id
belongs_to :linkable, :polymorphic => true
end
Controllers
*controllers/comments_controller.rb*
class CommentsController < ApplicationController
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
def index
#commentable = find_commentable
#comments = #commentable.comments
end
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment])
if #comment.save
flash[:notice] = "Successfully saved comment."
redirect_to :id => nil
else
render :action => 'new'
end
end
end
*controller/links_controller.rb*
class LinksController < ApplicationController
def find_linkable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
def index
#linkable = find_linkable
#links = #linkable.links
end
def create
#linkable = find_linkable
#link = #linkable.links.build(params[:link])
if #link.save
flash[:notice] = "Successfully saved link."
redirect_to :id => nil
else
render :action => 'new'
end
end
end
Views
views/comments/index.html.erb
<h1>Comments</h1>
<ul id="comments">
<% #comments.each do |comment| %>
<li><%= comment.content %></li>
<% end %>
</ul>
<h2>New Comment</h2>
<%= form_for [#commentable, Comment.new()] do |form| %>
<%= form.label :content %><br/>
<%= form.text_area :content, :rows => 5 %><br/>
<%= form.submit "Add comment" %>
<% end %>
views/links/index.html.erb
<h1>Links</h1>
<ul id="links">
<% #links.each do |link| %>
<li><%= link.display_name %></li>
<% end %>
</ul>
<h2>New Link</h2>
<%= form_for [#linkable, Link.new()] do |form| %>
<%= form.label :display_name %><br/>
<%= form.text_area :display_name %><br/>
<%= form.submit "Add link" %>
<% end %>
Your Links model should be placed at app/models/link.rb and should be called Link, not Links.

Resources