权威政策错误未定义方法`image'为零:NilClass

时间:2016-11-01 18:48:44

标签: ruby-on-rails ruby-on-rails-4 devise pundit

我一直坚持这个问题很长一段时间,不知道我做错了什么。

我正在使用Rails 4.2.5.1,Pundit 1.1.0和Devise。

我有一篇博文,其中显示以下内容:

  • title
  • 作者用户名
  • 图像
  • 清理摘录(在索引页面上)
  • body(在显示页面上)

索引页面显示正确(除了未显示的作者用户名,因为它不识别用户名参数)。但是,当我尝试通过显示页面查看单个帖子时,出现以下错误:

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>

我还有一些其他问题,一旦这个问题得到解决,希望能够自行解决:

  • 已删除的帖子会收到一条消息,说明他们已删除,但仍然存在
  • 编辑帖子获取相同的图片错误消息
  • 未登录的用户被拒绝访问查看帖子,我希望他们能够查看所有帖子,无论是否已登录。这是同一个问题,但解决方案对我不起作用,我没有得到任何类型的rails错误消息:Pundit policy_scope error。也许这与应用政策中的初始化有关?

这些其他问题可以在以后解决,或者如果您发现错误,我会感激您的帮助。

现在我的主要问题是尝试解决undefined method "image" for nil:NilClass错误

1 个答案:

答案 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