mirror of
https://github.com/rails/rails.git
synced 2022-11-09 12:12:34 -05:00
Refactor AssetUrlHelper to make it friendly for plugins and extensions
Add asset_path/url helper for a consolidated entry point Expose compute_asset_path as a public API Expose compute_asset_host as a public API Move RAILS_ASSET_ID to its own module, AssetIdHelper Removed AV::AssetPaths
This commit is contained in:
parent
bd38d9f211
commit
1e2b0ce95e
9 changed files with 275 additions and 346 deletions
|
@ -29,7 +29,6 @@ module ActionView
|
|||
extend ActiveSupport::Autoload
|
||||
|
||||
eager_autoload do
|
||||
autoload :AssetPaths
|
||||
autoload :Base
|
||||
autoload :Context
|
||||
autoload :CompiledTemplates, "action_view/context"
|
||||
|
|
|
@ -1,143 +0,0 @@
|
|||
require 'zlib'
|
||||
require 'active_support/core_ext/file'
|
||||
|
||||
module ActionView
|
||||
class AssetPaths #:nodoc:
|
||||
URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}
|
||||
|
||||
attr_reader :config, :controller
|
||||
|
||||
def initialize(config, controller = nil)
|
||||
@config = config
|
||||
@controller = controller
|
||||
end
|
||||
|
||||
# Add the extension +ext+ if not present. Return full or scheme-relative URLs otherwise untouched.
|
||||
# Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
|
||||
# roots. Rewrite the asset path for cache-busting asset ids. Include
|
||||
# asset host, if configured, with the correct request protocol.
|
||||
#
|
||||
# When :relative (default), the protocol will be determined by the client using current protocol
|
||||
# When :request, the protocol will be the request protocol
|
||||
# Otherwise, the protocol is used (E.g. :http, :https, etc)
|
||||
def compute_public_path(source, dir, options = {})
|
||||
source = source.to_s
|
||||
return source if is_uri?(source)
|
||||
|
||||
source = rewrite_extension(source, dir, options[:ext]) if options[:ext]
|
||||
source = rewrite_asset_path(source, dir, options)
|
||||
source = rewrite_relative_url_root(source, relative_url_root)
|
||||
source = rewrite_host_and_protocol(source, options[:protocol])
|
||||
source
|
||||
end
|
||||
|
||||
# Return the filesystem path for the source
|
||||
def compute_source_path(source, dir, ext)
|
||||
source = rewrite_extension(source, dir, ext) if ext
|
||||
|
||||
sources = []
|
||||
sources << config.assets_dir
|
||||
sources << dir unless source[0] == ?/
|
||||
sources << source
|
||||
|
||||
File.join(sources)
|
||||
end
|
||||
|
||||
def is_uri?(path)
|
||||
path =~ URI_REGEXP
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def rewrite_extension(source, dir, ext)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def rewrite_asset_path(source, path = nil)
|
||||
raise NotImplementedError
|
||||
end
|
||||
|
||||
def rewrite_relative_url_root(source, relative_url_root)
|
||||
relative_url_root && !source.starts_with?("#{relative_url_root}/") ? "#{relative_url_root}#{source}" : source
|
||||
end
|
||||
|
||||
def has_request?
|
||||
controller.respond_to?(:request)
|
||||
end
|
||||
|
||||
def rewrite_host_and_protocol(source, protocol = nil)
|
||||
host = compute_asset_host(source)
|
||||
if host && !is_uri?(host)
|
||||
if (protocol || default_protocol) == :request && !has_request?
|
||||
host = nil
|
||||
else
|
||||
host = "#{compute_protocol(protocol)}#{host}"
|
||||
end
|
||||
end
|
||||
host ? "#{host}#{source}" : source
|
||||
end
|
||||
|
||||
def compute_protocol(protocol)
|
||||
protocol ||= default_protocol
|
||||
case protocol
|
||||
when :relative
|
||||
"//"
|
||||
when :request
|
||||
unless @controller
|
||||
invalid_asset_host!("The protocol requested was :request. Consider using :relative instead.")
|
||||
end
|
||||
@controller.request.protocol
|
||||
else
|
||||
"#{protocol}://"
|
||||
end
|
||||
end
|
||||
|
||||
def default_protocol
|
||||
@config.default_asset_host_protocol || (has_request? ? :request : :relative)
|
||||
end
|
||||
|
||||
def invalid_asset_host!(help_message)
|
||||
raise ActionView::MissingRequestError, "This asset host cannot be computed without a request in scope. #{help_message}"
|
||||
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)
|
||||
if host = asset_host_config
|
||||
if host.respond_to?(:call)
|
||||
args = [source]
|
||||
arity = arity_of(host)
|
||||
if (arity > 1 || arity < -2) && !has_request?
|
||||
invalid_asset_host!("Remove the second argument to your asset_host Proc if you do not need the request, or make it optional.")
|
||||
end
|
||||
args << current_request if (arity > 1 || arity < 0) && has_request?
|
||||
host.call(*args)
|
||||
else
|
||||
(host =~ /%d/) ? host % (Zlib.crc32(source) % 4) : host
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def relative_url_root
|
||||
config.relative_url_root || current_request.try(:script_name)
|
||||
end
|
||||
|
||||
def asset_host_config
|
||||
config.asset_host
|
||||
end
|
||||
|
||||
# Returns the current request if one exists.
|
||||
def current_request
|
||||
controller.request if has_request?
|
||||
end
|
||||
|
||||
# Returns the arity of a callable
|
||||
def arity_of(callable)
|
||||
callable.respond_to?(:arity) ? callable.arity : callable.method(:call).arity
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -3,6 +3,7 @@ module ActionView #:nodoc:
|
|||
extend ActiveSupport::Autoload
|
||||
|
||||
autoload :ActiveModelHelper
|
||||
autoload :AssetIdHelper
|
||||
autoload :AssetTagHelper
|
||||
autoload :AssetUrlHelper
|
||||
autoload :AtomFeedHelper
|
||||
|
|
154
actionpack/lib/action_view/helpers/asset_id_helper.rb
Normal file
154
actionpack/lib/action_view/helpers/asset_id_helper.rb
Normal file
|
@ -0,0 +1,154 @@
|
|||
require 'thread'
|
||||
require 'active_support/core_ext/file'
|
||||
require 'active_support/core_ext/module/attribute_accessors'
|
||||
|
||||
module ActionView
|
||||
# = Action View Asset Cache ID Helpers
|
||||
#
|
||||
# Rails appends asset's timestamps to public 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 Helpers #:nodoc:
|
||||
module AssetIdHelper
|
||||
# You can enable or disable the asset tag ids cache.
|
||||
# With the cache enabled, the asset tag helper methods will make fewer
|
||||
# expensive file system calls (the default implementation checks the file
|
||||
# system timestamp). However this prevents you from modifying any asset
|
||||
# files while the server is running.
|
||||
#
|
||||
# ActionView::Helpers::AssetIdHelper.cache_asset_ids = false
|
||||
mattr_accessor :cache_asset_ids
|
||||
|
||||
# Add or change an asset id in the asset id cache. This can be used
|
||||
# for SASS on Heroku.
|
||||
# :api: public
|
||||
def add_to_asset_ids_cache(source, asset_id)
|
||||
self.asset_ids_cache_guard.synchronize do
|
||||
self.asset_ids_cache[source] = asset_id
|
||||
end
|
||||
end
|
||||
|
||||
mattr_accessor :asset_ids_cache
|
||||
self.asset_ids_cache = {}
|
||||
|
||||
mattr_accessor :asset_ids_cache_guard
|
||||
self.asset_ids_cache_guard = Mutex.new
|
||||
|
||||
# Use the RAILS_ASSET_ID environment variable or the source's
|
||||
# modification time as its cache-busting asset id.
|
||||
def rails_asset_id(source)
|
||||
if asset_id = ENV["RAILS_ASSET_ID"]
|
||||
asset_id
|
||||
else
|
||||
if self.cache_asset_ids && (asset_id = self.asset_ids_cache[source])
|
||||
asset_id
|
||||
else
|
||||
path = File.join(config.assets_dir, source)
|
||||
asset_id = File.exist?(path) ? File.mtime(path).to_i.to_s : ''
|
||||
|
||||
if self.cache_asset_ids
|
||||
add_to_asset_ids_cache(source, asset_id)
|
||||
end
|
||||
|
||||
asset_id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Override +compute_asset_path+ to add asset id query strings to
|
||||
# generated urls. See +compute_asset_path+ in AssetUrlHelper.
|
||||
def compute_asset_path(source, options = {})
|
||||
source = super(source, options)
|
||||
path = config.asset_path
|
||||
|
||||
if path && path.respond_to?(:call)
|
||||
path.call(source)
|
||||
elsif path && path.is_a?(String)
|
||||
path % [source]
|
||||
elsif asset_id = rails_asset_id(source)
|
||||
asset_id.empty? ? source : "#{source}?#{asset_id}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -19,6 +19,7 @@ module ActionView
|
|||
extend ActiveSupport::Concern
|
||||
|
||||
include AssetUrlHelper
|
||||
include AssetIdHelper
|
||||
include TagHelper
|
||||
|
||||
# Returns an HTML script tag for each of the +sources+ provided.
|
||||
|
|
|
@ -1,94 +0,0 @@
|
|||
require 'thread'
|
||||
require 'active_support/core_ext/file'
|
||||
require 'active_support/core_ext/module/attribute_accessors'
|
||||
|
||||
module ActionView
|
||||
module Helpers
|
||||
module AssetTagHelper
|
||||
|
||||
class AssetPaths < ::ActionView::AssetPaths #:nodoc:
|
||||
# You can enable or disable the asset tag ids cache.
|
||||
# With the cache enabled, the asset tag helper methods will make fewer
|
||||
# expensive file system calls (the default implementation checks the file
|
||||
# system timestamp). However this prevents you from modifying any asset
|
||||
# files while the server is running.
|
||||
#
|
||||
# ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids = false
|
||||
mattr_accessor :cache_asset_ids
|
||||
|
||||
# Add or change an asset id in the asset id cache. This can be used
|
||||
# for SASS on Heroku.
|
||||
# :api: public
|
||||
def add_to_asset_ids_cache(source, asset_id)
|
||||
self.asset_ids_cache_guard.synchronize do
|
||||
self.asset_ids_cache[source] = asset_id
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def rewrite_extension(source, dir, ext)
|
||||
source_ext = File.extname(source)
|
||||
|
||||
source_with_ext = if source_ext.empty?
|
||||
"#{source}.#{ext}"
|
||||
elsif ext != source_ext[1..-1]
|
||||
with_ext = "#{source}.#{ext}"
|
||||
with_ext if File.exist?(File.join(config.assets_dir, dir, with_ext))
|
||||
end
|
||||
|
||||
source_with_ext || source
|
||||
end
|
||||
|
||||
# Break out the asset path rewrite in case plugins wish to put the asset id
|
||||
# someplace other than the query string.
|
||||
def rewrite_asset_path(source, dir, options = nil)
|
||||
dir = "/#{dir}" if dir && dir[0] != ?/
|
||||
source = File.join(dir.to_s, source) unless source[0] == ?/
|
||||
path = config.asset_path
|
||||
|
||||
if path && path.respond_to?(:call)
|
||||
return path.call(source)
|
||||
elsif path && path.is_a?(String)
|
||||
return path % [source]
|
||||
end
|
||||
|
||||
asset_id = rails_asset_id(source)
|
||||
if asset_id.empty?
|
||||
source
|
||||
else
|
||||
"#{source}?#{asset_id}"
|
||||
end
|
||||
end
|
||||
|
||||
mattr_accessor :asset_ids_cache
|
||||
self.asset_ids_cache = {}
|
||||
|
||||
mattr_accessor :asset_ids_cache_guard
|
||||
self.asset_ids_cache_guard = Mutex.new
|
||||
|
||||
# Use the RAILS_ASSET_ID environment variable or the source's
|
||||
# modification time as its cache-busting asset id.
|
||||
def rails_asset_id(source)
|
||||
if asset_id = ENV["RAILS_ASSET_ID"]
|
||||
asset_id
|
||||
else
|
||||
if self.cache_asset_ids && (asset_id = self.asset_ids_cache[source])
|
||||
asset_id
|
||||
else
|
||||
path = File.join(config.assets_dir, source)
|
||||
asset_id = File.exist?(path) ? File.mtime(path).to_i.to_s : ''
|
||||
|
||||
if self.cache_asset_ids
|
||||
add_to_asset_ids_cache(source, asset_id)
|
||||
end
|
||||
|
||||
asset_id
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -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
|
||||
|
|
|
@ -23,7 +23,7 @@ module ActionView
|
|||
initializer "action_view.cache_asset_ids" do |app|
|
||||
unless app.config.cache_classes
|
||||
ActiveSupport.on_load(:action_view) do
|
||||
ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids = false
|
||||
ActionView::Helpers::AssetIdHelper.cache_asset_ids = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -42,6 +42,7 @@ class AssetTagHelperTest < ActionView::TestCase
|
|||
def protocol() 'http://' end
|
||||
def ssl?() false end
|
||||
def host_with_port() 'localhost' end
|
||||
def base_url() 'http://www.example.com' end
|
||||
end.new
|
||||
|
||||
@controller.request = @request
|
||||
|
@ -602,7 +603,7 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
|
|||
@controller = BasicController.new
|
||||
@controller.config.relative_url_root = "/collaboration/hieraki"
|
||||
|
||||
@request = Struct.new(:protocol).new("gopher://")
|
||||
@request = Struct.new(:protocol, :base_url).new("gopher://", "gopher://www.example.com")
|
||||
@controller.request = @request
|
||||
end
|
||||
|
||||
|
@ -617,10 +618,33 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
|
|||
assert_dom_equal(%(/collaboration/hieraki/images/xml.png), image_path("xml.png"))
|
||||
end
|
||||
|
||||
def test_should_return_nothing_if_asset_host_isnt_configured
|
||||
assert_equal nil, compute_asset_host("foo")
|
||||
end
|
||||
|
||||
def test_should_current_request_host_is_always_returned_for_request
|
||||
assert_equal "gopher://www.example.com", compute_asset_host("foo", :protocol => :request)
|
||||
end
|
||||
|
||||
def test_should_ignore_relative_root_path_on_complete_url
|
||||
assert_dom_equal(%(http://www.example.com/images/xml.png), image_path("http://www.example.com/images/xml.png"))
|
||||
end
|
||||
|
||||
def test_should_return_simple_string_asset_host
|
||||
@controller.config.asset_host = "assets.example.com"
|
||||
assert_equal "gopher://assets.example.com", compute_asset_host("foo")
|
||||
end
|
||||
|
||||
def test_should_return_relative_asset_host
|
||||
@controller.config.asset_host = "assets.example.com"
|
||||
assert_equal "//assets.example.com", compute_asset_host("foo", :protocol => :relative)
|
||||
end
|
||||
|
||||
def test_should_return_custom_protocol_asset_host
|
||||
@controller.config.asset_host = "assets.example.com"
|
||||
assert_equal "ftp://assets.example.com", compute_asset_host("foo", :protocol => "ftp")
|
||||
end
|
||||
|
||||
def test_should_compute_proper_path_with_asset_host
|
||||
@controller.config.asset_host = "assets.example.com"
|
||||
assert_dom_equal(%(<link href="http://www.example.com/collaboration/hieraki" rel="alternate" title="RSS" type="application/rss+xml" />), auto_discovery_link_tag)
|
||||
|
@ -653,6 +677,11 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
|
|||
assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/images/xml.png), image_url("xml.png"))
|
||||
end
|
||||
|
||||
def test_should_return_asset_host_with_protocol
|
||||
@controller.config.asset_host = "http://assets.example.com"
|
||||
assert_equal "http://assets.example.com", compute_asset_host("foo")
|
||||
end
|
||||
|
||||
def test_should_ignore_asset_host_on_complete_url
|
||||
@controller.config.asset_host = "http://assets.example.com"
|
||||
assert_dom_equal(%(<link href="http://bar.example.com/stylesheets/style.css" media="screen" rel="stylesheet" />), stylesheet_link_tag("http://bar.example.com/stylesheets/style.css"))
|
||||
|
@ -663,6 +692,11 @@ class AssetTagHelperNonVhostTest < ActionView::TestCase
|
|||
assert_dom_equal(%(<link href="//bar.example.com/stylesheets/style.css" media="screen" rel="stylesheet" />), stylesheet_link_tag("//bar.example.com/stylesheets/style.css"))
|
||||
end
|
||||
|
||||
def test_should_wildcard_asset_host
|
||||
@controller.config.asset_host = 'http://a%d.example.com'
|
||||
assert_match(%r(http://a[0123].example.com), compute_asset_host("foo"))
|
||||
end
|
||||
|
||||
def test_should_wildcard_asset_host_between_zero_and_four
|
||||
@controller.config.asset_host = 'http://a%d.example.com'
|
||||
assert_match(%r(http://a[0123].example.com/collaboration/hieraki/images/xml.png), image_path('xml.png'))
|
||||
|
|
Loading…
Reference in a new issue