Polymorphic uninitialized constant - ruby-on-rails

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.

Related

ActiveModel::UnknownAttributeError in MessagesController#create

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?

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

Rails unable to send id to update controller

I am building a blog application in which I am trying to have two links in that two links I am sending the post_id to my update controller but it is giving me error .And I want to collect that post_id in my update controller and want to check the "status"(Status is basically the column name in my posts table and by default my status column has a status of pending ) .So when admin click on approve the status of column should change to approve and when admin click on decline the status of column should change to decline also user post should be deleted .Admin can access all the users posts whereas user can access his only post
posts_controller
class PostsController < ApplicationController
before_action :authenticate_user!
def index
#posts = Post.user_post(current_user).order('created_at DESC').paginate(:page => params[:page], :per_page => 5)
end
def new
#post = Post.new
end
def show
#post=find_params
end
def create
#post = Post.new(post_params)
#post.user = current_user
if #post.save
Post.upload(params[:post][:files],#post.id)
redirect_to #post
else
render 'new'
end
end
def edit
#post = find_params
puts "cccccccccc#{params[:commit]}"
Post.up(#post.id,params[:commit])
end
def update
#post = find_params
if #post.update(post_params)
redirect_to #post
else
render 'edit'
end
end
def destroy
#post = find_params
#post.destroy
redirect_to posts_path
end
private
def post_params
params.require(:post).permit(:title, :body)
end
def find_params
Post.find(params[:id])
end
end
posts/_form.html.erb
<%= form_for #post,html: { multipart: true } do |f| %>
<% if #post.errors.any? %>
<div id="errors">
<h2><%= pluralize(#post.errors.count, "error") %> prevented this post from saving:</h2>
<ul>
<% #post.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= f.label :title %><br>
<%= f.text_field :title %><br>
<br>
<%= f.label :body %><br>
<%= f.text_field :body %><br>
<br>
<!-- if you want to upload multiple files at a time -->
<%= f.label :files %><br>
<%= f.file_field :files,:multiple => true %><br>
<br>
<%= f.submit %>
<br>
<% end %>
posts/edit.html.erb
<div id="page_wrapper">
<h1>Edit Post</h1>
<%= render 'form' %>
<br>
</div>
migration
class CreatePosts < ActiveRecord::Migration
def change
create_table :posts do |t|
t.string :title
t.text :body
t.integer :user_id
t.string :status
t.timestamps
end
end
end
posts/show.html.erb
<div id="post_content">
<h1 class="title"><%= #post.title %></h1>
<p class="date">
Submitted <%= time_ago_in_words(#post.created_at) %> Ago
<% if user_signed_in? %>
<%= link_to 'Edit', edit_post_path(#post) %> |
<%= link_to 'Delete', post_path(#post), method: :delete, data: { confirm: 'Are you sure?' } %>
<%= link_to "approve",[:edit,#post] %>
<%= link_to "decline",[:edit,#post] %>
<% end %>
</p>
<p class="body"><%= #post.body %></p>
<div id="comments">
<h2><%= #post.comments.count %> Comments</h2>
<%= render #post.comments %>
<h3>Add a comment:</h3>
<%= render "comments/form" %>
</div>
</div>
post.rb
class Post < ActiveRecord::Base
has_many :documents
has_many :comments, dependent: :destroy
belongs_to :user
validates :title, presence: true, length: {in: 5..15}
validates :body, presence: true,length: {in: 5..200}
def self.up(id,params)
puts "aaaaaaaa#{id}"
puts "bbbbbbbbbbbbb#{params}"
end
def self.user_post(id)
role = User.find_role(id)
if role == 'admin'
Post.all
elsif role == 'user'
Post.where(user_id: id)
elsif role == 'developer'
Post.where(user_id: id)
end
end
def self.icon(extension)
case extension
when 'pdf'
EXTENSION[1]['pdf']
when 'png' || 'jpg' || 'jpeg'
EXTENSION[0]['png']
when 'doc' || 'odt'
EXTENSION[2]['doc']
end
end
####limit to upload files not more than ######
def self.upload(files,post_id)
files.each do |file|
#file_extension=file.content_type.split('/')[1]
doc = Document.new(document: file,post_id: post_id )
#save is a method which will save the content in the database
doc.save!
end
end
end
routes.rb
Rails.application.routes.draw do
root "posts#index"
devise_for :users
resources :posts do
resources :comments
end
resources :uploads
end
There are a bunch of problems with your code.. I'll not try to address them all but the specific problem you are facing..
first you need to generate path for both approve and decline action..
resources :posts do
patch '/approve' => 'posts#approve', as: :approve #posts_approve_path(post) is the route helper for this
patch '/decline' => 'posts#decline', as: :decline #posts_decline_path(post) is the route helper for this
resources :comments
end
this will generate the required routes.
now inside your view wherever you want to approve and decline.
<%= link_to "approve",posts_approve_path(#post.id), method: :patch %>
<%= link_to "decline",posts_decline_path(#post.id), method: :patch %>
In your controller you need to modify find_params as the generated routes will pass :posts_id as the :id param.
def find_params
id = params[:id] || params[:posts_id]
Post.find(id)
end
Now in your controller add 2 new methods to implement the functionality.
def approve
#post = find_params
#post.update_attribute(:status, 'approved') if #post.present?
redirect_to post_path(#post), notice: 'Post approved'
end
def decline
#post = find_params
#post.destroy if #post.present?
redirect_to post_path(#post), notice: 'Post deleted'
end

Rails polymorphic to nested polymorphic Mass Assignment Security Error

New to rails and this one has been troubling me for a while now. All the tutorials on polymorphic associations seem to be only two levels and never a polymorphic model nested in a polymorphic model. really need help on this.
I have a app that has post, comment and link models. Both comments and links are polymorphic as I want to add link urls to both comments and posts, as well as comments to other things, similar to how facebook works. I want to nest the links model so can send as one form. Post and links work but comments and links has an MassAssignmentSecurity error.
Erorr
ActiveModel::MassAssignmentSecurity::Error in CommentsController#create
Can't mass-assign protected attributes: link
Request Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"
J1JfIINrtvax77M3JcbDDWvHFpyGG1ciK1DisGOLu6M=",
"comment"=>{"content"=>"z",
"link"=>{"link_url"=>"z"}},
"commit"=>"Add comment",
"forum_post_id"=>"16"}
Routes
resources :forum_posts do
resources :comments
resources :links
end
resources :comments do
resources :links
end
Forum_Post Model
class ForumPost < ActiveRecord::Base
attr_accessible :content, :links_attributes, :comments_attributes , :link_url
has_many :links, :as => :linkable
has_many :comments, :as => :commentable
accepts_nested_attributes_for :links, :allow_destroy => true #, :reject_if => lambda { |t| t[:link].nil?}
end
Comment Model
class Comment < ActiveRecord::Base
attr_accessible :commentable_id, :commentable_type, :content,:links_attributes, :link_url
belongs_to :commentable, :polymorphic => true
has_many :links, :as => :linkable
accepts_nested_attributes_for :links, :allow_destroy => true #, :reject_if => lambda { |t| t[:link].nil?}
end
Link Model
class Link < ActiveRecord::Base
attr_accessible :description, :image_url, :link_url, :linkable_id, :linkable_type, :title, :link_id
belongs_to :linkable, :polymorphic => true
end
Forum_Post Controller
class ForumPostsController < ApplicationController
...............
def new
#forum_post = ForumPost.new
#link = #forum_post.links.build
respond_to do |format|
format.html # new.html.erb
format.json { render json: #forum_post }
end
end
........
end
Comments Controller
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])
#comment.links.build
if #comment.save
flash[:notice] = "Successfully saved comment."
redirect_to :id => nil
else
render :action => 'new'
end
end
end
Links Controller
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.link.build(params[:link])
if #link.save
flash[:notice] = "Successfully saved link."
redirect_to :id => nil
else
render :action => 'new'
end
end
end
Comment Partial View
<h2>Comments</h2>
<% if commentable.comments.empty? %>
No comments to display.
<% else %>
<% for comment in commentable.comments %>
<%= comment.content %>
<% end %>
<% end %>
<% :link_url %>
<h2>New Comment</h2>
<%= form_for [#commentable, Comment.new] do |f| %>
<div class="field">
<%= f.label :content %><br />
<%= f.text_area :content, :rows => 5 %>
</div>
<%= f.fields_for [#linkable, Link.new] do |link| %>
<%= render :partial => 'links/link', :locals => { :f => link } %>
<% end%>
<div class="actions">
<%= submit_tag "Add comment" %>
</div>
<% end %>
Link Partial View
<h2>Link Previews</h2>
<div class="field">
<%= f.label :link_url %><br />
<%= f.text_field :link_url, :id => "url_field" %>
</div>

partial form for nested models in rails 3

I have two models pages and author, here is the code of model pages:
edit -1
now my models are as follows:
class Page < ActiveRecord::Base
validates :title, :presence => true
belongs_to :author
end
author model:
class Author < ActiveRecord::Base
has_many :pages
end
and my form is as follows:
<%= form_for(#page) do |f| %>
<% if #page.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#page.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #page.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 :body %><br />
<%= f.text_area :body %>
</p>
<p>
<%= f.fields_for :author do |fields| %>
<%= f.label :author %><br />
<%= fields.text_field :author %>
<% end %>
</p>
<p>
<%= f.label :email %><br />
<%= f.text_field :email %>
</p>
<p>
<%= f.label :reference %><br />
<%= f.select(:reference,[['google',1],['yahoo',2],['MSN',3],['Ask',4]]) %>
</p>
<%= f.submit "Submit" %>
<% end %>
and controller :
class PagesController < ApplicationController
def index
#total = Page.count
#pages = Page.find(:all)
end
def show
#page = Page.find(params[:id])
end
def new
#page = Page.new
end
def create
#page = Page.new(params[:page])
if #page.save
redirect_to pages_path, :notice => "The data has been saved!"
else
render "new"
end
end
def edit
#page = Page.find(params[:id])
end
def update
#page = Page.find(params[:id])
if #page.update_attributes(params[:page])
redirect_to pages_path, :notice => "Your post has been updated!"
else
render "edit"
end
end
def destroy
#page = Page.find(params[:id])
#page.destroy
redirect_to pages_path, :notice => "Your page has been deleted!"
end
end
Now when i am submitting the form it is giving me this error:
ActiveRecord::AssociationTypeMismatch in PagesController#create
Author(#40328004) expected, got ActiveSupport::HashWithIndifferentAccess(#32291496)
Rails.root: C:/rorapp
Application Trace | Framework Trace | Full Trace
app/controllers/pages_controller.rb:19:in `new'
app/controllers/pages_controller.rb:19:in `create'
Request
Parameters:
{"utf8"=>"✓",
"authenticity_token"=>"hzBlXsdUjEDDCLp036R8bJBwep6BdATSvJPNwt0M8Dg=",
"page"=>{"title"=>"",
"body"=>"",
"author"=>{"author"=>""},
"email"=>"",
"reference"=>"1"},
"commit"=>"Submit"}
Show session dump
Show env dump
Response
Headers:
None
and one more problem if I add accepts_nested_attributes_for :author to my Page model, then the field is not displayed at all. I really want to accomplish this.. any help?
Apparently this doesn't work in reverse with the association. I'm sorry for the mistake. You could do the following, but then your pages controller is acting on the author, which isn't really appropriate. You could make an author controller though, and include fields_for :pages, to have the author and the first page created at the same time.
class PagesController < ApplicationController
def new
#author = Author.new
#page = #author.pages.new
end
def create
#author = Author.create(params[:author])
end
end
class Author < ActiveRecord::Base
has_many :pages
accepts_nested_attributes_for :pages
end
class Page < ActiveRecord::Base
belongs_to :author
end
<%= form_for(#author, :url => pages_url) do |f| %>
<%= f.text_field :author %>
<%= f.fields_for :pages do |fields| %>
<%= fields.text_area :body %>
<% end %>
<% end %>

Resources