I am trying to cache an AppLabel serializer, but I'm running into a problem with locales. The app labels should return different values depending on the current user's app_language, so that needs to be factored in with regard to cache keys.
Here is my serializer:
class AppLabelSerializer < ActiveModel::Serializer
cache key: 'app_label', expires_in: 3.hours
attributes :id, :key, :label, :label_plural
def key
object.app_label_dictionary.key
end
end
I tried interpolating into the key like this:
cache key: "#{scope.app_language.name}/app_label", expires_in: 3.hours
And I keep getting an undefined method 'scope' error. I double checked that the interpolation is working with this:
cache key: "#{ 1 + 1 }/app_label", expires_in: 3.hours
And that works just fine. I double checked that the scope is being passed into the serializer with this:
class AppLabelSerializer < ActiveModel::Serializer
cache key: "app_labels", expires_in: 3.hours
attributes :id, :key, :label, :label_plural, :cache_key_language
def key
object.app_label_dictionary.key
end
def cache_key_language
scope.app_language.name
end
end
And the app language is correctly being returned. So, I tried passing the cache_key_language into the cache key like this:
cache key: "#{ cache_key_language }/app_label", expires_in: 3.hours
But that returns undefined method 'cache_key_language'.
I'm stumped. Does anyone have any ideas?
I could provide a PR to allow cache key to be a Proc but I would like to know @joaomdmoura thoughts on this. Until then, monkey patch is the only solution here IMO. Here are two way that you could try:
class Proc
def to_s
call
end
end
class WhateverSerializer < ActiveModel::Serializer
cache key: ->(){ @serializer.scope.whatever }
end
module ActiveModel
class Serializer
class Adapter
def cache_key
key = @klass._cache_key
key = @cached_serializer.instance_exec key if key.is_a?(Proc)
key ? "#{key}/#{@cached_serializer.object.id}-#{@cached_serializer.object.updated_at}" : @cached_serializer.object.cache_key
end
end
end
end
class WhateverSerializer < ActiveModel::Serializer
cache key: ->(){ scope.whatever }
end
Disclaimer: this was not tested and might not be accurate.
@groyoh Awesome! I will give that a try for now. Thanks a bunch.
@eliduke you're welcome. If it doesn't work let me know. I'll take some time to try it out and fix it.
Hey fellows! :smile:
This is not the first time I hear this request. I'm not sure why I'm not so into doing this lol :stuck_out_tongue_closed_eyes:
But indeed @groyoh, this would be a great implementation.
It doesn't pleases me to need to use scope in order to do this. Another solution would be to define it as an option that you pass to the serializer, but keep this cache options apart from each other might not be so cool either. We also can make all cache options available through serializer options, but idk, I need to give it some thought.
I gave it a shot, and it didn't seem to work. Unfortunately I don't know enough about Procs to really be able to figure this one out on my own. But I tried both options and got the same "wrong number of arguments (1 for 0)" error on this line:
cache key: ->(){ "#{ scope.whatever }/app_labels" }, expires_in: 3.hours
I made a mistake in my snippet. Here is the correct one, I've tested it and it works for me:
module ActiveModel
class Serializer
class Adapter
def cache_key
key = @klass._cache_key
key = @cached_serializer.instance_exec &key if key.is_a?(Proc)
key ? "#{key}/#{@cached_serializer.object.id}-#{@cached_serializer.object.updated_at}" : @cached_serializer.object.cache_key
end
end
end
end
class AppLabelSerializer < ActiveModel::Serializer
cache key: ->(){ scope.app_language.name }, expires_in: 3.hours
attributes :id, :key, :label, :label_plural
def key
object.app_label_dictionary.key
end
end
BTW, make sure that you pass in the scope everytime or it will raise an error.
That worked! That's awesome. Thanks so much. I'm curious to see if this is some functionality that works it's way in more officially. I'm sure that it could be useful to others.
@joaomdmoura Should I close this issue or would you like to use it as a reminder? Do you want to flag it as a possible feature request?
I'm closing it for now :smile:
@groyoh in case you want to moves forward with this PR that we discussed just open a new issue so we can talk about what should be the implementation. :stuck_out_tongue_closed_eyes:
btw @eliduke could you replicate this solution for now at StackOverflow, just to help other ppl to easily find it :smile:
Here's the solution I posted on StackOverflow:
Let me know if there are any changes that I should make to it.
Thanks again for all your help!
I jump bumped into this now.
Isn't there any way to cache different versions of a fragment for different languages? If you use Globalize on your app you are pretty much left with stale data depending on which language hits the cache first. On Globalize's website they advise to redefine cache_key including the current locale so rails cache knows that we got two versions of the same object depending on the locale. As of now AMS does not seem to be calling cache_key on the object about to be serialized in order to figure out which version to use.
Or perhaps I went the wrong direction to deal with this. Any advice?
Oh! I figured this one out. When I read the README of this repo I saw somewhere that the key: option would be preprended to the key on a given form I dont recall. When I removed the key option then the cache_key method got called. Sorry to bother guys, RTFC to the win.
hahah @zaaroth glad that you made it @zaaroth
Well I am trying to do fragment caching with dynamic key as outlined in this thread. But it's not working when I specify except. Below is my cache code of serializer
cache key: ->(){ "#{@options[:platform]}/outlet/#{object.id}" }, expires_in: 20.minutes, except: [:open, :opens_at]
Cache key is not being appended with @options[:platform] value. However when I remove the except, it works as expected. Can someone please help me in this regards?
I tried with the except option but doesn't works. Someone can help?
I'm using AMS 0.10.0
Guys, you can solve except issue adding code below on their serializer:
def _cache_except
[:attribute_1, :attribute_2]
end
Most helpful comment
Well I am trying to do fragment caching with dynamic key as outlined in this thread. But it's not working when I specify
except. Below is my cache code of serializerCache key is not being appended with
@options[:platform]value. However when I remove theexcept, it works as expected. Can someone please help me in this regards?