Permalink
Browse files

Allow customisation of asset path generation using a proc.

Set config.action_controller.asset_path_template to a proc that takes one
argument - the direct, unchanged asset path - and returns a path to that asset
using the scheme that your assets require.

This is useful if you have a setup which scales by introducing new application
servers where the mtime of the asset files may not be the same as those of the
asset files on your previous servers, but it does require your web servers to
have knowledge of the asset template paths that you rewrite to so it's not
suitable for out-of-the-box use. An example of configuring asset path
generation and rewriting these paths using Apache is included in
actionpack/lib/action_view/helpers/asset_tag_helper.rb.

Signed-off-by: wycats <wycats@gmail.com>
  • Loading branch information...
craigw authored and wycats committed Mar 21, 2010
1 parent cf6734f commit 49bc6a249e2a200216f8a96c36093a2c7a471c9b
@@ -108,7 +108,7 @@ module Helpers #:nodoc:
# "http://asset%d.example.com", "https://asset1.example.com"
# )
#
- # === Using asset timestamps
+ # === Using asset path templates
#
# By default, Rails appends asset's timestamps to all asset paths. This allows
# you to set a cache-expiration date for the asset far into the future, but
@@ -133,6 +133,65 @@ module Helpers #:nodoc:
# will request the same assets over and over again even thought they didn't
# change. You can use something like Live HTTP Headers for Firefox to verify
# that the cache is indeed working.
+ #
+ # This strategy works well enough for most server setups and requires the
+ # least configuration, but if you deploy several application servers at
+ # different times - say to handle a temporary spike in load - then the
+ # asset time stamps will be out of sync. In a setup like this you may want
+ # to set the way that asset paths are generated yourself.
+ #
+ # Altering the asset paths that Rails generates can be done in two ways.
+ # The easiest is to define the RAILS_ASSET_ID environment variable. The
+ # contents of this variable will always be used in preference to
+ # calculated timestamps. A more complex but flexible way is to set
+ # <tt>ActionController::Base.config.asset_path_template</tt> to a proc
+ # that takes the unmodified asset path and returns the path needed for
+ # your asset caching to work. Typically you'd do something like this in
+ # <tt>config/environments/production.rb</tt>:
+ #
+ # # Normally you'd calculate RELEASE_NUMBER at startup.
+ # RELEASE_NUMBER = 12345
+ # config.action_controller.asset_path_template = proc { |asset_path|
+ # "/release-#{RELEASE_NUMBER}#{asset_path}"
+ # }
+ #
+ # This example would cause the following behaviour on all servers no
+ # matter when they were deployed:
+ #
+ # image_tag("rails.png")
+ # # => <img alt="Rails" src="/release-12345/images/rails.png" />
+ # stylesheet_link_tag("application")
+ # # => <link href="/release-12345/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" type="text/css" />
+ #
+ # Changing the asset_path_template does require that your web servers have
+ # knowledge of the asset template paths that you rewrite to so it's not
+ # suitable for out-of-the-box use. To use the example given above you
+ # could use something like this in your Apache VirtualHost configuration:
+ #
+ # <LocationMatch "^/release-\d+/(images|javascripts|stylesheets)/.*$">
+ # # Some browsers still send conditional-GET requests if there's a
+ # # Last-Modified header or an ETag header even if they haven't
+ # # reached the expiry date sent in the Expires header.
+ # Header unset Last-Modified
+ # Header unset ETag
+ # FileETag None
+ #
+ # # Assets requested using a cache-busting filename should be served
+ # # only once and then cached for a really long time. The HTTP/1.1
+ # # spec frowns on hugely-long expiration times though and suggests
+ # # that assets which never expire be served with an expiration date
+ # # 1 year from access.
+ # ExpiresActive On
+ # ExpiresDefault "access plus 1 year"
+ # </LocationMatch>
+ #
+ # # We use cached-busting location names with the far-future expires
+ # # headers to ensure that if a file does change it can force a new
+ # # request. The actual asset filenames are still the same though so we
+ # # need to rewrite the location from the cache-busting location to the
+ # # real asset location so that we can serve it.
+ # RewriteEngine On
+ # RewriteRule ^/release-\d+/(images|javascripts|stylesheets)/(.*)$ /$1/$2 [L]
module AssetTagHelper
JAVASCRIPT_DEFAULT_SOURCES = ['prototype', 'effects', 'dragdrop', 'controls', 'rails'].freeze unless const_defined?(:JAVASCRIPT_DEFAULT_SOURCES)
@@ -646,7 +705,12 @@ def compute_public_path(source, dir, ext = nil, include_host = true)
source += ".#{ext}" if rewrite_extension?(source, dir, ext)
source = "/#{dir}/#{source}" unless source[0] == ?/
- source = rewrite_asset_path(source)
+ asset_path_template = config.asset_path_template
+ if asset_path_template && asset_path_template.respond_to?(:call)
+ source = asset_path_template.call(source)
+ else
+ source = rewrite_asset_path(source)
+ end
has_request = controller.respond_to?(:request)
if has_request && include_host && source !~ %r{^#{controller.config.relative_url_root}/}
@@ -373,6 +373,15 @@ def test_timebased_asset_id
assert_equal %(<img alt="Rails" src="/images/rails.png?#{expected_time}" />), image_tag("rails.png")
end
+ def test_proc_asset_id
+ @controller.config.asset_path_template = Proc.new do |asset_path|
+ "/assets.v12345#{asset_path}"
+ end
+
+ expected_path = "/assets.v12345/images/rails.png"
+ assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
+ end
+
def test_timebased_asset_id_with_relative_url_root
@controller.config.relative_url_root = "/collaboration/hieraki"
expected_time = File.stat(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).mtime.to_i.to_s
@@ -133,6 +133,8 @@ h4. Configuring Action Controller
* +config.action_controller.asset_host+ provides a string that is prepended to all of the URL-generating helpers in +AssetHelper+. This is designed to allow moving all javascript, CSS, and image files to a separate asset host.
+* +config.action_controller.asset_template_path+ allows you to override the default asset path generation by providing your own instructions.
+
* +config.action_controller.consider_all_requests_local+ is generally set to +true+ during development and +false+ during production; if it is set to +true+, then any error will cause detailed debugging information to be dumped in the HTTP response. For finer-grained control, set this to +false+ and implement +local_request?+ to specify which requests should provide debugging information on errors.
* +config.action_controller.allow_concurrency+ should be set to +true+ to allow concurrent (threadsafe) action processing. Set to +false+ by default. You probably don't want to call this one directly, though, because a series of other adjustments need to be made for threadsafe mode to work properly. Instead, you should simply call +config.threadsafe!+ inside your +production.rb+ file, which makes all the necessary adjustments.

0 comments on commit 49bc6a2

Please sign in to comment.