Module: SupportTableCache
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/support_table_cache.rb,
lib/support_table_cache/associations.rb,
lib/support_table_cache/fiber_locals.rb,
lib/support_table_cache/memory_cache.rb,
lib/support_table_cache/find_by_override.rb,
lib/support_table_cache/relation_override.rb
Overview
This concern can be added to a model to add the ability to look up entries in the table using Rails.cache when calling find_by rather than hitting the database every time.
Defined Under Namespace
Modules: Associations, ClassMethods, FindByOverride, RelationOverride Classes: FiberLocals, MemoryCache
Class Method Summary collapse
-
.cache ⇒ ActiveSupport::Cache::Store
Get the global cache (will default to
Rails.cacheif running in a Rails environment). -
.cache=(value) ⇒ void
Set the global cache to use.
-
.cache_key(klass, attributes, key_attribute_names, case_sensitive) ⇒ Array(String, Hash)?
private
Generate a consistent cache key for a set of attributes.
-
.cache_key_for_query(klass, attributes) ⇒ Array(String, Hash)?
private
Find the cache key for a query on a set of attributes by matching the attributes against the cacheable attribute configuration for a class.
-
.cacheable_query?(klass, attributes) ⇒ Boolean
private
Return true if a query on a set of attributes can be looked up in the cache.
-
.disable(disabled = true) { ... } ⇒ Object?
Disable the caching behavior for all classes.
-
.disabled? ⇒ Boolean
Return true if caching has been disabled.
-
.enable { ... } ⇒ Object?
Enable the caching behavior for all classes.
- .fiber_local_value(varname) ⇒ Object
-
.merge_query_attributes(klass, scope_conditions, attributes) ⇒ Hash?
private
Merge the conditions from a relation's where clause with the attributes passed to a finder method.
-
.open_transaction?(klass) ⇒ Boolean
private
Return true if there is an open transaction on the class' connection.
-
.testing! { ... } ⇒ Object
Enter test mode for a block.
-
.testing_cache ⇒ SupportTableCache::MemoryCache?
private
Get the current test mode cache.
- .with_fiber_local(varname, value, &block) ⇒ Object
Instance Method Summary collapse
-
#uncache ⇒ void
Remove the cache entry for this record.
Class Method Details
.cache ⇒ ActiveSupport::Cache::Store
Get the global cache (will default to Rails.cache if running in a Rails environment).
206 207 208 209 210 211 212 213 214 |
# File 'lib/support_table_cache.rb', line 206 def cache if testing_cache testing_cache elsif @cache != NOT_SET @cache elsif defined?(Rails.cache) Rails.cache end end |
.cache=(value) ⇒ void
This method returns an undefined value.
Set the global cache to use.
198 199 200 201 |
# File 'lib/support_table_cache.rb', line 198 def cache=(value) value = MemoryCache.new if value == :memory @cache = value end |
.cache_key(klass, attributes, key_attribute_names, case_sensitive) ⇒ Array(String, Hash)?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Generate a consistent cache key for a set of attributes. It will return nil if the attributes are not cacheable.
250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 |
# File 'lib/support_table_cache.rb', line 250 def cache_key(klass, attributes, key_attribute_names, case_sensitive) return nil if attributes.blank? || key_attribute_names.blank? sorted_names = attributes.keys.map(&:to_s).sort return nil unless sorted_names == key_attribute_names sorted_attributes = {} sorted_names.each do |attribute_name| value = (attributes.key?(attribute_name) ? attributes[attribute_name] : attributes[attribute_name.to_sym]) # Cast the value through the attribute type so that equivalent values (e.g. a symbol # and a string, or "5" and 5) always produce the same cache key. Otherwise entries # could be written under keys that the invalidation callbacks can never delete. value = klass.type_for_attribute(attribute_name).cast(value) if !case_sensitive && (value.is_a?(String) || value.is_a?(Symbol)) value = value.to_s.downcase end sorted_attributes[attribute_name] = value end [klass.name, sorted_attributes] end |
.cache_key_for_query(klass, attributes) ⇒ Array(String, Hash)?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Find the cache key for a query on a set of attributes by matching the attributes against the cacheable attribute configuration for a class. Returns nil if the query cannot be cached.
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 |
# File 'lib/support_table_cache.rb', line 280 def cache_key_for_query(klass, attributes) return nil if attributes.blank? Array(klass.support_table_cache_by_attributes).each do |attribute_names, case_sensitive, where| # Cast both sides through the attribute type so that equivalent values (e.g. 1 and "1") # match the where clause the same way they are matched when building cache keys. where_matched = where.nil? || where.all? do |name, value| type = klass.type_for_attribute(name) attributes.include?(name) && type.cast(attributes[name]) == type.cast(value) end next unless where_matched key_attributes = (where ? attributes.except(*where.keys) : attributes) key = cache_key(klass, key_attributes, attribute_names, case_sensitive) return key if key end nil end |
.cacheable_query?(klass, attributes) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Return true if a query on a set of attributes can be looked up in the cache.
306 307 308 309 310 |
# File 'lib/support_table_cache.rb', line 306 def cacheable_query?(klass, attributes) return false if attributes.nil? !cache_key_for_query(klass, attributes).nil? end |
.disable(disabled = true) { ... } ⇒ Object?
Disable the caching behavior for all classes. If a block is specified, then caching is only disabled for that block. If no block is specified, then caching is disabled globally.
166 167 168 169 170 171 172 |
# File 'lib/support_table_cache.rb', line 166 def disable(disabled = true, &block) if block SupportTableCache.with_fiber_local("support_table_cache_disabled", !!disabled, &block) else @disabled = !!disabled end end |
.disabled? ⇒ Boolean
Return true if caching has been disabled.
185 186 187 188 189 190 191 192 |
# File 'lib/support_table_cache.rb', line 185 def disabled? block_value = SupportTableCache.fiber_local_value("support_table_cache_disabled") if block_value.nil? !!@disabled else block_value end end |
.enable { ... } ⇒ Object?
Enable the caching behavior for all classes. If a block is specified, then caching is only enabled for that block. If no block is specified, then caching is enabled globally.
179 180 181 |
# File 'lib/support_table_cache.rb', line 179 def enable(&block) disable(false, &block) end |
.fiber_local_value(varname) ⇒ Object
358 359 360 |
# File 'lib/support_table_cache.rb', line 358 def fiber_local_value(varname) @fiber_locals[varname] end |
.merge_query_attributes(klass, scope_conditions, attributes) ⇒ Hash?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Merge the conditions from a relation's where clause with the attributes passed to a finder method. A cache key can only represent a single value per attribute, so if both specify a different value for the same attribute the query cannot be cached; the database has to apply both conditions.
322 323 324 325 326 327 328 329 330 331 332 333 334 |
# File 'lib/support_table_cache.rb', line 322 def merge_query_attributes(klass, scope_conditions, attributes) return attributes if scope_conditions.blank? conflict = scope_conditions.any? do |name, value| next false unless attributes.include?(name) type = klass.type_for_attribute(name) type.cast(value) != type.cast(attributes[name]) end return nil if conflict scope_conditions.merge(attributes) end |
.open_transaction?(klass) ⇒ Boolean
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Return true if there is an open transaction on the class' connection. Queries should not be cached inside a transaction since they could return uncommitted data that would be invalid if the transaction is rolled back. Transactions opened with joinable: false (i.e. Rails transactional test fixtures) are ignored.
344 345 346 347 348 349 350 351 352 353 354 355 356 |
# File 'lib/support_table_cache.rb', line 344 def open_transaction?(klass) return false unless klass.connection_pool.active_connection? connection = klass.connection return false unless connection.transaction_open? # current_transaction and joinable? are internal Rails APIs. If a future Rails version # changes them, fail safe by treating the transaction as open (bypassing the cache) # rather than raising. There is a spec asserting the API exists so an incompatible # Rails upgrade fails explicitly. transaction = connection.current_transaction !transaction.respond_to?(:joinable?) || transaction.joinable? end |
.testing! { ... } ⇒ Object
Enter test mode for a block. New caches will be used within each test mode block. You can use this to wrap your test methods so that cached values from one test don't show up in subsequent tests.
222 223 224 225 226 227 228 229 |
# File 'lib/support_table_cache.rb', line 222 def testing!(&block) save_val = SupportTableCache.fiber_local_value("support_table_cache_test_cache") if save_val.nil? SupportTableCache.with_fiber_local("support_table_cache_test_cache", MemoryCache.new, &block) else yield end end |
.testing_cache ⇒ SupportTableCache::MemoryCache?
This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.
Get the current test mode cache. This will only return a value inside of a testing! block.
235 236 237 238 239 |
# File 'lib/support_table_cache.rb', line 235 def testing_cache return nil if @cache.nil? SupportTableCache.fiber_local_value("support_table_cache_test_cache") end |
.with_fiber_local(varname, value, &block) ⇒ Object
362 363 364 |
# File 'lib/support_table_cache.rb', line 362 def with_fiber_local(varname, value, &block) @fiber_locals.with(varname, value, &block) end |
Instance Method Details
#uncache ⇒ void
This method returns an undefined value.
Remove the cache entry for this record.
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 |
# File 'lib/support_table_cache.rb', line 370 def uncache cache_by_attributes = self.class.support_table_cache_by_attributes return if cache_by_attributes.blank? cache = self.class.send(:support_table_cache_for_invalidation) return if cache.nil? cache_by_attributes.each do |attribute_names, case_sensitive| attributes = {} attribute_names.each do |name| attributes[name] = self[name] end cache_key = SupportTableCache.cache_key(self.class, attributes, attribute_names, case_sensitive) cache.delete(cache_key) end end |