2013-11-11 4 views
8

В JSON представлении моей 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 

Потому что JSON представление 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.

ответ

2

Позвольте мне сначала сказать, что я приветствую усилия этого вопроса. Вам будет сложно найти кого-то, кто больше заинтересован в разработке программного обеспечения правильным способом - в том числе с помощью небольших методов, которые делают одну вещь на одном уровне абстракции, - чем я.

Однако я должен сказать, что на этом я фактически возражаю против предпосылки вопроса. Я думаю, что ваш as_json лучше всего.

Что важнее всего, так это отделить клиентов от реализации. Единственное, что нужно знать клиентам Car#as_json, это то, что возвращаемое значение представляет собой представление JSON для Car. И as_json делает это и делает это хорошо. Кэширование и/или выборка - это деталь реализации, которая должна оставаться внутри метода, и это деталь, которая является неотъемлемой частью этой одной задачи.

Говорить иначе было бы сказать, что любой метод с оператором if является «неправильным», потому что он выполняет две функции. Конечно, это не так. В обоих случаях (с использованием if и с использованием Rails.cache.fetch) реализация метода представляет собой некоторое атомное действие, результат которого основан на условии.

Это одна вещь, которая может идти одним из двух способов, что не то же самое, что две вещи.

Между тем, я должен был бы не согласиться с ответом @ severin. Конечно, это сработает, но теперь вы связали свое мнение с деталями реализации. Ничто, и, конечно, не ваше мнение, должно иметь какое-либо представление о методе кеширования или даже о том, что задействовано кэширование. На мой взгляд, вы уже пропустили абстракцию с таким подходом. Возможно, это не имеет значения, но поскольку мы говорим о «правильном пути», чтобы делать что-то ...

Так что я говорю, держите вещи так, как они есть. Но я считаю, что это большой вопрос.

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 обратного вызова для обновления этого поля после изменения модели рельсов.

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_jsonactive_model_serializer камень делает это тоже), но ваше замечание относительно этого не является правильным заставило меня задуматься, и я могу понять ваши проблемы.

Так что я просматривал 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: ... вызывает немного, а затем вы бы красивое решение с кэшированием обрабатываются в представлении, где она принадлежит.

Смежные вопросы