在 Rails 中,在仅模型设置中不会调用 Sweeper

发布于 2024-08-05 07:35:50 字数 1153 浏览 6 评论 0原文

我正在开发一个 Rails 应用程序,其中使用页面缓存来存储静态 html 输出。缓存工作正常。不过,我在使缓存过期时遇到了麻烦。

我相信我的问题部分是因为我没有使控制器的缓存过期。为此所需的所有操作都在模型内处理。这似乎应该是可行的,但是我发现的所有对基于模型的缓存过期的引用似乎都已过时,或者无法正常工作。

在我的environment.rb 文件中,我正在调用

config.load_paths += %W( #{RAILS_ROOT}/app/sweepers )

,并且在 /sweepers 文件夹中,有一个 LinkSweeper 文件:

class LinkSweeper < ActionController::Caching::Sweeper
  observe Link

  def after_update(link)
    clear_links_cache(link)
  end

  def clear_links_cache(link)
  # expire_page :controller => 'links', :action => 'show', :md5 => link.md5
    expire_page '/l/'+ link.md5 + '.html'
  end
end

那么...为什么在我更新模型时不删除缓存页面? (过程:使用脚本/控制台,我从数据库中选择项目并保存它们,但它们相应的页面不会从缓存中删除),并且我还调用链接模型中通常会执行的特定方法调用清扫器。两者都不起作用。

如果重要的话,缓存的文件是链接表中键值的 md5 哈希值。缓存的页面存储为 /l/45ed4aade64d427...99919cba2bd90f.html 之类的内容。

从本质上讲,扫地机似乎并没有真正观察到链接。我还读到(此处)它可能可以简单地将清理器添加到environment.rb中的config.active_record.observers中,但这似乎并没有做到这一点(并且我不确定environment.rb中app/sweepers的load_path是否避免了这一点)。

I'm working on a Rails app, where I'm using page caching to store static html output. The caching works fine. I'm having trouble expiring the caches, though.

I believe my problem is, in part, because I'm not expiring the cache from my controller. All of the actions necessary for this are being handled within the model. This seems like it should be doable, but all of the references to Model-based cache expiration that I'm finding seem to be out of date, or are otherwise not working.

In my environment.rb file, I'm calling

config.load_paths += %W( #{RAILS_ROOT}/app/sweepers )

And I have, in the /sweepers folder, a LinkSweeper file:

class LinkSweeper < ActionController::Caching::Sweeper
  observe Link

  def after_update(link)
    clear_links_cache(link)
  end

  def clear_links_cache(link)
  # expire_page :controller => 'links', :action => 'show', :md5 => link.md5
    expire_page '/l/'+ link.md5 + '.html'
  end
end

So ... why isn't it deleting the cached page when I update the model? (Process: using script/console, I'm selecting items from the database and saving them, but their corresponding pages aren't deleting from the cache), and I'm also calling the specific method in the Link model that would normally invoke the sweeper. Neither works.

If it matters, the cached file is an md5 hash off a key value in the Links table. The cached page is getting stored as something like /l/45ed4aade64d427...99919cba2bd90f.html.

Essentially, it seems as though the Sweeper isn't actually observing the Link. I also read (here) that it might be possible to simply add the sweeper to config.active_record.observers in environment.rb, but that didn't seem to do it (and I wasn't sure if the load_path of app/sweepers in environment.rb obviated that).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

你穿错了嫁妆 2024-08-12 07:35:51

我在这里写了一些关于这个主题的文章:Rails Cache Sweeper Confusion。很想听听您的意见。

I wrote a bit about this topic here: Rails Cache Sweeper Confusion. Would love to hear your opinions.

秋千易 2024-08-12 07:35:51

根据 @moiristo 和 @ZoogieZork 的答案,我猜这会起作用(未经测试)。

class LinkSweeper < ActiveRecord::Observer
  include ActionController::Caching::Pages
  # or if you want to expire fragments
  #include ActionController::Caching::Fragments

  observe Link

  def after_update(link)
    expire_page( ... )
    #expire_fragment( ... )
  end
end

Based on @moiristo and @ZoogieZork 's answers, I am guessing this would work (untested).

class LinkSweeper < ActiveRecord::Observer
  include ActionController::Caching::Pages
  # or if you want to expire fragments
  #include ActionController::Caching::Fragments

  observe Link

  def after_update(link)
    expire_page( ... )
    #expire_fragment( ... )
  end
end
谢绝鈎搭 2024-08-12 07:35:50

因此,我尝试了多种不同的方法,看看哪些有效,哪些无效。

再次总结一下情况:我的目标是在对象更新时使缓存页面过期,但在不依赖控制器操作的情况下使它们过期。传统的清理器使用控制器中的一行来通知清理器:它需要发挥作用。在这种情况下,我无法在控制器中使用线路,因为更新是在模型内发生的。普通的清理器教程不起作用,因为它们假定您与数据库对象的主要交互是通过控制器进行的。

如果在阅读本文时,您发现了一种加强我的代码的方法,请发表评论并告诉我。

首先,让我们看看哪些方法确实有效,以防您也陷入困境并需要帮助。

在我尝试的所有方法中,唯一真正有效的方法是在模型的观察者中声明 after_update 命令。在该命令中,我使用了 expire_page 操作的显式命令,并包含了在 paths.rb 中声明的路径。

所以。这有效:

在 config/routes.rb 中:

map.link 'l/:md5.:format',  :controller => 'links', :action => 'show'

在 app/models/link_observer.rb 中:

def after_update(link)
  ActionController::Base.expire_page(app.link_path(:md5 => link.md5))
end

请注意,“md5”特定于我的应用程序。您可能想使用 :id 或其他一些唯一标识符。

我还发现,从正在执行更新的模型中的方法声明 ActionController::Base... 行是有效的。也就是说,在 Link.rb 中,在实际更新数据库的方法中,如果我只是将整行插入其中,它就会起作用。但由于我将来可能希望在其他方法上使该页面缓存过期,所以我宁愿将其提取到观察者中。

现在,让我们看看一些不起作用的事情,以防您在谷歌上搜索这一点。

在 link_observer.rb 内的 after_update(link) 方法中调用“expire_page(...)”不起作用,因为它返回“未定义的方法‘expire_page'”错误

创建 Sweeper 文件 观察到该模型不起作用。我找不到任何错误代码,但它似乎甚至没有意识到它有工作要做。这是在environment.rb 中显式调用“config.load_paths += %W( #{RAILS_ROOT}/app/sweepers )”之后发生的。以防万一我在代码中犯了一些错误,如下所示:

class LinkSweeper < ActionController::Caching::Sweeper
  observe Link

  def after_update(link)
    clear_links_cache(link)
  end

  def clear_links_cache(link)
    # DID NOT WORK    expire_page :controller => 'links', :action => 'show', :md5 => link.md5
    # DID NOT WORK    expire_page '/l/'+ link.md5 + '.html'
    # DID NOT WORK    ActionController::Base.expire_page(app.link_path(:md5 => link.md5))
  end
end

上面的示例在目录 /app/sweepers 中有 link_sweeper.rb 文件。我还尝试将 link_sweeper.rb 放入 app/models 目录中,并尝试使用environment.rb 中的 config.active_record.observers 命令调用它:

config.active_record.observers = :link_observer, :link_sweeper

但这也不起作用。

所以,是的。这些方法中的一种很可能有效,但我弄乱了代码中的某些内容。但我想我所做的一切都是照章办事的。

最后,总结一下:您不想使用 Sweeper 来使页面缓存过期,而是希望在模型的观察者中设置 after_ 回调。您将需要使用 Base.expire_page 方法的显式路径:

def after_update(<model>) # where <model> is the name of the model you're observing
  ActionController::Base.expire_page(app.<model>_path(:id => <model>.id)) # where <model> is the name of the model you're observing
end

希望这能对其他人有所帮助。再说一次,如果您在我的不起作用的代码中看到我应该做不同的事情的任何地方,请告诉我。如果您在我的工作代码中发现一些可以更严格的内容,也请告诉我。

So I've tried a number of different approaches, to see what works, and what doesn't.

Again, to summarize the situation: My goal is to expire cached pages when an object updates, but to expire them without relying on a Controller action. Conventional sweepers use a line in the controller to notify the sweeper that it needs to function. In this case, I can't use a line in the controller, as the update is happening within the model. Normal sweeper tutorials aren't working, as they presume that your main interaction with the database object is through the controller.

If, in reading this, you see a way to tighten up my code, please comment and let me know.

First, let's look at the things that DO work, in case you're stuck on this, too, and need help.

Of all the things I tried, the only thing that really seemed to work was to declare an after_update command in the Observer for the model. In that command, I used the explicit command for the expire_page action, and included a path that had been declared in routes.rb.

So. This works:

In config/routes.rb:

map.link 'l/:md5.:format',  :controller => 'links', :action => 'show'

In app/models/link_observer.rb:

def after_update(link)
  ActionController::Base.expire_page(app.link_path(:md5 => link.md5))
end

Note that that "md5" is specific to my app. You might want to use :id or some other unique identifier.

I also found that declaring that ActionController::Base... line from the method in the model that's doing the updating worked. That is, within Link.rb, in the method that's actually updating the database, if I just stuck that whole line in, it worked. But since I might want to expire that page cache on other methods in the future, I'd rather have it extracted into the Observer.

Now, let's look at some things that DID NOT work, in case you're Googling around for this.

Calling "expire_page(...)" within the after_update(link) method within link_observer.rb did not work, as it returned an "undefined method `expire_page'" error

Creating a Sweeper file that observed the Model did not work. I couldn't find any error codes, but it just seemed to not even be aware that it had a job to do. This was after explicitly calling "config.load_paths += %W( #{RAILS_ROOT}/app/sweepers )" within environment.rb. Just in case I fat-fingered something in that code, here it is:

class LinkSweeper < ActionController::Caching::Sweeper
  observe Link

  def after_update(link)
    clear_links_cache(link)
  end

  def clear_links_cache(link)
    # DID NOT WORK    expire_page :controller => 'links', :action => 'show', :md5 => link.md5
    # DID NOT WORK    expire_page '/l/'+ link.md5 + '.html'
    # DID NOT WORK    ActionController::Base.expire_page(app.link_path(:md5 => link.md5))
  end
end

That above example had the link_sweeper.rb file in a directory, /app/sweepers. I also tried putting link_sweeper.rb within the app/models directory, and tried calling it with the config.active_record.observers command in environment.rb:

config.active_record.observers = :link_observer, :link_sweeper

But that didn't work, either.

So, yeah. It's quite possible that one of these methods would work, and that I messed up something in the code. But I think I did everything by the book.

Ultimately, to summarize: Rather than using a Sweeper to expire page caching, you want to set up an after_ callback in the model's Observer. You'll want to use the explicit path to the Base.expire_page method:

def after_update(<model>) # where <model> is the name of the model you're observing
  ActionController::Base.expire_page(app.<model>_path(:id => <model>.id)) # where <model> is the name of the model you're observing
end

Hopefully this will help someone else down the road. Again, if you see anywhere in my not-working code where I should have done something differently, please let me know. If you see something in my working code that can be tighter, please let me know that, too.

一身软味 2024-08-12 07:35:50

请注意:您可以在 ApplicationController 中使用 cache_sweeper

class ApplicationController < ActionController::Base
  cache_sweeper :my_sweeper
end

class MySweeper < ActionController::Caching::Sweeper
  observe MyModel

  def after_update(my_model)
    expire_page(...)
  end
end

Just a note: you can use cache_sweeper in ApplicationController.

class ApplicationController < ActionController::Base
  cache_sweeper :my_sweeper
end

class MySweeper < ActionController::Caching::Sweeper
  observe MyModel

  def after_update(my_model)
    expire_page(...)
  end
end
转角预定愛 2024-08-12 07:35:50

当我尝试进行片段缓存(rails 3)时,我遇到了同样的问题。无法让扫地机进行观察,因此我决定采用如上所述的解决方案,使其成为 AR 观察者并调用 ApplicationController.new.expire_fragment(...)

I was experiencing the same problem when trying to do fragment caching (rails 3). Couldn't get the sweeper to observe, so I settled for the solution to make it an AR Observer as described above and calling ApplicationController.new.expire_fragment(...).

余生再见 2024-08-12 07:35:50

我确实让这个工作了。我的设置中唯一的细微差别是清理器是 Rails 引擎的一部分;这说明了细微的差异(在引擎的 init 中使用 require 加载清理器文件,而不是将其添加到environment.rb等的加载路径中)。

因此,清扫器像这样加载到引擎的 init.rb 中:

require File.join(File.dirname(__FILE__), 'app', 'sweepers', cached_category_count_sweeper')

我称它为清扫器,因为它“清扫”缓存,但我猜它只是模型上的观察者:

class CachedCategoryCountSweeper < ActiveRecord::Observer
  observe CategoryFeature

  def before_save(cf)
    expire_cache(cf.category_id_was) if cf.category_id_changed?
  end

  def after_save(cf)
    expire_cache(cf.category_id)
  end

  def after_destroy(cf)
    expire_cache(cf.category_id)
  end

  def expire_cache(c)
    ApplicationController.expire_page("/categories/#{c}/counts.xml") if !c.nil?
  end
end

坦率地说,我不喜欢必须这样做对路径进行硬编码,但我尝试添加:

include ActionController:UrlWriter

然后使用路径方法,但它只对我在开发中有用。它在生产中不起作用,因为我的生产服务器使用相对 url 根(而不是虚拟主机),并且内部方法“page_cache_path”始终会得到错误的文件路径,因此它不会过期。

由于这是一个观察者,我添加到环境中。rb:

config.active_record.observers = :cached_category_count_sweeper

最后使用缓存的控制器(不会使其过期,这是通过模型观察者完成的):

class CachedCategoryCountsController < ApplicationController
  caches_page :index

  # GET /cached_category_counts.xml
  def index
    ...
  end
end

无论如何,希望这会有所帮助。

安德烈斯·蒙塔诺

I did get this working. The only slight difference in my setup is that the sweeper is part of a Rails engine; which accounts for slight differences (loading the sweeper file with a require in the engine's init instead of adding it to the load path in environment.rb, etc).

So, the sweeper is loaded in the init.rb of the engine like this:

require File.join(File.dirname(__FILE__), 'app', 'sweepers', cached_category_count_sweeper')

I called it a sweeper because it "sweeps" the cache, but I guess its just an observer on the model:

class CachedCategoryCountSweeper < ActiveRecord::Observer
  observe CategoryFeature

  def before_save(cf)
    expire_cache(cf.category_id_was) if cf.category_id_changed?
  end

  def after_save(cf)
    expire_cache(cf.category_id)
  end

  def after_destroy(cf)
    expire_cache(cf.category_id)
  end

  def expire_cache(c)
    ApplicationController.expire_page("/categories/#{c}/counts.xml") if !c.nil?
  end
end

Frankly, I don't like having to hard-code the path, but I tried adding:

include ActionController:UrlWriter

and then using the path method, but it only worked for me in development. It didn't work in production, because my production server uses a relative url root (instead of virtual hosts) and the internal method "page_cache_path" would consistently get the file path wrong so it couldn't expire.

Since this is an observer, I added to the environment.rb:

config.active_record.observers = :cached_category_count_sweeper

Finally the controller that uses the cache (doesn't expire it, that is done through the model observer):

class CachedCategoryCountsController < ApplicationController
  caches_page :index

  # GET /cached_category_counts.xml
  def index
    ...
  end
end

Anyhow, hope this helps.

Andres Montano

埖埖迣鎅 2024-08-12 07:35:50

通过添加

ActionController::Base.expire_page(app.link_path(:md5 => @link.md5))

到模型本身更新数据库的方法, 我已经能够让它工作。 不过,这感觉有点老套,我很想知道是否有人可以解释为什么它不能与正常的清扫器设置一起使用,以及是否有更优雅的方法来处理这个问题。

代码片段(除了我为自己的应用程序进行的自定义之外)来自 ruby-forum 上的这篇文章.com

I've been able to get it to work, by way of adding

ActionController::Base.expire_page(app.link_path(:md5 => @link.md5))

to the method in the Model itself that's updating the database. This feels somewhat hacky, though, and I'd love to know if anyone can explain why it's not working with the normal sweeper setup, and if there's a more elegant way to handle this.

That snippet of code (apart from customizations I put in for my own app) came from this post on ruby-forum.com.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文