Ну проблема в следующем. У меня иерархия моделей следующая:

class A < ActiveRecord::Base
end

Class B < A
end

class C1 < B
end

class C2 < B
end

В одном из контроллеров мне нужно искать B, C1 или C2. Rails делает это, если загружены связанные модели. Таким образом, запрос sql должен (и в идеале будет) содержать что-то вроде

B.find 224 
# => SELECT "bs".* FROM "bs" WHERE "bs"."type" IN ('B','C1','C2') AND "bs"."id" = 224 LIMIT 1

И это как раз то, что нужно. Однако оказывается, что модели выгружаются при каждом запросе и больше не загружаются (по этой причине не уверен). В том случае, если моделей нет в памяти, ActiveRecord сделает запрос типа

B.find 224 
# => SELECT "bs".* FROM "bs" WHERE "bs"."type" IN ('B') AND "bs"."id" = 224 LIMIT 1

Если вы просто вызовете C1 или C2, прежде чем найдете, их типы будут включены в sql

C1
B.find 224 
# => SELECT "bs".* FROM "bs" WHERE "bs"."type" IN ('B','C1') AND "bs"."id" = 224 LIMIT 1

Я попытался загрузить эти модели в инициализаторы. eval C1, require 'app / models / c1.rb', require_dependancy 'app / models / c1.rb', но ни один из них не работает. Собственно, все они работали одно время. Только один раз после запуска сервера. Подозреваю, что в производственной среде такой проблемы не будет, но это очень раздражает.

Уродливое решение - вызвать C1 и C2 после определения B, в этом случае все сработало так, как ожидалось, но, как я уже сказал, это уродливо

class B < A
end
C1, C2

Есть лучшие идеи?

Обновление перешло к ответу.

1
Max 27 Авг 2011 в 17:03

3 ответа

Лучший ответ

Хорошо, я думаю, что нашел какое-то решение. Не очень впечатляет, но вроде работает:

Мне нужно добавить хак в ActiveSupport :: Dependencies.

Итак, вот список взломов:

    #lib/system_hacks/active_support.rb
    module ActiveSupport #:nodoc:
      module Dependencies #:nodoc:

        #this array contains all the constants that have to be constantly loaded
        mattr_accessor :constantly_loaded_files
        self.constantly_loaded_files = []

        #redefining the method, with additional line
        def remove_unloadable_constants!
          autoloaded_constants.each { |const| remove_constant const }
          autoloaded_constants.clear
          Reference.clear!
          explicitly_unloadable_constants.each { |const| remove_constant const }
          #a hack
          constantly_loaded
        end

        #will simply make a call for each constants
        #in that case they will be updated... but still in the memory
        def constantly_loaded
          constantly_loaded_files.each do |file|
            file.to_s.classify.constantize
          end
        end
      end
    end

И в инициализаторах небольшой файл для загрузки хака и назначения массива констант:

  #config/initializers/model_loader.rb
  require File.join(Rails.root, 'lib','system_hacks','active_support')
    module ActiveSupport #:nodoc:
      module Dependencies #:nodoc:
        self.constantly_loaded_files = [A, B, C1, C2]
      end
    end

Если у вас есть какие-либо комментарии, я с радостью их выслушаю.

0
Max 5 Сен 2012 в 07:58

Можете ли вы объяснить, чего пытается достичь эта структура данных? Я вижу шаблон наследования одной таблицы, настроенный в наследовании модели, но я не могу понять, какое исходное решение вы пытаетесь достичь.

0
Sasha 29 Ноя 2011 в 07:46

Если вы хотите отключить перезагрузку классов при разработке, вы можете это сделать. В config / environment / development.rb просто сделайте:

Config.cache_classes = правда

Вместо значения по умолчанию false. Это отключит перезагрузку классов в режиме разработки для всех классов, что отчасти неудобно. Теоретически должен быть способ освободить только определенные классы от перезагрузки класса в режиме разработки, но он продолжает меняться от версии rails к версии rails, и часто работает не совсем правильно, и я не отслеживал, как это сделать. это в текущих рельсах или если он работает. Мне было бы неудобно с вашей хакерской попыткой реализовать эту функцию самостоятельно с помощью обезьяньего патча - тот факт, что Rails пытался официально поддерживать эту функцию в прошлом, но потерпел неудачу, указывает на то, что это сложно сделать правильно, и вы, вероятно, не сделали этого. т.

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

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

Существует также функция «полиморфной ассоциации» Rails, которая может быть вам полезна, а может и не оказаться.

В принципе, извините, не уверен, может ли AR делать то, что вы хотите, но на вашем месте я бы попробовал это, только если смогу найти доказательства того, что AR действительно поддерживает его - даже если вы заставляете его работать, отключив dev-mode при перезагрузке класса, вероятно, вас ждут другие странные ошибки, если вы пытаетесь сделать что-то, что AR не поддерживает. AR - сложный код.

0
jrochkind 9 Дек 2011 в 05:18