Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Fetching contributors…

Cannot retrieve contributors at this time

file 150 lines (127 sloc) 4.91 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150
require 'zlib'
require 'active_support/core_ext/file'

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 include_host is true and the asset host does not specify the protocol
    # the protocol parameter specifies how the protocol will be added.
    # 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)

      options[:include_host] ||= true
      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]) if options[:include_host]
      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|data):|^//}
    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.nil? ? source : "#{host}#{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
      protocol = @config.action_controller.default_asset_host_protocol if @config.action_controller.present?
      protocol ||= @config.default_asset_host_protocol
      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 the proc if it's a proc or the value from
    # invoking call if it's an object responding to call.
    def compute_asset_host(source)
      if host = asset_host_config
        if host.respond_to?(:call)
          args = [source]
          arity = arity_of(host)
          if arity > 1 && !has_request?
            invalid_asset_host!("Remove the second argument to your asset_host Proc if you do not need the request.")
          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
      if config.action_controller.present?
        config.action_controller.relative_url_root
      else
        config.relative_url_root
      end
    end

    def asset_host_config
      if config.action_controller.present?
        config.action_controller.asset_host
      else
        config.asset_host
      end
    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
Something went wrong with that request. Please try again.