【问题标题】:Cannot cache from decorator (draper)无法从装饰器(draper)缓存
【发布时间】:2012-09-15 13:02:09
【问题描述】:

到目前为止,缓存是我的视图代码中逻辑最密集的部分,所以我想从装饰器内部进行片段缓存,但是我做不到。

当我通过我的装饰器执行此操作时:

def cached_name
  h.cache do
   "a name here"
  end
end

我明白了:

当你没想到时,你有一个 nil 对象!你可能有 期望一个 Array 的实例。评估时发生错误 无长度

我从控制器内部实例化我的装饰器

@presenter = SomePresenter::new

我将 HAML 用于我的视图

我怎样才能成功地从我的装饰器中缓存,所以我的视图可以做这样的事情

= @decorator.cached_logic_heavy_stuff

更新:我创建了一个 git 存储库来显示我的问题:https://github.com/houen/presenter_caching

更新:这可能有效 - 请参阅 repo

  include Haml::Helpers
  def another_way_to_try
    self.init_haml_helpers
    buffer = haml_buffer.buffer

    h.with_output_buffer(buffer) do
      h.cache do
        h.concat "i should still not be empty"
      end
    end
  end

【问题讨论】:

  • 你在这里缓存什么?我没有看到任何块作为参数

标签: ruby-on-rails caching haml draper


【解决方案1】:

我建议直接使用Rails.cache 可能会解决您的问题;我们在使用 Rails 4 的装饰器中做同样的事情。

def cached_name
  Rails.cache.fetch(source) do
    source.name # etc.
  end
end

【讨论】:

    【解决方案2】:

    如果您使用的是 Draper,我相信您不需要显式传递视图上下文。您可能希望在实例化时将模型或集合传递给您的 draper。例子:

    class UserDecorator < Draper::Base
      decorates :user
    
      # additional methods
    end
    
    # in the controller
    @presenter = UserDecorator.new(@user)       # for an instance
    @presenter = UserDecorator.decorate(@users) # for a collection
    

    我怀疑您收到的 nil 对象错误来自代码中未列出的另一个方法调用。

    至于装饰器中的片段缓存,您需要使用 concat 辅助方法来使其在装饰器中工作:

    # your decorator class
    def cached_name
      h.cache("some_cache_key") do
        h.concat "a name here"
      end
    end
    

    【讨论】:

    • 您需要从控制器中传递它。该错误肯定是由于缓存造成的。很遗憾,您的回答不起作用:
    • def test h.cache do h.concat "hej" end end
    • 同样的错误:当你没有预料到的时候,你有一个 nil 对象!您可能期望有一个 Array 的实例。评估 nil 时发生错误。
    • 你能提供一个堆栈跟踪吗?此外,draper 的 README 中有一些很好的示例,说明如何在不传递 view_context 的情况下进行实例化:github.com/jcasimir/draper
    • 更好 - 我将在 5 分钟内链接到显示错误和问题的公共 git repo :)
    【解决方案3】:

    Rails 的cache 方法尝试根据调用它的视图推断缓存键。由于您实际上并不是从视图中调用它(而是从装饰器类的实例内部),所以我希望它在尝试构建缓存键时会爆炸。

    您可以尝试通过h.cache "your cache key" do 显式传递缓存键。通过完整的堆栈跟踪,您可以找出它在哪里引发异常,然后也可以解决这个问题。但是,如果没有完整的堆栈跟踪,就很难为您提供帮助。

    编辑:看看Rails' caching code,我认为这可能是一个更深层次的问题;它试图获取 output_buffer 的长度,这在您的视图上下文之外(即在 Draper 中)将不可用。您可以尝试添加:

    def output_buffer
      h.output_buffer
    end
    

    但如果不对其进行测试,我认为如果不进行更多工作,它可能无法完全按计划工作。这只是一个粗略的猜测 - 如果这确实是问题,我会感到惊讶,但希望它能让你走上正确的道路。

    源中的注释:

    # VIEW TODO: Make #capture usable outside of ERB
    # This dance is needed because Builder can't use capture
    

    表示这不是一个完全解决的问题,因此您可能需要在 Rails 内部进行一些挖掘才能使其正常工作。

    【讨论】:

    • 是的,我也得出了这个结论,你实际上可以使用 h.with_output_buffer 来做,但是问题是 HAML 使用的输出缓冲区与 ERB 不同,所以它需要使用那个输出缓冲,也是。这是一项艰巨的任务,我知道 :)
    • 嗯 - 我想也许我现在明白了:D 看看我的编辑。克里斯,如果你能验证这是否有效并且不会引起问题,我会给你赏金:)
    • 哈——它有效。实际上,你们俩都帮助了答案。希望我能给你们两个赏金......
    • 不错。将其包装在一个将cache 直接暴露给装饰器的小包装器中可能很容易;这可能是 Draper 本身的上游补丁的良好候选者。
    • 克里斯,这是一个非常好的主意 - 如果我们可以通过一些检查 model.responds_to?(:cache_key) 的 Ruby 检查来解决这个问题,那么几乎可以缓存所有内容在(正确使用的)装饰器中。这可能有助于实现这一目标37signals.com/svn/posts/… ...我会看看它是否有意义,如果是这样,请尝试让哥本哈根 Ruby Brigade (copenhagenrb.dk) 的其他一些人参与实现这一目标。装饰器可以选择说 :cache_aggressively => true
    【解决方案4】:

    这行得通:

      include Haml::Helpers
      def another_way_to_try
        self.init_haml_helpers
        buffer = haml_buffer.buffer
        h.with_output_buffer(buffer) do
          h.cache "some_key10", :expires_in => 10.seconds do
            h.concat "i should still not be empty 2"
          end
        end
      end
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2022-01-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-07-29
      • 1970-01-01
      相关资源
      最近更新 更多