Rails 4: undefined method `relation_delegate_class' for Model:Class
Asked Answered
K

3

14

I am trying to follow this coderwall tutorial about Creating a Scoped Invitation System for Rails.

In my Rails 4 app, I have the following models:

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

Here is the correspondance between my models and the models from the tutorial:

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

I am now in the Making a New Invitation section:

  • The Invite model has been updated with the before_create filter and generate_token method.
  • The Invites controller has been updated with the create action.

However, when I visit the Calendar edit view to fill out the Invite form, I get the following error:

NoMethodError in CalendarsController#edit
undefined method `relation_delegate_class' for Invite:Class
def edit
  @user = current_user
  @invite = @calendar.invites.build
  authorize @calendar
end

The issue seems to come from the @invite = @calendar.invites.build line.

—————

UPDATE: here is the content of my Invite model:

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

—————

UPDATE 2: in this question, the author explains the problem may come from CanCanCan & Rolify. I don't use these gems, but I use Pundit. Thought this would be useful in the context of my question.

—————

UPDATE 3: here is also the migration I used for the Invite model:

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

I am wondering if the problem could be caused by the t.string :recipient_role, since the role of a given user only exist in the administration table, for a given calendar: if :recipient_role is automatically interpreted as recipient.role by Rails, then maybe this is causing the error?

—————

UPDATE 4: here is the content of 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

—————

UPDATE 5: here are the server logs:

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)

—————

UPDATE 6: I just realized I did not have

def invite_params
  params.require(:invite)
end

in the Invites controller: could this be the root of the problem here?

—————

Any idea about what this error message mean and how to fix the issue?

Kadi answered 14/9, 2015 at 17:41 Comment(4)
Could you show full backtrace of the error you get? It would also be helpful if I can see contents of your CalendarsController. Thank you.Techno
Sure, I just added the full code of the CalendarsController and the server logs. Let me know if you need any additional information.Kadi
I don't see anything obvious. Try updating all of your gems. Try hardcoding a class name on the invites association. Try commenting out all of the associations then uncommenting them one at a time until it breaks.Shcherbakov
Thanks a lot for taking a look at it James, really appreciate it. I will try what you suggest.Kadi
K
23

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.

Kadi answered 15/9, 2015 at 16:1 Comment(4)
Thanks a lot K M Rakibul Islam. It took some time but we fixed it!Kadi
Ran into the exact same problem with a mailer called invite. Thanks for the detailed answer and for saving me some time.Turkmen
Thank you! Same kind of 'naming' issue with my project. Appreciate the detailed response.Lennon
I had the same issue and it was actually related to a class we had named "Messager.rb" for the API messages. A few months later we created a model called "Message" and was receiving this error. I figured out pretty quick that it was because we had 2 classes with the same name. Naming is key, lol... renamed Message to ApiMessage.rbBlotchy
A
1

It also happen if you forget to inherit your model from ActiveRecord. Thus it becomes just a simple ruby class.

class MyPlainModel
end
Atelectasis answered 30/12, 2019 at 19:48 Comment(0)
H
0

Another cause is that your sti column is pointing to an invalid column class. I somehow had ended up with:

=> #<Mailerlite::Subscriber:0x00005559d469bd00
...
 user_id: 2777,
 user_type: "NilClass",
...
>

Causing NoMethodError: undefined method 'relation_delegate_class' for NilClass:Class when trying to load the user relation.

Simple enough fix by changing the user_type column to be nil or the correct classname.

Hemorrhage answered 23/8, 2021 at 23:17 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.