【问题标题】:How can I cache the JSON representation of an object?如何缓存对象的 JSON 表示?
【发布时间】:2023-04-02 00:35:02
【问题描述】:

在我的Car 模型的JSON 表示中,我包含了一个昂贵方法的输出:

#car.rb
def as_json(options={})
  super(options.merge(methods: [:some_expensive_method]))
end

我有一个标准的索引操作:

#cars_controller.rb
respond_to :json
def index
  respond_with(Car.all)
end

我还在其他地方使用JSON 表示汽车,如下所示:

#user_feed.rb
def feed_contents
  Horse.all + Car.all
end

#user_feeds_controller.rb
respond_to :json
def index
  respond_with(UserFeed.feed_contents)
end

因为carJSON 表示在多个地方使用,我希望它自己被缓存,使用car.cache_key 作为自动过期的缓存键。

这就是我目前的做法:

#car.rb
def as_json(options={})
  Rails.cache.fetch("#{cache_key}/as_json") do
    super(options.merge(methods: [:some_expensive_method]))
  end
end

将缓存代码放入as_json 是不正确的,因为缓存不是as_json 责任的一部分。这样做的正确方法是什么?我正在使用 Rails 3.2.15。

【问题讨论】:

    标签: ruby-on-rails json caching


    【解决方案1】:

    我不确定我是否关注,但我想我会使用序列化字段

    http://api.rubyonrails.org/classes/ActiveRecord/Base.html#label-Saving+arrays%2C+hashes%2C+and+other+non-mappable+objects+in+text+columns

    和 before_save 回调,用于在 Rails 模型更改后更新此字段。

    class Car
       serialize :serialized_car, Hash
       before_save :generate_json_representation
       def generate_json_representation
          self.serialized_car = ...
       end
       def as_json(options={})
          super(options.merge(methods: [:serialized_car]))
       end
    end
    

    【讨论】:

      【解决方案2】:

      我总是将缓存放入 as_json 方法中(active_model_serializer gem 也这样做),但你关于这不正确的评论让我思考,我能理解你的担忧。

      所以我查看了respond_with 文档(参见enter link description here),我发现了这个:

      如果未识别出可接受的格式,应用程序将返回“406 - 不可接受”状态。否则,默认响应是呈现以当前操作和所选格式命名的模板,例如index.html.erb。如果没有可用的模板,则行为取决于所选格式...

      因此,您可以为受影响的操作创建一个 json 模板,然后在此视图中进行缓存。像下面这样的东西应该可以工作:

      # app/views/cars/index.json.erb
      [<%= @cars.map {|car| render partial: 'cars/car.json', locals: {car: car}}.join(',') %>]
      
      # app/views/cars/show.json.erb
      <%= render partial: 'cars/car.json', locals: {car: @car} %>
      
      # more templates for other actions...
      
      # app/views/cars/_car.json.erb
      <%= Rails.cache.fetch("#{car.cache_key}/as_json") { car.as_json } %>
      

      您可能可以稍微清理一下render partial: ... 调用,然后您将有一个很好的解决方案,在它所属的视图中处理缓存。

      【讨论】:

        【解决方案3】:

        首先让我说我为这个问题的努力喝彩。你会发现很难找到比我更热衷于以正确方式设计软件的人——包括用小方法在一个抽象层次上做好一件事。

        但是,我不得不说,在这一点上,我实际上反对问题的前提。我认为您的as_json 是最好的方式。

        最重要的是将客户端与实现分离。 Car#as_json 的客户唯一需要知道的是返回值是 Car 的 JSON 表示。而as_json 做了这件事并且做得很好。缓存和/或获取是应该保留在方法内部的实现细节,它是该任务不可或缺的细节。

        否则就像说任何带有if 语句的方法都是“不正确的”,因为它做了两件事。当然这不是真的。在这两种情况下(使用if 和使用Rails.cache.fetch),方法实现都是一些原子操作,其结果基于条件。

        这是一件事可以走两种方式之一,这与两件事不同。

        同时,我不得不不同意@severin 的回应。它当然会起作用,但是您现在已经将您的视图与实现细节相结合。没有什么,当然也不是你的观点,应该对缓存方法有任何想法,甚至涉及到缓存。在我看来,你现在已经用这种方法泄露了一个抽象。也许这无关紧要,但既然我们在谈论做事的“正确方法”......

        所以我说保持现状。但我相信这是一个很好的问题。

        【讨论】:

          猜你喜欢
          • 2017-06-08
          • 1970-01-01
          • 2011-10-18
          • 2010-11-03
          • 2018-09-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-03-29
          相关资源
          最近更新 更多