Проблема с постоянной автозагрузкой в ​​Rails-проекте (иногда работает) - PullRequest
2 голосов
/ 25 мая 2019

Я работаю с проектом Rails и не совсем понимаю, как работает автозагрузка Rails в моем конкретном случае. Я прочитал несколько статей об автозагрузке Rails и ее подводных камнях, но они мне не очень помогли

Я строю процессор для задач (упражнений). Каждая задача имеет свой собственный класс процессора в Tasks::<TaskName>::Processor, который смешивается в модуле Tasks::Processor, который содержит общий код для процессоров задач. Процессоры содержат класс Get (для обработки запросов GET), расположенный в Tasks::<TaskName>::Processor::Get, который смешивается с Tasks::Processor::Get, содержащим общий код Get.

Я немного упростил код, чтобы было проще понять и убрал всю бизнес-логику, но этого все же достаточно, чтобы воспроизвести проблему.

Итак, проблема в следующем:

когда я запускаю Tasks::TaskOne::Processor.new.get, он работает нормально, но если я запускаю Tasks::TaskTwo::Processor.new.get, после этого выдается ошибка: NoMethodError: неопределенный метод `new 'для Tasks :: Processor :: Get: Module, Это также работает наоборот: если я сначала запускаю код процессора TaskTwo, то он работает нормально, но процессор TaskOne выдаст ошибку. Он просто не может найти конкретную реализацию Get и вместо этого находит универсальный модуль и пытается создать его экземпляр, что, очевидно, невозможно.

Вот код вместе со структурой.

Общий код:

приложение / модели / задачи / processor.rb

module Tasks

  # generic Processor (mixed in by custom processors)
  module Processor
    # ...
  end
end

приложение / модели / задачи / процессор / get.rb

module Tasks
  module Processor

    # generic Get
    module Get
      # ...
    end
  end
end

Код TaskOne:

приложение / модели / задачи / task_one / processor.rb

module Tasks
  module TaskOne

    # processor for task_one
    class Processor
      include Tasks::Processor # mix in generic task processor

      def get
        Get.new.call
      end
    end
  end
end

приложение / модели / задачи / task_one / процессор / get.rb

module Tasks
  module TaskOne
    class Processor

      # task_one's processor's custom Get
      class Get
        include Tasks::Processor::Get # mix in generic Get

        def call
          puts "in task_one's Processor's Get"
        end
      end
    end
  end
end

И практически идентичный код для TaskTwo:

приложение / модели / задачи / task_two / processor.rb

module Tasks
  module TaskTwo

    # processor for task_two
    class Processor
      include Tasks::Processor # mix in generic task processor

      def get
        Get.new.call
      end
    end
  end
end

приложение / модели / задачи / task_two / процессор / get.rb

module Tasks
  module TaskTwo
    class Processor

      # task_two's processor's custom Get
      class Get
        include Tasks::Processor::Get # mix in generic Get

        def call
          puts "in task_two's Processor's Get"
        end
      end
    end
  end
end

Скорее всего, это как-то связано с автозагрузкой Rails, потому что когда я использую обычный ruby ​​и вручную требую все файлы и пытаюсь запустить код, проблема не возникает. Не могли бы вы объяснить, почему это так, и скажите мне, как лучше всего избежать этой проблемы? Похоже, Rails не нравится тот факт, что у меня есть класс и модуль с одинаковыми именами, и это путает, но я подумал, что это не должно быть проблемой, поскольку они находятся в разных пространствах имен. Я мог бы просто назвать универсальный класс как-то по-другому, но мне бы очень хотелось понять, почему использование одного и того же имени класса как для конкретной реализации, так и для универсального, работает только для первой загрузки, а не для следующей. Большое спасибо за вашу помощь!

P.S. моя версия Ruby 2.5.1, а версия Rails 5.2.1

1 Ответ

1 голос
/ 26 мая 2019

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

https://guides.rubyonrails.org/autoloading_and_reloading_constants.html#when-constants-aren-t-missed

В принципе, каждый раз, когда вы пишете Get.new.call, вам нужно быть более конкретным. Он не знает, какой Get использовать в дереве возможных Get с. Когда вы вызываете его в первый раз, ему не нужно было загружать более одного Get класса, и поэтому он действительно находит нужный. После этого вызова у вас теперь автоматически загружаются БОЛЬШЕ классов, и теперь все начинает становиться рискованным. Вам нужно либо квалифицировать свой Get, чтобы он был более конкретным, и / или использовать require_dependency для принудительной загрузки нужных классов. Однако, учитывая ваш случай, я думаю, что require_dependency просто будет приводить к сбою каждый раз, так как Теперь вы загрузите все классы.

...