aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb68
-rw-r--r--actionpack/test/template/asset_tag_helper_test.rb9
-rw-r--r--railties/guides/source/configuring.textile2
3 files changed, 77 insertions, 2 deletions
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 02ad41719b..635c58bc35 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -108,7 +108,7 @@ module ActionView
# "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 ActionView
# 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 @@ module ActionView
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}/}
diff --git a/actionpack/test/template/asset_tag_helper_test.rb b/actionpack/test/template/asset_tag_helper_test.rb
index c471df861d..43d11df8fe 100644
--- a/actionpack/test/template/asset_tag_helper_test.rb
+++ b/actionpack/test/template/asset_tag_helper_test.rb
@@ -373,6 +373,15 @@ class AssetTagHelperTest < ActionView::TestCase
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
diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile
index 5418ad7dd4..e438d4cc0a 100644
--- a/railties/guides/source/configuring.textile
+++ b/railties/guides/source/configuring.textile
@@ -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.