@@ -72,6 +72,65 @@ def persistent(name, **opts, &block)
7272 end
7373 end
7474
75+ ##
76+ # Cache an ActiveRecord collection. Supports only a basic collection of one type of object. Column selections or
77+ # joins etc. will NOT be respected when the collection is read back out.
78+ # @param name [String] cache key name
79+ # @param value [ActiveRecord::Relation] collection to cache
80+ # @param opts [Hash] options hash - any unlisted options will be passed to the underlying cache
81+ # @option opts [Boolean] :include_community whether to include the community ID in the cache key
82+ def write_collection ( name , value , **opts )
83+ types = value . map ( &:class ) . uniq
84+ if types . size > 1
85+ raise TypeError , "Can't cache more than one type of object via write_collection"
86+ end
87+
88+ data = [ types [ 0 ] . to_s , *value . map ( &:id ) ]
89+ namespaced = construct_ns_key ( name , include_community : include_community ( opts ) )
90+ @underlying . write ( namespaced , data , **opts )
91+ end
92+
93+ ##
94+ # Read an ActiveRecord collection from cache. Returns a basic collection of the records that were cached, with
95+ # no selects or joins applied.
96+ # @param name [String] cache key name
97+ # @param opts [Hash] options hash - any unlisted options will be passed to the underlying cache
98+ # @options opts [Boolean] :include_community whether to include the community ID in the cache key
99+ def read_collection ( name , **opts )
100+ namespaced = construct_ns_key ( name , include_community : include_community ( opts ) )
101+ data = @underlying . read ( namespaced , **opts )
102+ return nil if data . nil?
103+ type = data . slice! ( 0 )
104+ begin
105+ type . constantize . where ( id : data )
106+ rescue NameError
107+ delete ( name )
108+ nil
109+ end
110+ end
111+
112+ ##
113+ # Fetch an ActiveRecord collection from cache if it is present, otherwise cache the value returned by +block+.
114+ # @param name [String] cache key name
115+ # @param opts [Hash] options hash - any unlisted options will be passed to the underlying cache
116+ # @option opts [Boolean] :include_community whether to include the community ID in the cache key
117+ # @yieldreturn [ActiveRecord::Relation]
118+ def fetch_collection ( name , **opts , &block )
119+ existing = if exist? ( name , include_community : include_community ( opts ) )
120+ read_collection ( name , **opts )
121+ end
122+ if existing . nil?
123+ unless block_given?
124+ raise ArgumentError , "Can't fetch collection without a block given"
125+ end
126+ data = block . call
127+ write_collection ( name , data , **opts )
128+ data
129+ else
130+ existing
131+ end
132+ end
133+
75134 # We have to statically report that we support cache versioning even though this depends on the underlying class.
76135 # However, this is not really a problem since all cache stores provided by activesupport support the feature and
77136 # we only use the redis cache (by activesupport) for QPixel.
0 commit comments