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:
Invite
model has been updated with the before_create
filter and generate_token
method.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?
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:
Invite
mailer from invite.rb
to invite_mailer.rb
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.