diff options
Diffstat (limited to 'actionpack/lib/action_view/helpers/asset_url_helper.rb')
-rw-r--r-- | actionpack/lib/action_view/helpers/asset_url_helper.rb | 189 |
1 files changed, 83 insertions, 106 deletions
diff --git a/actionpack/lib/action_view/helpers/asset_url_helper.rb b/actionpack/lib/action_view/helpers/asset_url_helper.rb index 35d312e61a..8cbcdfe99e 100644 --- a/actionpack/lib/action_view/helpers/asset_url_helper.rb +++ b/actionpack/lib/action_view/helpers/asset_url_helper.rb @@ -1,4 +1,4 @@ -require 'action_view/helpers/asset_tag_helpers/asset_paths' +require 'zlib' module ActionView # = Action View Asset URL Helpers @@ -104,104 +104,12 @@ module ActionView # "http://asset%d.example.com", "https://asset1.example.com" # ) # - # === Customizing the asset path - # - # 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 - # still be able to instantly invalidate it by simply updating the file (and - # hence updating the timestamp, which then updates the URL as the timestamp - # is part of that, which in turn busts the cache). - # - # It's the responsibility of the web server you use to set the far-future - # expiration date on cache assets that you need to take advantage of this - # feature. Here's an example for Apache: - # - # # Asset Expiration - # ExpiresActive On - # <FilesMatch "\.(ico|gif|jpe?g|png|js|css)$"> - # ExpiresDefault "access plus 1 year" - # </FilesMatch> - # - # Also note that in order for this to work, all your application servers must - # return the same timestamps. This means that they must have their clocks - # synchronized. If one of them drifts out of sync, you'll see different - # timestamps at random and the cache won't work. In that case the browser - # 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</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 = proc { |asset_path| - # "/release-#{RELEASE_NUMBER}#{asset_path}" - # } - # - # This example would cause the following behavior 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" /> - # - # Changing the asset_path 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 AssetUrlHelper - ASSET_EXTENSIONS = { - javascript: 'js', - stylesheet: 'css' - } + URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//} - ASSET_PUBLIC_DIRECTORIES = { - audio: '/audios', - font: '/fonts', - image: '/images', - javascript: '/javascripts', - stylesheet: '/stylesheets', - video: '/videos' + ASSET_EXTENSIONS = { + javascript: '.js', + stylesheet: '.css' } # Computes the path to asset in public directory. If :type @@ -215,9 +123,28 @@ module ActionView # asset_path "application", type: :stylesheet # => /stylesheets/application.css # asset_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js def asset_path(source, options = {}) + source = source.to_s return "" unless source.present? - options[:ext] ||= ASSET_EXTENSIONS[options[:type]] if options[:type] - asset_paths.compute_public_path(source, ASSET_PUBLIC_DIRECTORIES[options[:type]], options) + return source if source =~ URI_REGEXP + + if File.extname(source).empty? && (ext = ASSET_EXTENSIONS[options[:type]]) + source = "#{source}#{ext}" + end + + if source[0] != ?/ + source = compute_asset_path(source, options) + end + + current_request = controller.request if controller.respond_to?(:request) + if relative_url_root = config.relative_url_root || current_request.try(:script_name) + source = "#{relative_url_root}#{source}" unless source.starts_with?("#{relative_url_root}/") + end + + if host = compute_asset_host(source, options) + source = "#{host}#{source}" + end + + source end alias_method :path_to_asset, :asset_path # aliased to avoid conflicts with a asset_path named route @@ -225,11 +152,66 @@ module ActionView # will use +asset_path+ internally, so most of their behaviors # will be the same. def asset_url(source, options = {}) - host = url_for(:only_path => false) - URI.join(host, path_to_asset(source, options)).to_s + path_to_asset(source, options.merge(:protocol => :request)) end alias_method :url_to_asset, :asset_url # aliased to avoid conflicts with an asset_url named route + # Maps asset types to public directory. + ASSET_PUBLIC_DIRECTORIES = { + audio: '/audios', + font: '/fonts', + image: '/images', + javascript: '/javascripts', + stylesheet: '/stylesheets', + video: '/videos' + } + + # Computes asset path to public directory. Plugins and + # extensions can override this method to point to custom assets + # or generate digested paths or query strings. + def compute_asset_path(source, options = {}) + dir = ASSET_PUBLIC_DIRECTORIES[options[:type]] || "" + File.join(dir, source) + end + + # Pick an asset host for this source. Returns +nil+ if no host is set, + # the host if no wildcard is set, the host interpolated with the + # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4), + # or the value returned from invoking call on an object responding to call + # (proc or otherwise). + def compute_asset_host(source = "", options = {}) + if controller.respond_to?(:request) + request = controller.request + end + + host = config.asset_host + host ||= request.base_url if request && options[:protocol] == :request + return unless host + + if host.respond_to?(:call) + arity = host.respond_to?(:arity) ? host.arity : host.method(:call).arity + args = [source] + args << request if request && (arity > 1 || arity < 0) + host = host.call(*args) + elsif host =~ /%d/ + host = host % (Zlib.crc32(source) % 4) + end + + if host =~ URI_REGEXP + host + else + protocol = options[:protocol] || config.default_asset_host_protocol || (request ? :request : :relative) + case protocol + when :relative + "//#{host}" + when :request + "#{request.protocol}#{host}" + else + "#{protocol}://#{host}" + end + end + end + # Computes the path to a javascript asset in the public javascripts directory. # If the +source+ filename has no extension, .js will be appended (except for explicit URIs) # Full paths from the document root will be passed through. @@ -360,11 +342,6 @@ module ActionView url_to_asset(source, type: :font) end alias_method :url_to_font, :font_url # aliased to avoid conflicts with an font_url named route - - private - def asset_paths - @asset_paths ||= AssetTagHelper::AssetPaths.new(config, controller) - end end end end |