In my Rack-based app I want to serve CSS and JS and so I use Rack::Static
middleware as shown below:
config.ru
use Rack::Static, urls: ["/css" ], root: "public"
run MyApp
public
folder structure:
public
css
application.min.css
As per Rack::Static
implementation at https://github.com/rack/rack/blob/2.2.4/lib/rack/static.rb (link refers to code in the version of Rack I am using ie 2.2.4) by default Cache-Control
header will not be set in Response.
But if I use following configuration
use Rack::Static, urls: ["/css" ], root: "public",
:header_rules => [
# Cache CSS/JS files, matching given regex in public caches (e.g. Rack::Cache) as well as in the browser. For e.g. myfile.1.2.1.css
#
[ /\.(?:[1-9]\.[0-9]\.[0-9])\.(?:css|js)\z/, {'cache-Control' => 'public, max-age=60'} ]
]
Then I can see following header Cache-Control: public, max-age=60
under Response Headers for eg in Network tab under Web Developer Tools in Firefox.
Now I want to cache bust that CSS file using fingerprint strategy as explained in following resources I found
https://css-tricks.com/strategies-for-cache-busting-css/#aa-changing-file-name
https://csswizardry.com/2019/03/cache-control-for-civilians/
So in my HTML pages I would have my stylesheet name include the fingerprint version for eg like following
<head>
...
...
<link href="/css/application.min.<MY_ASSET_VERSION>.css" rel="stylesheet">
</head>
where say <MY_ASSET_VERSION>
is set to 1.0.0
.
But I should not have any file by name application.min.1.0.0.css
in my public
folder. That naming is just done so as to trigger cache bust. So how can I make Rack::Static
to serve the file css/application.min.css
when it encounters path /css/application.min.1.0.0.css
?
Will I need to implement a middleware which should be put in application's middleware stack after Rack::Static
? If yes, can anybody please help me with an example because I have not implemented any middleware.
Or if there is any other standard way for addressing the need at hand, then please suggest that.
Thanks.
Posting below the solution which I implemented using a middleware and which is working for me.
middlewares/custom_middleware/util.rb
module CustomMiddleware
module Util
extend self
EXTENSIONS_OF_ASSETS_TO_BE_FINGER_PRINTED = /css|js/
ASSET_FINGER_PRINT_FORMAT_REGEX = /[1-9]\.[0-9]\.[0-9]/
FINGER_PRINTED_ASSET_NAME_MATCHER_REGEX = /\.(?:#{ASSET_FINGER_PRINT_FORMAT_REGEX})\.(?:#{EXTENSIONS_OF_ASSETS_TO_BE_FINGER_PRINTED})\z/
ORIGINAL_ASSET_NAME_DETERMINER_FROM_FINGER_PRINTED_NAME_REGEX = /(.+)\.(?:#{ASSET_FINGER_PRINT_FORMAT_REGEX})\.(#{EXTENSIONS_OF_ASSETS_TO_BE_FINGER_PRINTED})\z/
def determine_original_asset_name(fingerprinted_asset_name:)
md = fingerprinted_asset_name.match(ORIGINAL_ASSET_NAME_DETERMINER_FROM_FINGER_PRINTED_NAME_REGEX)
return fingerprinted_asset_name if md.nil?
arr = md.captures
asset_file_name = arr[0]
asset_file_extension = arr[1]
asset_name = "#{asset_file_name}.#{asset_file_extension}"
asset_name
end
end
end
middlewares/custom_middleware/fingerprinted_asset_name_modifier.rb
require_relative 'util'
module CustomMiddleware
class FingeprintedAssetNameModifier
def initialize(app)
@app = app
end
def call(env)
env_path_info_key = 'PATH_INFO'
orig_path = env[env_path_info_key]
modified_path = Util.determine_original_asset_name(fingerprinted_asset_name: orig_path)
if modified_path != orig_path
env.merge!(env_path_info_key => modified_path)
end
@app.call(env)
end
end
end
config.ru
require_relative "middlewares/custom_middleware/fingerprinted_asset_name_modifier"
use CustomMiddleware::FingeprintedAssetNameModifier
use Rack::Static, urls: ["/css", "/js" ], root: "public",
:header_rules => [
# Cache CSS/JS files in public caches (e.g. Rack::Cache) as well as in the browser. For e.g. myfile.css
[ %w(css js), {'cache-control' => 'public, max-age=60'} ]
]
run MyApp
With above solution when following CSS file is included in my page
<head>
...
...
<link href="/css/application.min.1.0.0.css" rel="stylesheet">
</head>
application.min.1.0.0.css
file serves my file at public/css/application.min.css
and in Response headers Cache-Control: public, max-age=60
is set implying the after 60 seconds if application.min.1.0.0.css
is re-requested it will be served from my application and not from browser's cache.
Also within 60 seconds of 1st request to the asset if changing the asset fingerprint in page like following
<link href="/css/application.min.1.0.5.css" rel="stylesheet">
and reloading the page, the asset is served from my application and not from browser's cache.
Hoping that this turns out to be useful to people who may come up with a requirement like in question post.
Thanks.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.