我一直坚持这个问题很长一段时间,不知道我做错了什么。
我正在使用Rails 4.2.5.1,Pundit 1.1.0和Devise。
我有一篇博文,其中显示以下内容:
索引页面显示正确(除了未显示的作者用户名,因为它不识别用户名参数)。但是,当我尝试通过显示页面查看单个帖子时,出现以下错误:
undefined method `image' for nil:NilClass
如果我删除了用于显示图像的代码行,则会出现具有相同未定义方法错误的标题错误。
我在SitePoint-source/Authorization_with_Pundit处的示例几乎完全是针对策略和控制器(仅进行了少量修改)
在添加Pundit以在管理员,编辑和用户之间创建授权之前,一切都运行良好。
这是我目前的代码:
应用程序控制器
class ApplicationController < ActionController::Base
include Pundit
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
# Prevent CSRF attacks by raising an exception.
# For APIs, you may want to use :null_session instead.
protect_from_forgery with: :exception
before_filter :configure_permitted_parameters, if: :devise_controller?
private
def user_not_authorized
flash[:alert] = "Access denied. You are not authorized to view that page."
redirect_to (request.referrer || root_path)
end
protected
def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_up) { |u| u.permit(:username, :email, :password, :password_confirmation, :remember_me) }
devise_parameter_sanitizer.permit(:sign_in) { |u| u.permit(:username, :email, :password, :remember_me) }
devise_parameter_sanitizer.permit(:account_update) {|u| u.permit(:username, :email, :password, :password_confirmation, :current_password)}
end
end
后置控制器
class PostsController < ApplicationController
before_action :set_post, only: [:show, :edit, :update, :destroy]
after_action :verify_authorized, only: [:destroy]
after_action :verify_policy_scoped, only: [:user_posts]
def index
@meta_title = "Blog"
@meta_description = "description here"
@posts = Post.all.order("created_at DESC").paginate(:page => params[:page], :per_page => 4)
end
def show
end
def new
@meta_title = "Add New Blog"
@meta_description ="Add a new blog to your profile."
@post = Post.new
end
def edit
@meta_title = "Edit Blog"
@meta_description ="Edit an existing blog from your profile."
end
def create
@post = Post.new
@post.update_attributes(permitted_attributes(@post))
if @post.save
redirect_to @post, notice: 'Post was successfully created.'
else
render :new
end
end
def update
@post = Post.find(params[:id])
if @post.update_attributes(permitted_attributes(@post))
redirect_to @post, notice: 'Post was successfully updated.'
else
render :edit
end
end
def destroy
if @post.present?
authorize @post
@post.destroy
else
skip_authorization
end
redirect_to posts_url, notice: 'Post was successfully deleted.'
end
def user_posts
@posts = policy_scope(Post)
end
private
# Use callbacks to share common setup or constraints between actions.
def set_post
@post = Post.find_by(id: params[:id])
end
# Only allow the white list through.
def post_params
params.require(:post).permit(policy(@post).permitted_attributes)
end
end
申请政策
class ApplicationPolicy
attr_reader :user, :record
def initialize(user, record)
raise Pundit::NotAuthorizedError, "You must be logged in to perform this action" unless user
@user = user
@record = record
end
def index?
false
end
def show?
scope.where(:id => record.id).exists?
end
def create?
false
end
def new?
create?
end
def update?
false
end
def edit?
update?
end
def destroy?
false
end
def scope
Pundit.policy_scope!(user, record.class)
end
class Scope
attr_reader :user, :scope
def initialize(user, scope)
@user = user
@scope = scope
end
def resolve
scope
end
end
end
邮政政策
class PostPolicy < ApplicationPolicy
class Scope < Scope
def resolve
scope.where(user: user)
end
end
def permitted_attributes
if user.admin? || user.editor?
[:title, :body, :image, :permalink, :description, :tag_list, :username]
else
[:title, :body, :image, :username]
end
end
def new?
user.admin? || user.editor?
end
def index?
true
end
def create?
user.admin? || user.editor?
end
def update?
user.admin? || user.editor? || record.user == user
end
def destroy?
user.admin? || record.user == user
end
end
Post.rb
class Post < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
belongs_to :user
# This method associates the attribute ":image" with a file attachment
has_attached_file :image, styles: {
thumb: '100x100>',
square: '200x200#',
medium: '300x300>',
}
extend FriendlyId
friendly_id :permalink, use: [:slugged, :history, :finders]
validates :permalink, presence: true, uniqueness: true
validates :title, presence: true, length: { minimum: 5}
validates :description, presence: true, uniqueness: true, length: {maximum: 160}
validates :body, presence: true
validates :image, presence: true
# Validate the attached image is image/jpg, image/png, etc
validates_attachment_content_type :image, :content_type => /\Aimage\/.*\Z/
def should_generate_new_friendly_id?
permalink_changed?
end
end
发表#展示
<% provide(:title, "@post.title") %>
<% provide(:description, "@post.description") %>
<div class="row">
<div class="col-md-offset-1 col-md-10">
<div class="panel panel-default">
<div class="panel-heading center">
<%= image_tag @post.image.url, :style => "width: 100%; height: auto;" %>
</div>
<div class="panel-body">
<h2 class="title center"><%= @post.title %></h2>
<p class="posted"><i class="ion-android-time"></i> <%= @post.created_at.strftime("%B %d, %Y") %> </p>
<p class="posted"><i class="ion-person"></i> Author: <%= link_to @post.username, about_path(:anchor => "coaches") %></p>
<hr>
<div class="postBody" id="summernote">
<%= @post.body.html_safe %>
</div>
</div>
<div class="panel-footer center">
<%= link_to 'Back', posts_path %> |
<%= link_to 'Edit', edit_post_path(@post) %> |
<%= link_to 'Delete', @post, method: :delete, data: { confirm: 'Are you sure you want to delete this post?' } %>
<%= render 'disqus' %>
</div>
<div class="panel-footer center">
<%= link_to 'Back', posts_path %>
</div>
</div>
</div>
</div>
发表#索引
<div class="container">
<div class="row">
<div class="col-md-9">
<% @posts.each do |post| %>
<div class="post-wrapper">
<h3 class="title center"><%= link_to post.title, post %></h3>
<p class="posted"><i class="ion-android-time"></i> <%= post.created_at.strftime("%B %d, %Y") %></p>
<p class="posted"><i class="ion-person"></i> Author: <%= link_to post.user(:username), about_path(:anchor => "coaches") %></p><br>
<div class="post-image center"><%= link_to image_tag(post.image.url, :style => "width: 100%; height: auto;"), post %></div><br>
<%= sanitize(post.body[0,300]) %>...<br>
<div class="center">
<%= link_to 'View Blog', post, class: "btn btn-primary" %>
<% if policy(post).update? %>
<%= link_to 'Edit', edit_post_path(post) %> |
<% end %>
<% if policy(post).destroy? %>
<%= link_to 'Delete', post, method: :delete, data: { confirm: 'Are you sure?' } %>
<% end %>
</div>
<br>
</div>
<% end %>
<div class="center">
<%= will_paginate @posts, renderer: BootstrapPagination::Rails %>
</div>
</div>
</div>
</div>
我还有一些其他问题,一旦这个问题得到解决,希望能够自行解决:
这些其他问题可以在以后解决,或者如果您发现错误,我会感激您的帮助。
现在我的主要问题是尝试解决undefined method "image" for nil:NilClass
错误
答案 0 :(得分:2)
undefined method `image' for nil:NilClass
这意味着您尝试拨打.image
的对象(@post
)是nil
。追溯,找出为什么它没有。
在Posts#show
视图中,您依靠set_post
回调来设置帖子。 set_post
回调使用带有Post.find_by
参数的id
来查找记录。
find_by
行为的方式是,如果给出的参数为nil
,则返回nil
(即,如果您拨打Post.find_by(id: nil)
,则表示您{l}得到nil
。这可能表示params[:id]
本身就是nil
- 检查它是否设置为查询字符串参数(example.com/posts/show?id=12
)或作为网址本身的一部分(example.com/posts/12
)。
如果您无法说明,请在byebug
行动中添加Posts#show
来电:
def show
byebug
end
这将在执行时停止操作,并为您提供一个可供使用的控制台 - 此时,您可以键入params[:id]
以找出其值。
我建议您使用Post.find_by
,而不是使用Post.find
。不同之处在于find
默认使用ID(因此您不需要指定您正在使用的参数),并且它会引发404 Not Found响应,而不是返回{{1}如果它找不到记录。为此,您的nil
回调应如下所示:
set_post