Подтвердить что ты не робот

Как вызвать expire_fragment из Rails Observer/Model?

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

Я создал модуль в lib/cache_helper.rb со всеми моими помощниками по истечению срока действия, внутри каждого - всего лишь куча вызовов expire_fragment. У меня есть все мои настройки кэширования подметей под/app/подметальные машины и включить "включить CacheHelper "в моем контроллере приложений, так что срок действия кеша приложение при вызове через контроллеры прекрасно работает.

Тогда у меня есть некоторые внешние демоны, и особенно некоторые повторяющиеся задачи cron, которые вызывают задачу рейка, которая вызывает определенную метод. Этот метод выполняет некоторые операции обработки и ввода записей в модель, после которой мне нужно истечь кеш.

Какой лучший способ сделать это, поскольку я не могу указать утилизатор кэша в модели. Кажется, что правильные наблюдатели являются лучшим решением, но жалуется на expire_fragment undefined и т.д., я даже попытался включить классы кэширования ActionController в наблюдателя но это не сработало. Мне бы хотелось, чтобы некоторые идеи о том, как создать решение для этого. Благодарю.

4b9b3361

Ответ 1

Отказ от ответственности: мои рельсы немного ржавые, но это или что-то вроде этого должно работать

ActionController::Base.new.expire_fragment(key, options = nil) 

Ответ 2

Решение, предлагаемое компанией Orion, отлично работает. В качестве улучшения и удобства я поставил следующий код в config/initializers/active_record_expire_fragment.rb

class ActiveRecord::Base
  def expire_fragment(*args)
    ActionController::Base.new.expire_fragment(*args)
  end
end

Теперь вы можете использовать expire_fragment во всех экземплярах ActiveRecord:: Base, например. User.first.expire_fragment('user-stats')

Ответ 3

Это довольно легко сделать. Вы можете реализовать предложение Ориона, но вы также можете реализовать более широкую технику, показанную ниже, которая дает вам доступ к текущему контроллеру из любой модели и для любой цели, которую вы решили нарушить разделение MVC для (например, возиться с кешем фрагмента, обращаясь к current_user, генерация путей/URL-адресов и т.д.)

Чтобы получить доступ к текущему контроллеру запроса (если таковой имеется) из любой модели, добавьте следующее в environment.rb или, что более желательно, к новому плагину (например, создайте vendor/plugins/controller_from_model/init.rb содержащий код ниже):

module ActiveRecord
  class Base
    protected
      def self.thread_safe_current_controller #:nodoc:
        Thread.current[:current_controller]
      end

      def self.thread_safe_current_controller=(controller) #:nodoc:
        Thread.current[:current_controller] = controller
      end

      # pick up the correct current_controller version
      #  from @@allow_concurrency
      if @@allow_concurrency
        alias_method :current_controller,  :thread_safe_current_controller
        alias_method :current_controller=, :thread_safe_current_controller=
      else
        cattr_accessor :current_controller
      end
  end
end

Затем в app/controllers/application.rb,

class ApplicationController < ActionController::Base
  before_filter { |controller|
    # all models in this thread/process refer to this controller
    #  while processing this request
    ActiveRecord::Base.current_controller = controller
  }

  ...

Затем из любой модели,

if controller = ActiveRecord::Base.current_controller
  # called from within a user request
else
  # no controller is available, didn't get here from a request - maybe irb?
fi

Во всяком случае, в вашем конкретном случае вы можете захотеть ввести код в свои различные потомки ActiveRecord::Base, когда загружаются соответствующие классы контроллеров, чтобы фактический код, поддерживающий контроллер, все еще находился в app/controllers/*.rb, но это не обязательно сделайте это, чтобы получить что-то функциональное (хотя и уродливое и трудно поддерживаемое).

Удачи!

Ответ 4

В одном из моих сценариев я использую следующий хак:

  require 'action_controller/test_process'

  sweepers = [ApartmentSweeper]

  ActiveRecord::Base.observers = sweepers
  ActiveRecord::Base.instantiate_observers

  controller = ActionController::Base.new
  controller.request = ActionController::TestRequest.new
  controller.instance_eval do
    @url = ActionController::UrlRewriter.new(request, {})
  end

  sweepers.each do |sweeper|
    sweeper.instance.controller = controller
  end

Затем, после вызова обратных вызовов ActiveRecord, подметальные машины могут вызывать expire_fragment.

Ответ 5

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

Невозможно ли написать какое-либо действие в контроллере, которое делает то, что вы хотите, а затем вызвать действие контроллера из вашей задачи рейка?

Ответ 6

Почему бы вам не потребоваться, чтобы ваши внешние задачи грабли вызывали метод истечения срока действия на контроллере. Тогда вы по-прежнему являетесь MVC-совместимым, вы не строите зависимость от какого-то взлома и т.д.

В этом случае, почему бы вам просто не поместить все функции демона/внешнего устройства на контроллер и не позвольте rake/cron просто вызвать это. Было бы легче поддерживать нагрузку.

- MarkusQ

Ответ 7

Не будет ли проще и чисто просто передать текущий контроллер в качестве аргумента для вызова метода модели? Пример:

def delete_cascade(controller)

  self.categories.each do |c|
    c.delete_cascade(controller)
    controller.expire_fragment(%r{article_manager/list/#{c.id}.*})                
  end
  PtSection.delete(self.id)
  controller.expire_fragment(%r{category_manager/list/#{self.id}.*})        
end

Вы можете получить доступ ко всем общедоступным методам и свойствам контроллера из модели. Пока вы не изменяете состояние контроллера, это должно быть хорошо.

Ответ 8

Это может не сработать для того, что вы делаете, но вы можете определить пользовательский вызов на своей модели:

class SomeModel < ActiveRecord::Base
    define_callback :after_exploded

    def explode
        ... do something that invalidates your cache ...
        callback :after_exploded
    end
end

Затем вы можете использовать подметальную машину, как обычно:

class SomeModelSweeper < ActionController::Caching::Sweeper
  observe SomeModel 

    def after_exploded(model)
      ... expire your cache
    end
end

Сообщите мне, если это полезно!