如何缓存对象的JSON表示?

时间:2013-11-11 19:19:07

标签: ruby-on-rails json caching

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

#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

由于JSON的{​​{1}}表示形式在多个地方使用,我希望将其自身缓存,使用car作为自动过期的缓存键。

这就是我目前的做法:

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的可复制性的一部分。这样做的正确方法是什么?我正在使用Rails 3.2.15。

3 个答案:

答案 0 :(得分: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: ...调用,然后你会得到一个很好的解决方案,在它所属的视图中处理缓存。

答案 1 :(得分:2)

首先我要说的是,我赞赏这个问题的努力。你会发现很难找到一个对软件设计更有兴趣的人 - 包括小方法在一个抽象层面做一件事 - 比我更好。

但是,我不得不说,在这个问题上,我实际上反对这个问题的前提。我认为你的as_json是最好的。

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

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

这是可以采用两种方式之一的一种方式,这与两种方式不同。

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

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

答案 2 :(得分:0)

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

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