ArtirixCacheService
The basic use of this gem is to compile a cache key based on a given key prefix and some extra variables or arguments, with some helper methods. It also helps with the cache options.
Usage: .key
The basic way of using it is with the key method, which will return the key based on the given arguments.
ArtirixCacheService.key :some_key # => will return a string with the cache key to usePrefix
The service can use a prefix to be applied to all keys
ArtirixCacheService.register_key_prefix = :configured_prefix
ArtirixCacheService.key :some_key # => "configured_prefix/some_key"
ArtirixCacheService.key :another # => "configured_prefix/another"Extra Arguments
We can pass other arguments, that will be treated and appended to the cache key.
note: blank? arguments will be skipped.
ArtirixCacheService.register_key_prefix :configured_prefix
ArtirixCacheService.key :some_key, :arg1, nil, 'arg2'
# => "configured_prefix/some_key/arg1/arg2"
cache_key compliant arguments
if an argument (including the first argument) responds to cache_key,
it will be called.
ArtirixCacheService.register_key_prefix :configured_prefix
article = Article.find 17
article.cache_key # => "cache_key_article_17"
ArtirixCacheService.key :some_key, :arg1, article, 'arg2'
# => "configured_prefix/some_key/arg1/cache_key_article_17/arg2"Digest
we may want to add a digest to the cache key instead of all arguments, for example in case that we're giving it a long list.
It will use SHA1.
ArtirixCacheService.register_key_prefix :prfx
arg3 = { a: 1, b: 2 }
ArtirixCacheService.digest arg3
# => "032b5f154d4ada01bc89a2e8fae8251c090212db"
ArtirixCacheService.key :some_key, :arg1, 'arg2', digest: arg3
# => "prfx/some_key/arg1/arg2/032b5f154d4ada01bc89a2e8fae8251c090212db"
arg4 = [1, 2, 3]
ArtirixCacheService.digest [arg3, arg4]
# => "7448a071aeee91fc9ee1c705f15445fdd8411224"
ArtirixCacheService.key :some_key, :arg1, 'arg2', digest: [arg3, arg4]
# => "prfx/some_key/arg1/arg2/7448a071aeee91fc9ee1c705f15445fdd8411224"
request as special digest part
we can pass the request as a special argument. The Service will invoke fullpath
on it in the final digest.
It uses a parameterized version of the fullpath followed by a untouched version, to avoid any collision.
request
# => an ActionDispatch::Request, or any object that responds to `fullpath`
request.fullpath # => "/some/path?with=arguments"
ArtirixCacheService.key :some_key, :arg1, 'arg2', request: request
# => "prfx/some_key/arg1/arg2/1d3c96f449b8d21df75ebcf378c8f2455bf4e93c"
# >> Digest::SHA1.hexdigest [request.fullpath.parameterize, request.fullpath].to_s
# => "1d3c96f449b8d21df75ebcf378c8f2455bf4e93c"Usage: .options
used for getting the cache options based on the registered defaults and the registered options.
# unless registered otherwise, the default options is an empty array
ArtirixCacheService.default_options # => {}
# sets the options to be used as default when needed
ArtirixCacheService.register_default_options expires_in: 300
# we can register some options based on a name (Symbol)
ArtirixCacheService.registered_options? :my_options # => false
ArtirixCacheService.registered_options :my_options # => nil
ArtirixCacheService.register_options :my_options, race_condition_ttl: 1
ArtirixCacheService.registered_options? :my_options # => true
ArtirixCacheService.registered_options :my_options # => { race_condition_ttl: 1 }once we have our different options registered, we can use the Service to get the desired final options.
Given a list of names, it will use the first one that is registered. It will return the options on that name, merged over the default options
ArtirixCacheService.options :missing, :my_options
# => { expires_in: 300, race_condition_ttl: 1 } If no registered option is found from the given list, then it will return
-
nil(if passing keywordreturn_if_missing: :nil) - default options (if passing keyword
return_if_missing: :default) - an empty hash (default behaviour, or passing keyword
return_if_missingwith any other value)
ArtirixCacheService.options :missing, :another_missing
# => {}
ArtirixCacheService.options :missing, :another_missing, return_if_missing: :default
# => { expires_in: 300 }
ArtirixCacheService.options :missing, :another_missing, return_if_missing: :nil
# => nil
ArtirixCacheService.options :missing, :another_missing, return_if_missing: :empty
# => {} Variables
as part of the cache_key, we can specify the name of a variable that the Service can retrieve to use in the digest.
Using this, we can effectively change cache_keys arguments without changing code, effectively invalidating cache without coupling.
If the variable does not have a value, it will get nil, which is valid for the digest.
Note: we retrieve the variables as strings always, and return nil if blank?.
# some_view.html.erb
<%= cache ArtirixCacheService.key(:my_key, variables: :my_var) %>
...
<% end %>
# first request, variable :my_var does not have a value (nil), so
# the cache_key is "prfx/my_key/333a21750df06ef3c82aece819ded0f6f691638a"
# Digest::SHA1.hexdigest( { my_var: nil }.to_s )
# # => "333a21750df06ef3c82aece819ded0f6f691638a"
# model_a.rb
uuid = SecureRandom.uuid # => "6d6eb11e-0241-4f97-b706-91982eb8e69b"
ArtirixCacheService.variable_set :my_var, uuid
# now the next request on the view, the cache key is different:
# cache key is "prfx/my_key/a8484d25b7c57b1f93a05ad82422d7b45c4ad83e"
# Digest::SHA1.hexdigest( { my_var: uuid }.to_s )
# # => "a8484d25b7c57b1f93a05ad82422d7b45c4ad83e"
# => This way we can invalidate based on a variable value, without directly
invalidating cache, for the use cases when we cannot rely on the argument's cache_key.
We use variable_set to set new values, and variable_get to retrieve them.
We can also pass an optional block to variable_get to set the value if it's nil.
ArtirixCacheService.variable_get :my_var # => nil
ArtirixCacheService.variable_get(:my_var) { 990 } # => "990"
ArtirixCacheService.variable_get :my_var # => "990" We can list the variables that the service has currently access to with .variables
ArtirixCacheService.variables # => [ "my_var" ]Variable Store
by default (dev mode) the values are stored in an internal hash.
Redis
it can connect to Redis, using the given options, and store the variables with a given prefix.
redis_options = {
namespace: 'xyz',
host: 'localhost',
port: 6379,
db: 0,
}
ArtirixCacheService.redis_options = redis_options
ArtirixCacheService.register_variables_store :redis, force: trueA prefix on the variable name will be used. By default it's artirix_cache_service.
It gets prepended to the given variable name, and separated by _.
# default prefix
ArtirixCacheService.redis_variable_prefix # => "artirix_cache_service"
# setting a new prefix (don't forget to reload the store)
ArtirixCacheService.redis_variable_prefix = 'my_app_prefix'
ArtirixCacheService.register_variables_store :redis, force: true
# checking variables
ArtirixCacheService.variable_set 'myvar', 'paco'
redis_client = Redis.new redis_options
redis_client.get 'my_app_prefix_myvar' # => 'paco'View Helper
Calling ArtirixCacheService.view_helper we can access a module with a
artirix_cache helper method that acts as a proxy to normal Rails cache
view helper method.
It will use the Service to get the final key using the .key Service method,
and will load up the options to pass to cache method looking for the second
argument on the registered options (using .options Service method).
# in ApplicationHelper
include ArtirixCacheService.view_helper
# in the view
<%= artirix_cache :my_key, :registered_options_name, :arg1, request: request do %>
...
<% end %>
# same as
<%= cache ArtirixCacheService.key(:my_key, :arg1, request: request),
ArtirixCacheService.options(:registered_options_name) do %>
...
<% end %>First argument is required and it's the first argument to
ArtirixCacheService.key call. The 3rd and subsequent arguments are the extra
arguments in that call
The second argument will be used to call .options. We can supply an array of
possible option names to lookup (see .options method).
<%= artirix_cache :my_key, [:opts1, :otps2], :arg1, request: request do %>
...
<% end %>with disable_cache: true in the options
There is one important difference from using the artirix_cache view helper and
calling cache directly. If the options hash to be used has a truthy value on
disable_cache key, then it will yield directly and it will not call the
cache method
example:
# having:
ArtirixCacheService.options :options_with_disabled
# => { expires_in: 300, disable_cache: true }
# in the view
<%= artirix_cache :my_key, :options_with_disabled, :arg1, request: request do %>
...
<% end %>
# will never call `cache` method, it will yield directlyInstallation
Add this line to your application's Gemfile:
gem 'artirix_cache_service'And then execute:
$ bundle
Or install it yourself as:
$ gem install artirix_cache_service
Development
After checking out the repo, run bin/setup to install dependencies. Then, run rake rspec to run the tests. You can also run bin/console for an interactive prompt that will allow you to experiment.
To install this gem onto your local machine, run bundle exec rake install. To release a new version, update the version number in version.rb, and then run bundle exec rake release, which will create a git tag for the version, push git commits and tags, and push the .gem file to rubygems.org.
Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/artirix/artirix_cache_service.
CHANGELOG
v0.7.0
opening dependency on ActiveSupport to include v5
v0.6.0
(renamed v.0.5.1 to v.0.6.0 since it is a change in the API)
-
redis_clientcan now be injected into the service.
v0.5.0
- added
.variables
v0.4.0
- View Helper with
artirix_cachehelper method
v0.3.1
- add request support
v0.3.0
- add Redis support
v0.2.0
- removed
ArtirixCacheService.config_paramssupport, now usingregister_key_prefixmethod - add
optionssupport