ruby-on-railsactiverecordassociationspolymorphic-associationsuninitialized-constant

Rails Polymorphic Associations Uninitialized Constant Error


Been working on debugging this for a few hours and still can't find the solution. When I send a PUT request that calls @transaction.save in the update method of my Transaction Controller, my server log spits out:

NameError - uninitialized constant Transaction::Respondable:

Basic relevant schema is both Users and Organizations can have Requests, Requests can have Transactions, and both Users and Organizations can also have Transactions. The differentiation is that the transaction exists between Person/Organization 1, who initializes the request and Person/Organization 2, who responds to the request. In other words, a Transaction belongs to both a Request (and through a request User/Orgnization 1) and User/Organization 2. I'm just looking for now at Users, so I won't include code about Organizations for now since it's not yet implemented. Here's the relevant code:

Schema:

create_table "transactions", force: true do |t|
    t.string   "name",             null: false
    t.string   "description",      null: false
    t.integer  "listable_id",      null: false
    t.string   "listable_type",    null: false
    t.integer  "respondable_id",   null: false
    t.string   "respondable_type", null: false
    t.datetime "created_at"
    t.datetime "updated_at"
end

listable refers to the Request (also polymorphic, since it can also come from an Offer, but irrelevant for now). respondable refers to the User.

create_table "requests", force: true do |t|
    t.string   "name",             null: false
    t.string   "description",      null: false
    t.integer  "requestable_id",   null: false
    t.string   "requestable_type", null: false
    t.datetime "created_at"
    t.datetime "updated_at"
end

User schema is irrelevant since it contains no links to other tables (only relationships are has_many).

And here are my models:

class User < ActiveRecord::Base
  ...
  has_many :requests, as: :requestable, dependent: :destroy
  has_many :responded_transactions, class_name: 'Transaction', as: :respondable, dependent: :destroy
  has_many :listed_transactions, through: :requests, source: :transactions
  ...
end

class Request < ActiveRecord::Base
    validates :name, :description, :requestable, presence: true

    has_many :transactions, as: :listable, dependent: :destroy

    belongs_to :requestable, polymorphic: true

    def relevant_transaction(user)
        self.transactions.where('respondable_id = ?', user.id).first
    end
end

class Transaction < ActiveRecord::Base
    validates :name, :description, :listable, :respondable, presence: true
    validates :respondable, uniqueness: { scope: :listable, message: 'You have already initialized a transaction on this item' }

    belongs_to :listable, polymorphic: true
    belongs_to :respondable, polymorphic: true

    before_validation :set_name_and_description

    private

    def set_name_and_description
        self.name = listable.name
        self.description = listable.description
    end
end

And the TransactionsController:

module Api
    class TransactionsController < ApplicationController
        def create
            @transaction = current_user.responded_transactions.new(transaction_params)

            if @transaction.save
                render :show, status: :created
            else
                render json: @transaction.errors.full_messages, status: :unprocessable_entity
            end
        end

        def update
            @transaction = Transaction.find(params[:id])

            if @transaction.save
                render :show, status: :saved
            else
                render json: @transaction.errors.full_messages, status: :unprocessable_entity
            end
        end

        private

        def transaction_params
            params.require(:transaction).permit(:listable_id, :listable_type)
        end
    end
end

The weirdest thing about this is that in the Rails Controller, making calls like User.first.responded_transactions or Transaction.first.respondable work. Yet it seems like something related to respondable is failing somewhere... Any help would be greatly appreciated!


Solution

  • The problem lies in your validation. Try validating your polymorphic columns individually. Let me show you:

    validates(
      :respondable_id, 
      uniqueness: { 
        scope: [:respondable_type, :listable_id, :listable_type], 
        message: "You've already initialized a transaction on this item" 
      }
    )