Как вызвать expire_fragment из Rails Observer / Model? - PullRequest
29 голосов
/ 26 декабря 2008

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

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

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

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

Ответы [ 8 ]

43 голосов
/ 20 февраля 2009

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

ActionController::Base.new.expire_fragment(key, options = nil) 
13 голосов
/ 05 января 2010

Решение, предоставленное 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')

7 голосов
/ 04 марта 2009

Это довольно легко сделать. Вы можете реализовать предложение Ориона, но вы также можете реализовать более широкую технику, показанную ниже, которая дает вам доступ к текущему контроллеру из любой модели и для любой цели, для которой вы решили нарушить разделение MVC (например, возиться с фрагментным кешем, получая доступ к * 1001). *, генерация путей / 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, но это не обязательно сделайте это, чтобы получить что-то функциональное (хотя и некрасивое и сложное в обслуживании.)

Веселись!

6 голосов
/ 26 декабря 2008

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

  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.

2 голосов
/ 01 июня 2009

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

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

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

2 голосов
/ 02 марта 2009

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

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

- MarkusQ

2 голосов
/ 27 февраля 2009

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

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

1 голос
/ 03 марта 2009

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

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

Дайте мне знать, если это полезно!

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...