In my app I have a commenting system that's largely based off of this railscast. Now in my models I'm changing the to_param to a random string so the id isn't in the url. But then that breaks commenting.
status.rb
class Status < ActiveRecord::Base
attr_accessible :content, :member_id, :document_attributes, :permalink
belongs_to :member
belongs_to :document
has_many :comments, as: :commentable, dependent: :destroy
before_create :make_it_permalink
accepts_nested_attributes_for :document
def to_param
permalink
end
private
def make_it_permalink
# this can create permalink with random 12 digit alphanumeric
self.permalink = SecureRandom.hex(12)
end
end
statuses_controller.rb
class StatusesController < ApplicationController
before_filter :authenticate_member!, only: [:index, :new, :create, :destroy]
before_filter :find_member
rescue_from ActiveRecord::RecordNotFound do
render file: 'public/404', status: 404, formats: [:html]
end
def index
#statuses = Status.order('created_at desc').page(params[:page]).per_page(21)
respond_to do |format|
format.html # index.html.erb
format.js
end
end
def show
#status = Status.find_by_permalink(params[:id])
#commentable = #status
#comments = #commentable.comments.order('created_at desc').page(params[:page]).per_page(15)
#comment = #commentable.comments.new
respond_to do |format|
format.html # show.html.erb
format.json { redirect_to profile_path(current_member) }
end
end
def new
#status = Status.new
#status.build_document
respond_to do |format|
format.html # new.html.erb
format.json { render json: #status }
format.js
end
end
def create
#status = current_member.statuses.new(params[:status])
respond_to do |format|
if #status.save
#activity = current_member.create_activity(#status, 'created')
format.html { redirect_to :back }
format.json
format.js
else
format.html { redirect_to profile_path(current_member), alert: 'Post wasn\'t created. Please try again and ensure image attchments are under 10Mbs.' }
format.json { render json: #status.errors, status: :unprocessable_entity }
format.js
end
end
end
def destroy
#status = current_member.statuses.find(params[:id])
#activity = Activity.find_by_targetable_id(params[:id])
#commentable = #status
#comments = #commentable.comments
if #activity
#activity.destroy
end
if #comments
#comments.destroy
end
#status.destroy
respond_to do |format|
format.html { redirect_to profile_path(current_member) }
format.json { head :no_content }
end
end
private
def find_member
#member = Member.find_by_user_name(params[:user_name])
end
def find_status
#status = current_member.statuses.find_by_permalink(params[:id])
end
end
comments_controller.rb
class CommentsController < ApplicationController
before_filter :authenticate_member!
before_filter :load_commentable
before_filter :find_member
def index
redirect_to root_path
end
def new
#comment = #commentable.comments.new
end
def create
#comment = #commentable.comments.new(params[:comment])
#comments = #commentable.comments.order('created_at desc').page(params[:page]).per_page(15)
#comment.member = current_member
respond_to do |format|
if #comment.save
format.html { redirect_to :back }
format.json
format.js
else
format.html { redirect_to :back }
format.json
format.js
end
end
end
def destroy
#comment = Comment.find(params[:id])
respond_to do |format|
if #comment.member == current_member || #commentable.member == current_member
#comment.destroy
format.html { redirect_to :back }
format.json
format.js
else
format.html { redirect_to :back, alert: 'You can\'t delete this comment.' }
format.json
format.js
end
end
end
private
# def load_commentable
# resource, id = request.path.split('/')[1,2] # photos/1/
# #commentable = resource.singularize.classify.constantize.find(id) # Photo.find(1)
# end
# alternative option:
def load_commentable
klass = [Status, Medium, Project, Event, Listing].detect { |c| params["#{c.name.underscore}_id"] }
#commentable = klass.find(params["#{klass.name.underscore}_id"])
end
#def load_commentable
# #commentable = params[:commentable_type].camelize.constantize.find(params[:commentable_id])
#end
def find_member
#member = Member.find_by_user_name(params[:user_name])
end
end
The problem lies in the load_commentable method in the comments_controller. I've tried a couple different variations of the method but the second one works best for my app and it was working when the url's had their id's in them. But since I overwrote the to_param to use my random permalink commenting stopped working because it's trying to find theid where it equals the permalink. Since it seems to try to find the id through the url, how do I pass the the actual id and not the permalink or how do I find commentable by it's permalink instead of id?
It's hard to tell if your param will always be the value of id or always be the permalink, or will sometimes be an id and sometimes a permalink.
If it will always be a permalink, then do:
#commentable = klass.find_by_permalink(params["#{klass.name.underscore}_id"])
instead of
#commentable = klass.find(params["#{klass.name.underscore}_id"])
If it is sometimes id and sometimes other, then you will need make logic to determine which is needed based on the class.
Related
I'm trying to send emails through my actionmailer to the commentable owners' email after another user writes them a comment but I keep getting an error. Can someone help me out with this? Thanks in advance.
comment_mailer.rb
def email_notification(member, comment)
#member = commentable.member
#sender = comment.member
mail to: commentable.member.email, subject: "#{comment.member.full_name} (#{comment.member.user_name}) has left you a comment"
end
comment.rb
belongs_to :member
belongs_to :commentable, polymorphic: true
attr_accessible :content
after_create :send_email
def send_email
CommentMailer.email_notification(member, comment).deliver
end
error
undefined local variable or method `comment' for #<Comment:0x51c2ad8>
app/models/comment.rb:18:in `send_email'
app/controllers/comments_controller.rb:20:in `block in create'
app/controllers/comments_controller.rb:19:in `create'
comments_controller
before_filter :authenticate_member!
before_filter :load_commentable
before_filter :find_member
def index
redirect_to root_path
end
def new
#comment = #commentable.comments.new
end
def create
#comment = #commentable.comments.new(params[:comment])
#comments = #commentable.comments.order('created_at desc').page(params[:page]).per_page(15)
#comment.member = current_member
respond_to do |format|
if #comment.save
format.html { redirect_to :back }
format.json
format.js
else
format.html { redirect_to :back }
format.json
format.js
end
end
end
def destroy
#comment = Comment.find(params[:id])
respond_to do |format|
if #comment.member == current_member || #commentable.member == current_member
#comment.destroy
format.html { redirect_to :back }
format.json
format.js
else
format.html { redirect_to :back, alert: 'You can\'t delete this comment.' }
format.json
format.js
end
end
end
private
def load_commentable
klass = [Status, Medium, Project, Event, Listing].detect { |c| params["#{c.name.underscore}_id"] }
#commentable = klass.find(params["#{klass.name.underscore}_id"])
end
def find_member
#member = Member.find_by_user_name(params[:user_name])
end
You error is in the send_email method. There is no local variable called comment. You are already inside an instance of comment, the instance you want to send an email about. So you want to use the keyword self instead.
Change this:
def send_email
CommentMailer.email_notification(member, comment).deliver
end
To this:
def send_email
CommentMailer.email_notification(member, self).deliver
end
self refers to the current instance of comment, which you want to use for your mailer.
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
I installed Authlogic gem, set up controllers and models. When I make a registration of new account with Authlogic, everything is successfully created in the DB table (like crypted_password, password_salt etc).
The components: UserSessionsController:
# encoding: utf-8
class UserSessionsController < ApplicationController
def new
#user_session = UserSession.new
end
def create
#user_session = UserSession.new(params[:user_session])
respond_to do |format|
if #user_session.save
format.html { redirect_to(root_url, :flash => { success: 'Successfully logged in.'}) }
format.xml { render :xml => #user_session, :status => :created, :location => #user_session }
else
format.html { #render :action => :new
redirect_to :back
flash[:warning] = 'Wrong credentials.'
}
format.xml { render :xml => #user_session.errors, :status => :unprocessable_entity }
end
end
end
def signin
#user_session = UserSession.new
end
def destroy
#user_session = UserSession.find
#user_session.destroy
respond_to do |format|
format.html { redirect_to(root_url, :notice => 'Logged out.') }
format.xml { head :ok }
end
end
end
UsersController
class UsersController < ApplicationController
filter_access_to :all
def index
#users = User.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #users }
end
end
def show
#user = User.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #user }
end
end
def signup
#user = User.new
#categories = Category.order('name')
end
def new
#user = User.new
#categories = Category.order('name')
respond_to do |format|
format.html # new.html.erb
format.json { render json: #user }
end
end
def edit
#user = User.find(params[:id])
#categories = Category.order('name')
redirect_to root_url unless current_user.id == #user.id
end
def create
params[:user][:id_code] = User.random_numbers
params[:user][:url_name] = params[:user][:name].parameterize
#user = User.new(params[:user])
respond_to do |format|
if #user.save
Assignment.create(:user_id => #user.id, :role_id => MEMBER)
format.html { redirect_to root_url, notice: 'User was successfully created.' }
format.json { render json: #user, status: :created, location: #user }
else
format.html { render action: "new" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def update
#user = User.find(params[:id])
respond_to do |format|
if #user.update_attributes(params[:user])
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
def destroy
#user = User.find(params[:id])
#user.destroy
respond_to do |format|
format.html { redirect_to users_url }
format.json { head :no_content }
end
end
end
application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery
before_filter :mailer_set_url_options
before_filter :set_current_user
helper_method :current_user_session, :current_user
def mailer_set_url_options
ActionMailer::Base.default_url_options[:host] = request.host_with_port
end
protected
def set_current_user
Authorization.current_user = current_user
end
private
def current_user_session
logger.debug "ApplicationController::current_user_session"
return #current_user_session if defined?(#current_user_session)
#current_user_session = UserSession.find
end
def current_user
logger.debug "ApplicationController::current_user"
return #current_user if defined?(#current_user)
#current_user = current_user_session && current_user_session.user
end
def authentication
logger.debug "ApplicationController::authentication"
unless current_user
#store_location
flash[:warning] = "You must be logged out to access this page"
redirect_to root_url
return false
end
end
end
user_session.rb
class UserSession < Authlogic::Session::Base
# attr_accessible :data, :sessions_id
generalize_credentials_error_messages "Login info is invalid!"
def to_key
new_record? ? nil : [ self.send(self.class.primary_key) ]
end
self.logout_on_timeout = true
def persisted?
false
end
end
user.rb
class User < ActiveRecord::Base
attr_accessible #list of all columns
acts_as_authentic do |c|
c.login_field = 'email'
c.logged_in_timeout(15.minutes)
end
has_many :assignments
has_many :roles, :through => :assignments
#validates...
# declarative_authentications
def role_symbols
roles.map do |role|
role.name.underscore.to_sym
end
end
end
I am trying to solve this issue already second day, but I still cannot find the problem... In the DB table are all data needed for Authlogic created, but when I try to log in, I always get the error message Wrong credentials..
Could anyone help me, please, with this problem? I already have no idea, how to fix it :/
Ok, problem seems to be solved - I used for my purposes the column name active, which using also Authlogic.
Renamed to activity and everything is working well.
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
class CommentsController < ApplicationController
def create
#contact = Contact.find(params[:contact_id])
#comment = #contact.comments.create(params[:comment])
respond_to do |format|
format.html { redirect_to contact_path(#contact) }
format.js
end
end
def destroy
#contact = Contact.find(params[:contact_id])
#comment = #contact.comments.find(params[:id])
#comment.destroy
respond_to do |format|
format.html { redirect_to contact_path(#contact) }
format.js
end
end
end
Is it possible to also create and destroy comments for the company model? How do you check whether a user is on a certain page? Because then I can just have an if statement.
The changed CommentsController
class CommentsController < ApplicationController
def create
#object = find_object
#comment = #object.comments.create(params[:comment])
respond_to do |format|
format.html { redirect_to [#object] }
format.js
end
end
def destroy
#object = find_object
#comment = object.comments.find(params[:id])
#comment.destroy
respond_to do |format|
format.html { redirect_to [#object] }
format.js
end
end
private
def object
#object = if params[:contact_id]
Contact.find(params[:contact_id]
elsif params[:company_id]
Company.find(params[:company_id])
end
end
end
you can do it with routing
# routes.rb
resources :contacts do
resources :comments
end
resources :company do
resources :comments
end
So in controller you can handle if there any company or contact around:
def destroy
#object = find_object
#comment = #object.comments.find(params[:id])
#comment.destroy
redirect_to [#object]
end
private
def find_object
#object = if params[:contact_id]
Contact.find(params[:contact_id])
elsif params[:company_id]
Company.find(params[:company_id])
end
end
But best solution here is to use POLYMORPHISM here. Check out:
http://railscasts.com/episodes/154-polymorphic-association