Rails 4:Model:Class的未定义方法`relation_delegate_class'

Thi*_*ent 11 model-view-controller activerecord ruby-on-rails nomethoderror ruby-on-rails-4

我正在尝试按照这个关于为Rails创建一个Scoped邀请系统的 coderwall教程.

在我的Rails 4应用程序中,我有以下模型:

class User < ActiveRecord::Base
  has_many :administrations
  has_many :calendars, through: :administrations
  has_many :invitations, :class_name => "Invite", :foreign_key => 'recipient_id'
  has_many :sent_invites, :class_name => "Invite", :foreign_key => 'sender_id'
end

class Calendar < ActiveRecord::Base
  has_many :administrations
  has_many :users, through: :administrations
  has_many :invites
end

class Administration < ActiveRecord::Base
  belongs_to :user
  belongs_to :calendar
end

class Invite < ActiveRecord::Base
  belongs_to :calendar
  belongs_to :sender, :class_name => 'User'
  belongs_to :recipient, :class_name => 'User'
end
Run Code Online (Sandbox Code Playgroud)

以下是我的模型与教程中的模型之间的对应关系:

  • User <=> User
  • Calendar <=> UserGroup
  • Administration <=> Membership
  • Invite <=> Invite

我现在正在制作一个新邀请部分:

  • Invite模型已使用before_create过滤器和generate_token方法进行了更新.
  • Invites控制器已更新的create动作.

但是,当我访问日历编辑视图以填写邀请表单时,我收到以下错误:

NoMethodError in CalendarsController#edit
undefined method `relation_delegate_class' for Invite:Class
def edit
  @user = current_user
  @invite = @calendar.invites.build
  authorize @calendar
end
Run Code Online (Sandbox Code Playgroud)

问题似乎来自这@invite = @calendar.invites.build条线.

-----

更新:这是我的邀请模型的内容:

class Invite < ActiveRecord::Base
  belongs_to :calendar
  belongs_to :sender, :class_name => 'User'
  belongs_to :recipient, :class_name => 'User'

  before_create :generate_token

  def generate_token
   self.token = Digest::SHA1.hexdigest([self.calendar_id, self.recipient_role, Time.now, rand].join)
  end

end
Run Code Online (Sandbox Code Playgroud)

-----

更新2:在这个问题中,作者解释了问题可能来自CanCanCan和Rolify.我不使用这些宝石,但我使用的是Pundit.认为这在我的问题中是有用的.

-----

更新3:这里也是我用于Invite模型的迁移:

class CreateInvites < ActiveRecord::Migration
  def change
    create_table :invites do |t|
      t.string :email 
      t.integer :calendar_id
      t.integer :sender_id
      t.integer :recipient_id
      t.string :recipient_role
      t.string :token
      t.timestamps null: false
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

我想知道问题是否可能是由于t.string :recipient_role,由于role给定的user只存在于administration表中,对于给定的calendar:如果:recipient_rolerecipient.roleRails 自动解释,那么这可能导致错误?

-----

更新4:这是CalendarsController的内容:

class CalendarsController < ApplicationController
  before_action :set_calendar, only: [:show, :edit, :update, :destroy]
  before_action :authenticate_user!

  # GET /calendars
  # GET /calendars.json
  def index
    @user = current_user
    @calendars = @user.calendars.all
  end

  # GET /calendars/1
  # GET /calendars/1.json
  def show
    @user = current_user
    @calendar = @user.calendars.find(params[:id])
    authorize @calendar
  end

  # GET /calendars/new
  def new
    @user = current_user
    @calendar = @user.calendars.new
    authorize @calendar
  end

  # GET /calendars/1/edit
  def edit
    @user = current_user
    @invite = @calendar.invites.build
    authorize @calendar
  end

  # POST /calendars
  # POST /calendars.json
def create
  @user = current_user
  @calendar = @user.calendars.create(calendar_params)
  authorize @calendar
  respond_to do |format|
    if @calendar.save
      current_user.set_default_role(@calendar.id, 'Owner')
      format.html { redirect_to calendar_path(@calendar), notice: 'Calendar was successfully created.' }
      format.json { render :show, status: :created, location: @calendar }
    else
      format.html { render :new }
      format.json { render json: @calendar.errors, status: :unprocessable_entity }
    end
  end
end

  # PATCH/PUT /calendars/1
  # PATCH/PUT /calendars/1.json
  def update
    @user = current_user
    @calendar = Calendar.find(params[:id])
    authorize @calendar
    respond_to do |format|
      if @calendar.update(calendar_params)
        format.html { redirect_to calendar_path(@calendar), notice: 'Calendar was successfully updated.' }
        format.json { render :show, status: :ok, location: @calendar }
      else
        format.html { render :edit }
        format.json { render json: @calendar.errors, status: :unprocessable_entity }
      end
    end
  end

  # DELETE /calendars/1
  # DELETE /calendars/1.json
  def destroy
    @user = current_user
    @calendar.destroy
    authorize @calendar
    respond_to do |format|
      format.html { redirect_to calendars_url, notice: 'Calendar was successfully destroyed.' }
      format.json { head :no_content }
    end
  end

  private
    # Use callbacks to share common setup or constraints between actions.
    def set_calendar
      @calendar = Calendar.find(params[:id])
    end

    # Never trust parameters from the scary internet, only allow the white list through.
    def calendar_params
      params.require(:calendar).permit(:name)
    end
end
Run Code Online (Sandbox Code Playgroud)

-----

更新5:这是服务器日志:

Started GET "/calendars/2/edit" for ::1 at 2015-09-14 11:44:13 -0700
Processing by CalendarsController#edit as HTML
  Parameters: {"id"=>"2"}
  Calendar Load (0.1ms)  SELECT  "calendars".* FROM "calendars" WHERE "calendars"."id" = ? LIMIT 1  [["id", 2]]
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ?  ORDER BY "users"."id" ASC LIMIT 1  [["id", 1]]
Completed 500 Internal Server Error in 3ms (ActiveRecord: 0.3ms)

NoMethodError (undefined method `relation_delegate_class' for Invite:Class):
  app/controllers/calendars_controller.rb:30:in `edit'


  Rendered /Users/TXC/.rvm/gems/ruby-2.2.1@global/gems/actionpack-4.2.2/lib/action_dispatch/middleware/templates/rescues/_source.erb (6.0ms)
  Rendered /Users/TXC/.rvm/gems/ruby-2.2.1@global/gems/actionpack-4.2.2/lib/action_dispatch/middleware/templates/rescues/_trace.html.erb (2.8ms)
  Rendered /Users/TXC/.rvm/gems/ruby-2.2.1@global/gems/actionpack-4.2.2/lib/action_dispatch/middleware/templates/rescues/_request_and_response.html.erb (1.7ms)
  Rendered /Users/TXC/.rvm/gems/ruby-2.2.1@global/gems/actionpack-4.2.2/lib/action_dispatch/middleware/templates/rescues/diagnostics.html.erb within rescues/layout (68.9ms)
  Rendered /Users/TXC/.rvm/gems/ruby-2.2.1/gems/web-console-2.2.1/lib/web_console/templates/_markup.html.erb (0.5ms)
  Rendered /Users/TXC/.rvm/gems/ruby-2.2.1/gems/web-console-2.2.1/lib/web_console/templates/_inner_console_markup.html.erb within layouts/inlined_string (0.3ms)
  Rendered /Users/TXC/.rvm/gems/ruby-2.2.1/gems/web-console-2.2.1/lib/web_console/templates/_prompt_box_markup.html.erb within layouts/inlined_string (0.3ms)
  Rendered /Users/TXC/.rvm/gems/ruby-2.2.1/gems/web-console-2.2.1/lib/web_console/templates/style.css.erb within layouts/inlined_string (0.3ms)
  Rendered /Users/TXC/.rvm/gems/ruby-2.2.1/gems/web-console-2.2.1/lib/web_console/templates/console.js.erb within layouts/javascript (39.3ms)
  Rendered /Users/TXC/.rvm/gems/ruby-2.2.1/gems/web-console-2.2.1/lib/web_console/templates/main.js.erb within layouts/javascript (0.4ms)
  Rendered /Users/TXC/.rvm/gems/ruby-2.2.1/gems/web-console-2.2.1/lib/web_console/templates/error_page.js.erb within layouts/javascript (0.4ms)
  Rendered /Users/TXC/.rvm/gems/ruby-2.2.1/gems/web-console-2.2.1/lib/web_console/templates/index.html.erb (94.2ms)
Run Code Online (Sandbox Code Playgroud)

-----

更新6:我刚刚意识到我没有

def invite_params
  params.require(:invite)
end
Run Code Online (Sandbox Code Playgroud)

Invites控制器中:这可能是问题的根源吗?

-----

有关此错误消息的含义以及如何解决问题的任何想法?

Thi*_*ent 20

The problem was tricky to identify, especially just from the content of the question.

THE PROBLEM

The reason why I was getting the undefined method 'relation_delegate_class' for Invite:Class error is because Invite was no longer considered to be a model by Rails.

THE ROOT CAUSE OF THE PROBLEM

When I created the Invite mailer, I ran rails g mailer Invite instead of rails g mailer InviteMailer.

Because of this, Invite as a mailer override Invite as a model, hence creating errors as soon as methods were applied to instances of the Invite model.

HOW WE FIGURED IT OUT

One of my friends, who is way more experienced with programming than I am, identified the problem by tweaking the @invite = @calendar.invites.build line that was causing the error.

This led us to eventually run Invite.first in the rails console: while we should have got either an instance of the Invite class, or nil, we actually got an error.

Since .first should be a valid method on any ActiveRecord model, we realized that Invite was not a considered to be a model by Rails.

HOW WE FIXED IT

Once we had identified the issue, fixing it was pretty straightforward:

  • We changed the name of the Invite mailer from invite.rb to invite_mailer.rb
  • In the newly renamed invite_mailer.rb file, we replaced class Invite < ApplicationMailer with class InviteMailer < ApplicationMailer

I hope this can be useful to other Stack Overflow users who might get a similar relation_delegate_class error.