diff options
Diffstat (limited to 'actionpack/lib/action_view/asset_paths.rb')
-rw-r--r-- | actionpack/lib/action_view/asset_paths.rb | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/actionpack/lib/action_view/asset_paths.rb b/actionpack/lib/action_view/asset_paths.rb new file mode 100644 index 0000000000..aa5db0d7bc --- /dev/null +++ b/actionpack/lib/action_view/asset_paths.rb @@ -0,0 +1,136 @@ +require 'zlib' +require 'active_support/core_ext/file' +require 'action_controller/metal/exceptions' + +module ActionView + class AssetPaths #:nodoc: + 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 + File.join(config.assets_dir, dir, source) + end + + def is_uri?(path) + path =~ %r{^[-a-z]+://|^cid:|^//} + 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 ActionController::RoutingError, "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 + 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 |