我正在使用 Bootstrap,它有
div class="alert notice"
,它有一堆用于各种通知消息的类。
我还有一个针对评论的 AJAX 销毁操作,我已添加
cancan
授权。当我尝试删除 current_user
无权访问的评论时,它不起作用 - 这是正确的。
但是我想要发生的是在 Bootstrap 样式的 div 中弹出错误消息 5 - 10 秒,然后消失。
这是对我的
destroy
执行的
CommentsController.rb
操作
def destroy
respond_to do |format|
if @comment.destroy
format.html { redirect_to root_url, notice: 'Comment was successfully deleted.' }
format.json { head :no_content }
format.js { render :layout => false }
else
format.json { render json: @comment.errors, status: :unprocessable_entity }
end
end
end
我在同一控制器的私有方法中设置了
@comment
:
private
def set_comment
@comment = current_user.comments.find(params[:id])
end
这是我的
comments/destroy.js.erb
$('.delete_comment').bind('ajax:success', function() {
$(this).closest('div#new_comment').fadeOut();
});
但这并不影响未经授权的访问。
在我的
ability.rb
中,我有这个:
can :manage, Comment, user_id: user.id
在我的日志中,当我尝试删除我无权访问的评论时,我会在日志中看到以下内容:
Started DELETE "/comments/5" for 127.0.0.1 at 2014-10-16 02:56:53 -0500
Processing by CommentsController#destroy as JS
Parameters: {"id"=>"5"}
User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = 1 ORDER BY "users"."id" ASC LIMIT 1
FamilyTree Load (0.2ms) SELECT "family_trees".* FROM "family_trees" WHERE "family_trees"."user_id" = $1 LIMIT 1 [["user_id", 1]]
ReadMark Load (0.1ms) SELECT "read_marks".* FROM "read_marks" WHERE "read_marks"."user_id" = $1 AND "read_marks"."readable_type" = 'PublicActivity::ORM::ActiveRecord::Activity' AND "read_marks"."readable_id" IS NULL ORDER BY "read_marks"."id" ASC LIMIT 1 [["user_id", 1]]
Comment Load (0.3ms) SELECT "comments".* FROM "comments" WHERE "comments"."user_id" = $1 AND "comments"."id" = $2 LIMIT 1 [["user_id", 1], ["id", 5]]
Completed 404 Not Found in 8ms
ActiveRecord::RecordNotFound - Couldn't find Comment with 'id'=5 [WHERE "comments"."user_id" = $1]:
这是完美的。
我想做的就是在 Bootstrap 警报中显示适当的错误,该错误会在几秒钟内消失。
我该如何实现这一点?
首先,如果您使用
cancan
- 只需使用 cancan:
#app/controllers/comments_controller.rb
class CommentsController < ApplicationController
load_and_authorize_resource #this will set @comment by your ability and authorize action
...
end
这将引发
CanCan::AccessDenied
而不是 ActiveRecord::RecordNotFound
错误。
让我们用rescue_from
在
ApplicationController
中捕获它
#app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
...
rescue_from CanCan::AccessDenied do |exception|
@error_message = exception.message
respond_to do |f|
f.js{render 'errors/error', status: 401}
end
end
end
对于弹出通知,我使用 PNotify 库 http://sciactive.github.io/pnotify/ 它将在右上角显示错误,然后隐藏。 只需将其包含在您的项目中,您就可以显示如下错误:
#app/views/errors/error.js.erb
new PNotify({
title: 'Oh No!',
text: '<%=@error_message%>',
type: 'error'
});
此代码可让您避免因不良做法而捕获
ActiveRecord::RecordNotFound
错误。
更新
我忘记了什么!你必须删除
set_comment
方法和 before_action
或这样写:
before_action :set_comment
...
private
def set_comment
@comment ||= current_user.comments.find(params[:id])
end
此回调覆盖了代码中
load_and_authorize_resource
的@comment变量。
Cancan 不需要这个助手,因为它通过 load_and_authorize_resource
加载资源
更新2
您还需要确保您使用的是来自
CanCanCommunity的
rails4
的最新版本的 cancan,因为原来的旧版本不支持 rails4
只需在 Gemfile 中使用它即可
gem 'cancancan', '~> 1.9'
而不是
gem 'cancan'
在您的
comments/destroy.js.erb
文件中添加以下代码:
<% if defined?(@error) %>
$('.alert.notice').text("<%= @error %>");
$('.alert.notice').show();
setTimeout(function() {
$('.alert.notice').fadeOut('fast');
}, 5000);
<% end %>
更改
set_comment
方法以使用 find_by
以避免出现 RecordNotFound
异常:
def set_comment
@comment = current_user.comments.find_by(id:params[:id])
end
然后在你的控制器中你可以修改销毁动作如下:
def destroy
respond_to do |format|
if @comment.present?
@comment.destroy
format.html { redirect_to root_url, notice: 'Comment was successfully deleted.' }
format.json { head :no_content }
format.js { render :layout => false }
else
format.html { redirect_to root_url, notice: 'unauthorized access error.' }
format.js { @error = 'unauthorized access error' }
end
end
结束
我将其放在 app/controllers/application_controller.rb 中
## Handling unauthorized access: https://github.com/CanCanCommunity/cancancan#handle-unauthorized-access
rescue_from CanCan::AccessDenied do |exception|
respond_to do |format|
format.json { head :forbidden, content_type: 'text/html' }
format.html { redirect_to main_app.root_url, notice: exception.message }
format.js { head :forbidden, content_type: 'text/html' }
end
end