Rails Comments User + Id - ruby-on-rails

In my Rails app, I have a comments scaffold which lets users comment on a movie.
I am faced with two problems.
The first problem is that anyone can create a comment, even if they are not signed in, how would I assign a comment to a user, so if there is a current_user, they can create a comment and I would be able to assign the user to the comment so <%= comment.user.first_name %>, and if they are not signed in, they cant create a comment. How would i do this? ( I am using devise )
The second problem is that when I create a comment, it takes me to this path (where 12 is :movie_id)
localhost:3000/movies/12/comments/new
This is fine but when i am creating the comment, I have to specify the movie_id (12), how this be done automatically, so rails sees that the movie_id for the comment is 12.
My Comments Controller, incase needed
class CommentsController < ApplicationController
# GET /comments
# GET /comments.json
before_filter :load_movie
def index
#comments = #movie.comments.all
#search = Movie.search(params[:q])
respond_to do |format|
format.html # index.html.erb
format.json { render json: #comments }
end
end
# GET /comments/1
# GET /comments/1.json
def show
#comment = Comment.find(params[:id])
#search = Movie.search(params[:q])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #comment }
end
end
# GET /comments/new
# GET /comments/new.json
def new
#comment = Comment.new
#search = Movie.search(params[:q])
respond_to do |format|
format.html # new.html.erb
format.json { render json: #comment }
end
end
# GET /comments/1/edit
def edit
#comment = Comment.find(params[:id])
#search = Movie.search(params[:q])
end
# POST /comments
# POST /comments.json
def create
#comment = Comment.new(params[:comment])
#search = Movie.search(params[:q])
respond_to do |format|
if #comment.save
format.html { redirect_to :back }
format.json { render json: #comment, status: :created, location: #comment }
else
format.html { render action: "new" }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
# PUT /comments/1
# PUT /comments/1.json
def update
#comment = Comment.find(params[:id])
#search = Movie.search(params[:q])
respond_to do |format|
if #comment.update_attributes(params[:comment])
format.html { redirect_to (#movie), notice: 'Comment was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
# DELETE /comments/1
# DELETE /comments/1.json
def destroy
#comment = Comment.find(params[:id])
#comment.destroy
respond_to do |format|
format.html { redirect_to comments_url }
format.json { head :no_content }
end
end
private
def load_movie
#movie = Movie.find_by_id(:movie_id)
end
end

First:
using devise, you can request that a user is signed in by saying at the top of your controller:
before_filter :authenticate_user!, only: [:new,:create]
so if someone not signed in tries to access these actions, they are redirected to the sign in page and after sign in forwarded to the original request.
Second:
As you can see from the routes, 12 is assigned to params[:movie_id]. So in your controllers new action write:
#movie = Movie.find(params[:movie_id])
#comment = #movie.comments.new
#comment.user=current_user

Related

How to access variables and methods across controllers in Rails

Books Controller:
class BooksController < ApplicationController
before_action :set_book, only: [:show, :edit, :update, :destroy]
# GET /books
# GET /books.json
def index
if params[:student_id]
student = Student.find(params[:student_id])
#books = student.books
else
#books = Book.all
end
respond_to do |format|
format.html
format.csv {render text: #books.to_csv }
end
end
def show
end
def new
#book = Book.new
end
def edit
end
def create
#book = Book.new(book_params)
respond_to do |format|
if #book.save
format.html { redirect_to #book, notice: 'Book was successfully created.' }
format.json { render :show, status: :created, location: #book }
else
format.html { render :new }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #book.update(book_params)
format.html { redirect_to #book, notice: 'Book was successfully updated.' }
format.json { render :show, status: :ok, location: #book }
else
format.html { render :edit }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
end
def destroy
#book.destroy
respond_to do |format|
format.html { redirect_to books_url, notice: 'Book was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_book
#book = Book.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def book_params
params.require(:book).permit(:book_name, :book_level, :total_words, :words_wrong, :self_corrections, :student_id)
end
end
Here is my "Students Controller"
class StudentsController < ApplicationController
def show
#student = Student.find(params[:id]) rescue nil
#books = Book.where(student_id: params[:id])
#book = Book.new
end
def create
student = Student.new(student_parameters)
student.user_id = current_user.id
if student.save
redirect_to student
else
redirect_to 'students#index'
end
end
def index
#students = Student.where("user_id = ?",current_user.id)
#student = Student.new
end
private
def student_parameters
params.require(:student).permit(:first_name, :last_name)
end
end
Books belong to students, and on the index view where I show an individual's students books, I want the heading at the top of the page to say "{current student}'s book". I'm unsure as to how to call the name of the current student, and I think the source of my confusion is the fact that I'm working with the books controller and student.first_name and student.last_name aren't available to me.
Additionally, I would like to know how to access book data when I'm using the students controller. For instance when I'm at localhost:3000/students/2, I'd like to show all that students books.
I'm looking for something like current_student.books or current_student.name, I think, but I'm not sure how to create them.
Instead of doing...
student = Student.find(params[:student_id])
#books = student.books
do...
#student = Student.find(params[:student_id])
#books = #student.books
This gives you the instance variable #student that you can use in your views, in particular #student.first_name and #student.last_name
you may want to condition the code in the view so that it only shows if #student is not nil (it would be nil if params[:student_id] wasn't passed).

Missing Template and Controller Issues in Rails Project (rating)

I am stuck on a problem and i would love a hand! I got the polymorphic association from railscast #154
So far I have gotten the comment model working splendidly. However I want to create a rating model as well that I can then add js to make pretty.
Looked at every other tutorial/stackoverflows I could find. None of them show how to work with multiple polymorphic associations in a controller.
I tried to duplicate the code for the article_controller but I am not sure how to do that for both models. So far my attempts have been unsuccessful. The partial form has the proper name, it is _ratings.html.erb
My Error:
Missing partial ratings/ratings with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :coffee]}. Searched in:
* "/code/jimbo/bigdog/app/views"
* "/home/user/.rvm/gems/ruby-2.0.0-p247#lulla-core/gems/kaminari-0.14.1/app/views"
* "/home/user/.rvm/gems/ruby-2.0.0-p247#lulla-core/gems/devise-3.1.1/app/views"
My Models:
class Rating
include Mongoid::Document
include Mongoid::MultiParameterAttributes
field :value, type: Integer, default: "0"
belongs_to :rateable, polymorphic: true
end
My Controllers:
class RatingsController < ApplicationController
before_filter :load_rateable
def index
#ratings = #rateable.rating
end
def show
#rating = #rateable.ratings.find(params[:id])
respond_to do |format|
format.html # show.html.erb
end
end
def new
#rating = #rateable.rating.new
end
def create
#rating = #rateable.rating.new(params[:rating])
if #rating.save
redirect_to #rateable, notice: "Rating created."
else
render :new
end
end
private
def load_rateable
resource, id = request.path.split('/')[1, 2]
#rateable = resource.singularize.classify.constantize.find(id)
end
# def load_commentable
# klass = [Article, Photo, Event].detect { |c| params["#{c.name.underscore}_id"] }
# #rateable = klass.find(params["#{klass.name.underscore}_id"])
# end
end
Article Controller
class ArticlesController < ApplicationController
# GET /articles
# GET /articles.json
def index
#articles = Article.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #articles }
end
end
# GET /articles/1
# GET /articles/1.json
def show
#article = Article.find(params[:id])
#commentable = #article
#comments = #commentable.comments
#comment = Comment.new
respond_to do |format|
format.html # show.html.erb
format.json { render json: #article }
end
end
# GET /articles/new
# GET /articles/new.json
def new
#article = Article.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #article }
end
end
# GET /articles/1/edit
def edit
#article = Article.find(params[:id])
end
# POST /articles
# POST /articles.json
def create
#article = Article.new(params[:article])
respond_to do |format|
if #article.save
format.html { redirect_to #article, notice: 'Article was successfully created.' }
format.json { render json: #article, status: :created, location: #article }
else
format.html { render action: "new" }
format.json { render json: #article.errors, status: :unprocessable_entity }
end
end
end
# PUT /articles/1
# PUT /articles/1.json
def update
#article = Article.find(params[:id])
respond_to do |format|
if #article.update_attributes(params[:article])
format.html { redirect_to #article, notice: 'Article was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #article.errors, status: :unprocessable_entity }
end
end
end
# DELETE /articles/1
# DELETE /articles/1.json
def destroy
#article = Article.find(params[:id])
#article.destroy
respond_to do |format|
format.html { redirect_to articles_url }
format.json { head :no_content }
end
end
end
My View:
show.html (article)
<%= render "comments/comments" %>
<%= render "comments/form" %>
<%= render "ratings/ratings" %>
<%= render "ratings/form" %>
Routes:
resources :articles do
resources :comments
resources :ratings
end

Add user id to blog post

I have a simple blog application. When posts are added, i want the users id to be added to the post so that when a user is logged in, he can only see his posts. The id i want to add to the post, is the id of the user currently logged in.
How and where could the id attribute be added to the posts model?
Session controller:
class SessionsController < ApplicationController
def new
end
def create
user = User.authenticate(params[:username], params[:password])
if user
session[:user_id] = user.id
redirect_to blog_url, :notice => "Logged in!"
else
flash.now.alert = "Invalid email or password"
render "new"
end
end
def destroy
session[:user_id] = nil
redirect_to root_url, :notice => "Logged out!"
end
end
Application controller:
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :authenticate
helper_method :current_user
private
def current_user
#current_user ||= User.find(session[:user_id]) if session[:user_id]
end
def authenticate
redirect_to log_in_path unless session != nil
end
end
Posts controller:
class PostsController < ApplicationController
# GET /posts
# GET /posts.json
def index
#posts = Post.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #posts }
end
end
# GET /posts/1
# GET /posts/1.json
def show
#post = Post.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #post }
end
end
# GET /posts/new
# GET /posts/new.json
def new
#post = Post.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #post }
end
end
# GET /posts/1/edit
def edit
#post = Post.find(params[:id])
end
# POST /posts
# POST /posts.json
def create
#post = Post.new(params[:post])
post.user_id = session
respond_to do |format|
if #post.save
format.html { redirect_to #post, notice: 'Post was successfully created.' }
format.json { render json: #post, status: :created, location: #post }
else
format.html { render action: "new" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# PUT /posts/1
# PUT /posts/1.json
def update
#post = Post.find(params[:id])
respond_to do |format|
if #post.update_attributes(params[:post])
format.html { redirect_to #post, notice: 'Post was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #post.errors, status: :unprocessable_entity }
end
end
end
# DELETE /posts/1
# DELETE /posts/1.json
def destroy
#post = Post.find(params[:id])
#post.destroy
respond_to do |format|
format.html { redirect_to posts_url }
format.json { head :no_content }
end
end
end
Thanks in advance!
In PostsController
def create
#post = current_user.posts.build(params[:post])
...
end

How do I access an associated model of an associated model?

I have a Topic which has_many Posts. Each Post belongs to a User, and each User has_one Profile.
In my "show" page for a specific Topic, I try to display profile information of the user who created the post:
<% #topic.posts.each do |post| %>
<%= post.user.profile.first_name %>
<% end %>
I get the following error:
undefined method `profile' for nil:NilClass
Any idea why it does not allow me to access the profile? Please advise.
My Topic controller is as follows:
class TopicsController < ApplicationController
# GET /topics
# GET /topics.json
add_breadcrumb :index, :topics_path
def index
if params[:tag]
#topics = Topic.tagged_with(params[:tag])
else
#topics = Topic.all
end
#newtopic = Topic.new
respond_to do |format|
format.html # index.html.erb
format.json { render json: #topics }
end
end
# GET /topics/1
# GET /topics/1.json
def show
#topic = Topic.find(params[:id])
#posts = #topic.posts
#newpost = #topic.posts.build
add_breadcrumb #topic.name
respond_to do |format|
format.html # show.html.erb
format.json { render json: #topic }
end
end
# GET /topics/new
# GET /topics/new.json
def new
add_breadcrumb :new, :topics_path
#topic = Topic.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #topic }
end
end
# GET /topics/1/edit
def edit
#topic = Topic.find(params[:id])
end
# POST /topics
# POST /topics.json
def create
#topic = Topic.new(params[:topic])
#topic.user_id = current_user.id
#topic.last_poster_id = current_user.id
#topic.last_post_at = Time.now
respond_to do |format|
if #topic.save
format.html { redirect_to #topic, notice: 'Topic was successfully created.' }
format.json { render json: #topic, status: :created, location: #topic }
else
format.html { render action: "new" }
format.json { render json: #topic.errors, status: :unprocessable_entity }
end
end
end
# PUT /topics/1
# PUT /topics/1.json
def update
#topic = Topic.find(params[:id])
respond_to do |format|
if #topic.update_attributes(params[:topic])
format.html { redirect_to #topic, notice: 'Topic was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #topic.errors, status: :unprocessable_entity }
end
end
end
# DELETE /topics/1
# DELETE /topics/1.json
def destroy
#topic = Topic.find(params[:id])
#topic.destroy
respond_to do |format|
format.html { redirect_to topics_url }
format.json { head :no_content }
end
end
end
Your error is caused by this line in the show action #topic.posts.build and this line in the view #topic.posts.each. Since you are building a new post in the controller, #topic.posts includes that new record which most probably have the user as nil. So the solution to your problem is to use #posts instead of #topic.posts in your view.
Check your database. Its very likely that in your database there is a post which corresponds to no user. Since the user for that post is none, the profile becomes undefined for nil:NilClass which is user(null).
This happens mostly when you creates the post that belongs to user but then you deletes the user that belongs to that post from database.
The correct way is to impose a constraint in your user model that should be-
class Post
belongs_to :user, :dependent => :destroy
end
So if the user gets deleted, the corresponding posts of that user also get deleted.
Please note that it is not a good practice to directly delete records from database after imposing the relationship between them using tables.

Rails how to edit and delete comments created comments with cookie authication?

I want my visitors to be able to edit or delete their comment up too 5-10 min after they created it.
How should I authenticate this with a session or cookie?
My comment controller:
class CommentsController < ApplicationController
# GET /comments
# GET /comments.xml
# GET /comments/new
# GET /comments/new.xml
def new
#comment = Comment.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #comment }
end
end
# GET /comments/1/edit
def edit
#comment = Comment.find(params[:id])
end
# POST /comments
# POST /comments.xml
def create
#blog = Blog.find(params[:blog_id])
params[:comment][:ip] = request.remote_ip
#comment = #blog.comments.create!(params[:comment])
redirect_to #blog
end
# PUT /comments/1
# PUT /comments/1.xml
def update
#comment = Comment.find(params[:id])
respond_to do |format|
if #comment.update_attributes(params[:comment])
format.html { redirect_to(admin_comments_path, :notice => 'Comment was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #comment.errors, :status => :unprocessable_entity }
end
end
end
# DELETE /comments/1
# DELETE /comments/1.xml
def destroy
#comment = Comment.find(params[:id])
#comment.destroy
respond_to do |format|
format.html { redirect_to(admin_comments_url, :notice => 'Indlæg slettet') }
format.xml { head :ok }
end
end
end
store the saved comment's id in the session and then at the time of delete or update, check the session for the comment's id and compare the current-time with the comment's created_at... this can go in a filter method.
Also, you can move the code of finding the comment with id in a filter and can follow DRY.
Here it goes:
class CommentsController < ApplicationController
before_filter :get_blog
before_filter :get_comment, :only => [:edit, :update, :destroy]
before_filter :authorize_comment, :only => [:edit, :update, :destroy]
private
def get_blog
#blog = Blog.find(params[:blog_id])
end
def get_comment
#comment = Comment.find(params[:id])
end
def authorize_comment
unless #comment
flash[:error] = "Comment Not Found"
redirect_to #blog and return
else
# checks whether the comment is there in sessions' recent_comments
# if true, it means, this comment was created by the same visitor who is now attempting to delete/update it again
if session[:recent_comments].include?(#comment.id)
# now check if the comment is editable w.r.t time or not
if #comment.created_at < 10.minutes.ago
# if true, it means comment can no longer be updated/deleted
# if you wish you can now remove this from the session's recent_comments
session[:recent_comments].delete(#comment.id)
flash[:error] = "Sorry, you can not change this comment now"
redirect_to #blog and return
else
# it means comment can be edited/updated
return true
end
else
flash[:error] = "Sorry, you can not change this comment now"
redirect_to #blog and return
end
end
end
public
def new
#comment = Comment.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #comment }
end
end
def edit
end
def create
params[:comment][:ip] = request.remote_ip
#comment = #blog.comments.create!(params[:comment])
unless session[:recent_comments].is_a?(Array)
session[:recent_comments] = []
end
session[:recent_comments] << #comment.id
redirect_to #blog
end
def update
respond_to do |format|
if #comment.update_attributes(params[:comment])
format.html { redirect_to(admin_comments_path, :notice => 'Comment was successfully updated.') }
format.xml { head :ok }
else
format.html { render :action => "edit" }
format.xml { render :xml => #comment.errors, :status => :unprocessable_entity }
end
end
end
def destroy
#comment.destroy
respond_to do |format|
format.html { redirect_to(admin_comments_url, :notice => 'Indlæg slettet') }
format.xml { head :ok }
end
end
end

Resources