«неопределенный метод `scan’ для nil: NilClass» для полиморфной ассоциации ActiveRecord с:inverse_of

#ruby-on-rails #ruby #postgresql #activerecord

#ruby-on-rails #ruby #postgresql #activerecord

Вопрос:

У меня проблема с отношением «has_many», когда я получаю случайное исключение из ActiveRecord:

 product = Product.create!(valid_attributes)
product.prices 
# throws:
NoMethodError:
       undefined method `scan' for nil:NilClass
  

Похоже, это связано с «inverse_of», но я, по-видимому, сделал что-то, чего ActiveRecord не ожидал, но не мог побеспокоиться о том, чтобы иметь хорошую ошибку для. Лучше всего предположить, что это как-то связано с моим столбцом с именем «column» (хотя его нет в черном списке AFAIK). Я использую PostgreSQL. РЕДАКТИРОВАТЬ: попробовал переименовать столбец в «column_name» и «parent_column», и это ничего не исправило. Попробую сделать кое-что другое.

Вот соответствующий код модели и схема:

 class Price < ApplicationRecord
  belongs_to :parent, polymorphic: true
end

class Product < ApplicationRecord
  has_many :prices, as: :parent, inverse_of: :parent
end

class CreatePrices < ActiveRecord::Migration[5.2]
  def change
    create_table :prices do |t|
      t.string :parent_type, null: false
      t.bigint :parent_id, null: false
      t.string :column, null: false
      t.decimal :price, null: false, precision: 15, scale: 2
      t.timestamp :effective_date, null: false
    end

    add_index :prices, [:parent_type, :parent_id, :column]
  end
end
  

И полной трассировкой стека:

 NoMethodError:
       undefined method `scan' for nil:NilClass
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/inheritance.rb:185:in `compute_type'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/reflection.rb:422:in `compute_class'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/reflection.rb:379:in `klass'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/reflection.rb:234:in `inverse_of'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/reflection.rb:239:in `check_validity_of_inverse!'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/reflection.rb:474:in `check_validity!'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/associations/association.rb:26:in `initialize'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/associations.rb:237:in `new'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/associations.rb:237:in `association'
     # /Users/william/.rbenv/versions/2.5.5/lib/ruby/gems/2.5.0/gems/activerecord-5.2.2.1/lib/active_record/associations/builder/association.rb:108:in `prices'
  

Комментарии:

1. Не могли бы вы попробовать переименовать этот столбец, просто чтобы убедиться, проблема это или нет?

2. разве column это не зарезервированное слово любой используемой вами СУБД?. может быть, в этом проблема ?, не уверен. Попробуйте изменить его, как предложил @arieljuod.

3. dev.mysql.com/doc/refman/5.7/en/…

4. Я попытался переименовать его, и безуспешно. Я попробую еще раз на случай, если он не «принял». Я использую PostgreSQL, а не MySQL, но спасибо за идею. СТОЛБЕЦ также указан как зарезервированное слово в PG.

5. Возможно, я решил проблему, мне будет неловко, если это то, что я думаю.

Ответ №1:

Ответ заключался в том, что я нарушал ожидания ActiveRecord способом, которого я не осознавал, и должен был включить в исходный вопрос, но не сделал этого из-за высокомерия. Я использовал анонимную модель в тестировании. Например:

 module Priced
  extend ActiveSupport::Concern

  included do
    has_many :prices, as: :parent, inverse_of: :parent
  end
end

RSpec.describe Priced do
  let(:model_class) {
    Class.new(ActiveRecord::Base).tap do |klass|
      klass.table_name = "products"
      klass.send(:include, Priced)
    end
  end

  it "defines a 'has_many :prices' association" do
    model = model_class.create!
    price = Price.create!(parent: model, price: 100)
    expect(model.prices).to eq([price])
  end
end
  

Это позволяет мне использовать модель ActiveRecord с существующей таблицей, но отделить ее от всей логики в реальной Product модели.

Я никогда раньше не сталкивался с какими-либо проблемами с этой настройкой, поэтому я с удовольствием использую ее для тестирования модулей в течение многих лет. Итак, моя вина; если бы я поместил этот код в исходный вопрос, я, вероятно, понял бы ответ перед публикацией, но я отвечаю на свой собственный вопрос в качестве предупреждения другим «умным» программистам Ruby.