8

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

因为JSONa 的表示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是不正确的,因为缓存不是as_json's 责任的一部分。这样做的正确方法是什么?我正在使用 Rails 3.2.15。

4

3 回答 3

2

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

所以我查看了respond_with文档(请参阅在此处输入链接描述),我发现了这一点:

如果未识别出可接受的格式,则应用程序返回“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: ...调用,然后您将有一个很好的解决方案,在它所属的视图中处理缓存。

于 2013-11-14T09:26:15.873 回答
2

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

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

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

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

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

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

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

于 2013-11-20T00:31:37.293 回答
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
于 2013-11-11T22:54:03.793 回答