从 acts_as_list gem 调用 remove_from_list 时堆栈级别太深

Stack level too deep when calling remove_from_list from acts_as_list gem

我是运行升级到rails5.0之前没有失败的测试,但现在我的堆栈级别太深了,测试如下:

describe "item hierarchy" do
    before(:each) do
      @template = FactoryGirl.create(:item_template)
      @root_item_a = FactoryGirl.create(:item, :parent => @template)
      @root_item_b = FactoryGirl.create(:item, :parent => @template)
    end
    it "allows a root item without children to be moved under another item" do
      @root_item_a.parent_item = @root_item_b
      expect(@root_item_a).to be_valid
      expect(@root_item_a.save).to be_truthy

      @root_item_a.reload
      expect(@root_item_a.parent_item).to eql @root_item_b
    end

控制器有以下相关代码:

belongs_to :parent_item, :class_name => 'Item', :foreign_key => 'parent_item_id'
has_many :child_items, ->{ order 'position ASC' }, :class_name => 'Item', :foreign_key => 'parent_item_id', :dependent => :destroy
acts_as_list :scope => [:item_id, :parent_type, :parent_item_id]
validate :parent_items_cannot_have_parents

def parent_items_cannot_have_parents
  if child_items.any? && parent_item.present? # THIS IS LINE  #16
    errors.add(:parent_item, "An item with sub-items should live on the root")
  end
end

def parent_item_id=(item_id)
    if new_record?
      self[:parent_item_id] = item_id
    elsif self[:parent_item_id] != ActiveRecord::Base.connection.type_cast(item_id, self.class.columns_hash['parent_item_id'])
      remove_from_list  # IT FAILS HERE LINE # 30
      self[:parent_item_id] = item_id
      send(:add_to_list_bottom)
    end
  end

堆栈跟踪是:

Item item hierarchy allows a child item to be moved under a different parent
     Failure/Error: if child_item.any? && parent_item.present?

     SystemStackError:
       stack level too deep
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/scoping.rb:79:in `value_for'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/per_thread_registry.rb:53:in `value_for'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/scoping.rb:14:in `current_scope'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/scoping/named.rb:25:in `all'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/associations/association.rb:124:in `target_scope'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/associations/association.rb:86:in `scope'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/associations/collection_association.rb:429:in `scope'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/associations/collection_association.rb:348:in `empty?'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/associations/has_many_association.rb:50:in `empty?'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/associations/collection_association.rb:359:in `any?'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/associations/collection_proxy.rb:846:in `any?'
     # ./app/models/item.rb:16:in `parent_items_cannot_have_parents'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:382:in `block in make_lambda'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:169:in `block (2 levels) in halting'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:547:in `block (2 levels) in default_terminator'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:546:in `catch'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:546:in `block in default_terminator'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:170:in `block in halting'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:454:in `block in call'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:454:in `each'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:454:in `call'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:101:in `__run_callbacks__'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:750:in `_run_validate_callbacks'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activemodel-5.0.7/lib/active_model/validations.rb:408:in `run_validations!'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activemodel-5.0.7/lib/active_model/validations/callbacks.rb:113:in `block in run_validations!'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:126:in `call'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:455:in `call'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:101:in `__run_callbacks__'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:750:in `_run_validation_callbacks'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activemodel-5.0.7/lib/active_model/validations/callbacks.rb:113:in `run_validations!'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activemodel-5.0.7/lib/active_model/validations.rb:338:in `valid?'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/validations.rb:65:in `valid?'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/validations.rb:82:in `perform_validations'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/validations.rb:44:in `save'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/attribute_methods/dirty.rb:22:in `save'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/transactions.rb:319:in `block (2 levels) in save'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/connection_adapters/abstract/database_statements.rb:230:in `transaction'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/transactions.rb:211:in `transaction'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/transactions.rb:392:in `with_transaction_returning_status'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/transactions.rb:319:in `block in save'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/transactions.rb:334:in `rollback_active_record_state!'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/transactions.rb:318:in `save'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/suppressor.rb:41:in `save'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/acts_as_list-0.9.16/lib/acts_as_list/active_record/acts/list.rb:217:in `set_list_position'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/acts_as_list-0.9.16/lib/acts_as_list/active_record/acts/list.rb:128:in `remove_from_list'
     # ./app/models/item.rb:30:in `parent_item_id='
     # /usr/local/rvm/gems/ruby-2.5.1/gems/acts_as_list-0.9.16/lib/acts_as_list/active_record/acts/list.rb:466:in `block in check_scope'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/acts_as_list-0.9.16/lib/acts_as_list/active_record/acts/list.rb:466:in `each'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/acts_as_list-0.9.16/lib/acts_as_list/active_record/acts/list.rb:466:in `check_scope'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:382:in `block in make_lambda'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:150:in `block (2 levels) in halting_and_conditional'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:770:in `block (2 levels) in deprecated_false_terminator'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:769:in `catch'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:769:in `block in deprecated_false_terminator'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:151:in `block in halting_and_conditional'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:454:in `block in call'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:454:in `each'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:454:in `call'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:101:in `__run_callbacks__'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:750:in `_run_update_callbacks'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/callbacks.rb:306:in `_update_record'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/timestamp.rb:81:in `_update_record'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/persistence.rb:548:in `create_or_update'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/callbacks.rb:298:in `block in create_or_update'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:126:in `call'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:506:in `block (2 levels) in compile'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:455:in `call'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:101:in `__run_callbacks__'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activesupport-5.0.7/lib/active_support/callbacks.rb:750:in `_run_save_callbacks'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/callbacks.rb:298:in `create_or_update'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/persistence.rb:129:in `save'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/validations.rb:44:in `save'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/attribute_methods/dirty.rb:22:in `save'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/transactions.rb:319:in `block (2 levels) in save'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/transactions.rb:395:in `block in with_transaction_returning_status'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/connection_adapters/abstract/database_statements.rb:230:in `transaction'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/transactions.rb:211:in `transaction'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/transactions.rb:392:in `with_transaction_returning_status'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/transactions.rb:319:in `block in save'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/transactions.rb:334:in `rollback_active_record_state!'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/transactions.rb:318:in `save'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/activerecord-5.0.7/lib/active_record/suppressor.rb:41:in `save'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/acts_as_list-0.9.16/lib/acts_as_list/active_record/acts/list.rb:217:in `set_list_position'
     # /usr/local/rvm/gems/ruby-2.5.1/gems/acts_as_list-0.9.16/lib/acts_as_list/active_record/acts/list.rb:128:in `remove_from_list'
     # ./app/models/item.rb:30:in `parent_item_id='

acts_as_list gem 正在使用最新版本:0.9.16

回滚到 acts_as_list 的 0.9.5 版 gem 解决了问题