Skip to content
This repository
Browse code

Merge pull request #7927 from josh/asset-path-helper

Refactor AssetUrlHelper
  • Loading branch information...
commit 046ab843b84db1935047dbc796f5acae66c45a42 2 parents 46dc6e7 + eafc2b0
Rafael Mendonça França authored October 15, 2012
1  actionmailer/lib/action_mailer/railtie.rb
@@ -22,7 +22,6 @@ class Railtie < Rails::Railtie # :nodoc:
22 22
       options.queue ||= app.queue
23 23
 
24 24
       # make sure readers methods get compiled
25  
-      options.asset_path          ||= app.config.asset_path
26 25
       options.asset_host          ||= app.config.asset_host
27 26
       options.relative_url_root   ||= app.config.relative_url_root
28 27
 
2  actionpack/lib/abstract_controller/asset_paths.rb
@@ -3,7 +3,7 @@ module AssetPaths #:nodoc:
3 3
     extend ActiveSupport::Concern
4 4
 
5 5
     included do
6  
-      config_accessor :asset_host, :asset_path, :assets_dir, :javascripts_dir,
  6
+      config_accessor :asset_host, :assets_dir, :javascripts_dir,
7 7
         :stylesheets_dir, :default_asset_host_protocol, :relative_url_root
8 8
     end
9 9
   end
1  actionpack/lib/action_controller/railtie.rb
@@ -34,7 +34,6 @@ class Railtie < Rails::Railtie #:nodoc:
34 34
       options.stylesheets_dir      ||= paths["public/stylesheets"].first
35 35
 
36 36
       # Ensure readers methods get compiled
37  
-      options.asset_path           ||= app.config.asset_path
38 37
       options.asset_host           ||= app.config.asset_host
39 38
       options.relative_url_root    ||= app.config.relative_url_root
40 39
 
1  actionpack/lib/action_view.rb
@@ -29,7 +29,6 @@ module ActionView
29 29
   extend ActiveSupport::Autoload
30 30
 
31 31
   eager_autoload do
32  
-    autoload :AssetPaths
33 32
     autoload :Base
34 33
     autoload :Context
35 34
     autoload :CompiledTemplates, "action_view/context"
143  actionpack/lib/action_view/asset_paths.rb
... ...
@@ -1,143 +0,0 @@
1  
-require 'zlib'
2  
-require 'active_support/core_ext/file'
3  
-
4  
-module ActionView
5  
-  class AssetPaths #:nodoc:
6  
-    URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}
7  
-
8  
-    attr_reader :config, :controller
9  
-
10  
-    def initialize(config, controller = nil)
11  
-      @config = config
12  
-      @controller = controller
13  
-    end
14  
-
15  
-    # Add the extension +ext+ if not present. Return full or scheme-relative URLs otherwise untouched.
16  
-    # Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
17  
-    # roots. Rewrite the asset path for cache-busting asset ids. Include
18  
-    # asset host, if configured, with the correct request protocol.
19  
-    #
20  
-    # When :relative (default), the protocol will be determined by the client using current protocol
21  
-    # When :request, the protocol will be the request protocol
22  
-    # Otherwise, the protocol is used (E.g. :http, :https, etc)
23  
-    def compute_public_path(source, dir, options = {})
24  
-      source = source.to_s
25  
-      return source if is_uri?(source)
26  
-
27  
-      source = rewrite_extension(source, dir, options[:ext]) if options[:ext]
28  
-      source = rewrite_asset_path(source, dir, options)
29  
-      source = rewrite_relative_url_root(source, relative_url_root)
30  
-      source = rewrite_host_and_protocol(source, options[:protocol])
31  
-      source
32  
-    end
33  
-
34  
-    # Return the filesystem path for the source
35  
-    def compute_source_path(source, dir, ext)
36  
-      source = rewrite_extension(source, dir, ext) if ext
37  
-
38  
-      sources = []
39  
-      sources << config.assets_dir
40  
-      sources << dir unless source[0] == ?/
41  
-      sources << source
42  
-
43  
-      File.join(sources)
44  
-    end
45  
-
46  
-    def is_uri?(path)
47  
-      path =~ URI_REGEXP
48  
-    end
49  
-
50  
-  private
51  
-
52  
-    def rewrite_extension(source, dir, ext)
53  
-      raise NotImplementedError
54  
-    end
55  
-
56  
-    def rewrite_asset_path(source, path = nil)
57  
-      raise NotImplementedError
58  
-    end
59  
-
60  
-    def rewrite_relative_url_root(source, relative_url_root)
61  
-      relative_url_root && !source.starts_with?("#{relative_url_root}/") ? "#{relative_url_root}#{source}" : source
62  
-    end
63  
-
64  
-    def has_request?
65  
-      controller.respond_to?(:request)
66  
-    end
67  
-
68  
-    def rewrite_host_and_protocol(source, protocol = nil)
69  
-      host = compute_asset_host(source)
70  
-      if host && !is_uri?(host)
71  
-        if (protocol || default_protocol) == :request && !has_request?
72  
-          host = nil
73  
-        else
74  
-          host = "#{compute_protocol(protocol)}#{host}"
75  
-        end
76  
-      end
77  
-      host ? "#{host}#{source}" : source
78  
-    end
79  
-
80  
-    def compute_protocol(protocol)
81  
-      protocol ||= default_protocol
82  
-      case protocol
83  
-      when :relative
84  
-        "//"
85  
-      when :request
86  
-        unless @controller
87  
-          invalid_asset_host!("The protocol requested was :request. Consider using :relative instead.")
88  
-        end
89  
-        @controller.request.protocol
90  
-      else
91  
-        "#{protocol}://"
92  
-      end
93  
-    end
94  
-
95  
-    def default_protocol
96  
-      @config.default_asset_host_protocol || (has_request? ? :request : :relative)
97  
-    end
98  
-
99  
-    def invalid_asset_host!(help_message)
100  
-      raise ActionView::MissingRequestError, "This asset host cannot be computed without a request in scope. #{help_message}"
101  
-    end
102  
-
103  
-    # Pick an asset host for this source. Returns +nil+ if no host is set,
104  
-    # the host if no wildcard is set, the host interpolated with the
105  
-    # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
106  
-    # or the value returned from invoking call on an object responding to call
107  
-    # (proc or otherwise).
108  
-    def compute_asset_host(source)
109  
-      if host = asset_host_config
110  
-        if host.respond_to?(:call)
111  
-          args = [source]
112  
-          arity = arity_of(host)
113  
-          if (arity > 1 || arity < -2) && !has_request?
114  
-            invalid_asset_host!("Remove the second argument to your asset_host Proc if you do not need the request, or make it optional.")
115  
-          end
116  
-          args << current_request if (arity > 1 || arity < 0) && has_request?
117  
-          host.call(*args)
118  
-        else
119  
-          (host =~ /%d/) ? host % (Zlib.crc32(source) % 4) : host
120  
-        end
121  
-      end
122  
-    end
123  
-
124  
-    def relative_url_root
125  
-      config.relative_url_root || current_request.try(:script_name)
126  
-    end
127  
-
128  
-    def asset_host_config
129  
-      config.asset_host
130  
-    end
131  
-
132  
-    # Returns the current request if one exists.
133  
-    def current_request
134  
-      controller.request if has_request?
135  
-    end
136  
-
137  
-    # Returns the arity of a callable
138  
-    def arity_of(callable)
139  
-      callable.respond_to?(:arity) ? callable.arity : callable.method(:call).arity
140  
-    end
141  
-
142  
-  end
143  
-end
93  actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
... ...
@@ -1,93 +0,0 @@
1  
-require 'thread'
2  
-require 'active_support/core_ext/file'
3  
-require 'active_support/core_ext/module/attribute_accessors'
4  
-
5  
-module ActionView
6  
-  module Helpers
7  
-    module AssetTagHelper
8  
-
9  
-      class AssetPaths < ::ActionView::AssetPaths #:nodoc:
10  
-        # You can enable or disable the asset tag ids cache.
11  
-        # With the cache enabled, the asset tag helper methods will make fewer
12  
-        # expensive file system calls (the default implementation checks the file
13  
-        # system timestamp). However this prevents you from modifying any asset
14  
-        # files while the server is running.
15  
-        #
16  
-        #   ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids = false
17  
-        mattr_accessor :cache_asset_ids
18  
-
19  
-        # Add or change an asset id in the asset id cache. This can be used
20  
-        # for SASS on Heroku.
21  
-        # :api: public
22  
-        def add_to_asset_ids_cache(source, asset_id)
23  
-          self.asset_ids_cache_guard.synchronize do
24  
-            self.asset_ids_cache[source] = asset_id
25  
-          end
26  
-        end
27  
-
28  
-      private
29  
-
30  
-        def rewrite_extension(source, dir, ext)
31  
-          source_ext = File.extname(source)
32  
-
33  
-          source_with_ext = if source_ext.empty?
34  
-            "#{source}.#{ext}"
35  
-          elsif ext != source_ext[1..-1]
36  
-            with_ext = "#{source}.#{ext}"
37  
-            with_ext if File.exist?(File.join(config.assets_dir, dir, with_ext))
38  
-          end
39  
-
40  
-          source_with_ext || source
41  
-        end
42  
-
43  
-        # Break out the asset path rewrite in case plugins wish to put the asset id
44  
-        # someplace other than the query string.
45  
-        def rewrite_asset_path(source, dir, options = nil)
46  
-          source = "/#{dir}/#{source}" unless source[0] == ?/
47  
-          path = config.asset_path
48  
-
49  
-          if path && path.respond_to?(:call)
50  
-            return path.call(source)
51  
-          elsif path && path.is_a?(String)
52  
-            return path % [source]
53  
-          end
54  
-
55  
-          asset_id = rails_asset_id(source)
56  
-          if asset_id.empty?
57  
-            source
58  
-          else
59  
-            "#{source}?#{asset_id}"
60  
-          end
61  
-        end
62  
-
63  
-        mattr_accessor :asset_ids_cache
64  
-        self.asset_ids_cache = {}
65  
-
66  
-        mattr_accessor :asset_ids_cache_guard
67  
-        self.asset_ids_cache_guard = Mutex.new
68  
-
69  
-        # Use the RAILS_ASSET_ID environment variable or the source's
70  
-        # modification time as its cache-busting asset id.
71  
-        def rails_asset_id(source)
72  
-          if asset_id = ENV["RAILS_ASSET_ID"]
73  
-            asset_id
74  
-          else
75  
-            if self.cache_asset_ids && (asset_id = self.asset_ids_cache[source])
76  
-              asset_id
77  
-            else
78  
-              path = File.join(config.assets_dir, source)
79  
-              asset_id = File.exist?(path) ? File.mtime(path).to_i.to_s : ''
80  
-
81  
-              if self.cache_asset_ids
82  
-                add_to_asset_ids_cache(source, asset_id)
83  
-              end
84  
-
85  
-              asset_id
86  
-            end
87  
-          end
88  
-        end
89  
-      end
90  
-
91  
-    end
92  
-  end
93  
-end
257  actionpack/lib/action_view/helpers/asset_url_helper.rb
... ...
@@ -1,4 +1,4 @@
1  
-require 'action_view/helpers/asset_tag_helpers/asset_paths'
  1
+require 'zlib'
2 2
 
3 3
 module ActionView
4 4
   # = Action View Asset URL Helpers
@@ -104,92 +104,120 @@ module Helpers #:nodoc:
104 104
     #     "http://asset%d.example.com", "https://asset1.example.com"
105 105
     #   )
106 106
     #
107  
-    # === Customizing the asset path
108  
-    #
109  
-    # By default, Rails appends asset's timestamps to all asset paths. This allows
110  
-    # you to set a cache-expiration date for the asset far into the future, but
111  
-    # still be able to instantly invalidate it by simply updating the file (and
112  
-    # hence updating the timestamp, which then updates the URL as the timestamp
113  
-    # is part of that, which in turn busts the cache).
114  
-    #
115  
-    # It's the responsibility of the web server you use to set the far-future
116  
-    # expiration date on cache assets that you need to take advantage of this
117  
-    # feature. Here's an example for Apache:
118  
-    #
119  
-    #   # Asset Expiration
120  
-    #   ExpiresActive On
121  
-    #   <FilesMatch "\.(ico|gif|jpe?g|png|js|css)$">
122  
-    #     ExpiresDefault "access plus 1 year"
123  
-    #   </FilesMatch>
124  
-    #
125  
-    # Also note that in order for this to work, all your application servers must
126  
-    # return the same timestamps. This means that they must have their clocks
127  
-    # synchronized. If one of them drifts out of sync, you'll see different
128  
-    # timestamps at random and the cache won't work. In that case the browser
129  
-    # will request the same assets over and over again even thought they didn't
130  
-    # change. You can use something like Live HTTP Headers for Firefox to verify
131  
-    # that the cache is indeed working.
132  
-    #
133  
-    # This strategy works well enough for most server setups and requires the
134  
-    # least configuration, but if you deploy several application servers at
135  
-    # different times - say to handle a temporary spike in load - then the
136  
-    # asset time stamps will be out of sync. In a setup like this you may want
137  
-    # to set the way that asset paths are generated yourself.
138  
-    #
139  
-    # Altering the asset paths that Rails generates can be done in two ways.
140  
-    # The easiest is to define the RAILS_ASSET_ID environment variable. The
141  
-    # contents of this variable will always be used in preference to
142  
-    # calculated timestamps. A more complex but flexible way is to set
143  
-    # <tt>ActionController::Base.config.asset_path</tt> to a proc
144  
-    # that takes the unmodified asset path and returns the path needed for
145  
-    # your asset caching to work. Typically you'd do something like this in
146  
-    # <tt>config/environments/production.rb</tt>:
147  
-    #
148  
-    #   # Normally you'd calculate RELEASE_NUMBER at startup.
149  
-    #   RELEASE_NUMBER = 12345
150  
-    #   config.action_controller.asset_path = proc { |asset_path|
151  
-    #     "/release-#{RELEASE_NUMBER}#{asset_path}"
152  
-    #   }
153  
-    #
154  
-    # This example would cause the following behavior on all servers no
155  
-    # matter when they were deployed:
156  
-    #
157  
-    #   image_tag("rails.png")
158  
-    #   # => <img alt="Rails" src="/release-12345/images/rails.png" />
159  
-    #   stylesheet_link_tag("application")
160  
-    #   # => <link href="/release-12345/stylesheets/application.css?1232285206" media="screen" rel="stylesheet" />
161  
-    #
162  
-    # Changing the asset_path does require that your web servers have
163  
-    # knowledge of the asset template paths that you rewrite to so it's not
164  
-    # suitable for out-of-the-box use. To use the example given above you
165  
-    # could use something like this in your Apache VirtualHost configuration:
166  
-    #
167  
-    #   <LocationMatch "^/release-\d+/(images|javascripts|stylesheets)/.*$">
168  
-    #     # Some browsers still send conditional-GET requests if there's a
169  
-    #     # Last-Modified header or an ETag header even if they haven't
170  
-    #     # reached the expiry date sent in the Expires header.
171  
-    #     Header unset Last-Modified
172  
-    #     Header unset ETag
173  
-    #     FileETag None
174  
-    #
175  
-    #     # Assets requested using a cache-busting filename should be served
176  
-    #     # only once and then cached for a really long time. The HTTP/1.1
177  
-    #     # spec frowns on hugely-long expiration times though and suggests
178  
-    #     # that assets which never expire be served with an expiration date
179  
-    #     # 1 year from access.
180  
-    #     ExpiresActive On
181  
-    #     ExpiresDefault "access plus 1 year"
182  
-    #   </LocationMatch>
183  
-    #
184  
-    #   # We use cached-busting location names with the far-future expires
185  
-    #   # headers to ensure that if a file does change it can force a new
186  
-    #   # request. The actual asset filenames are still the same though so we
187  
-    #   # need to rewrite the location from the cache-busting location to the
188  
-    #   # real asset location so that we can serve it.
189  
-    #   RewriteEngine On
190  
-    #   RewriteRule ^/release-\d+/(images|javascripts|stylesheets)/(.*)$ /$1/$2 [L]
191  
-    #
192 107
     module AssetUrlHelper
  108
+      URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}
  109
+
  110
+      # Computes the path to asset in public directory. If :type
  111
+      # options is set, a file extension will be appended and scoped
  112
+      # to the corresponding public directory.
  113
+      #
  114
+      # All other asset *_path helpers delegate through this method.
  115
+      #
  116
+      #   asset_path "application.js"                     # => /application.js
  117
+      #   asset_path "application", type: :javascript     # => /javascripts/application.js
  118
+      #   asset_path "application", type: :stylesheet     # => /stylesheets/application.css
  119
+      #   asset_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
  120
+      def asset_path(source, options = {})
  121
+        source = source.to_s
  122
+        return "" unless source.present?
  123
+        return source if source =~ URI_REGEXP
  124
+
  125
+        if extname = compute_asset_extname(source, options)
  126
+          source = "#{source}#{extname}"
  127
+        end
  128
+
  129
+        if source[0] != ?/
  130
+          source = compute_asset_path(source, options)
  131
+        end
  132
+
  133
+        relative_url_root = (defined?(config.relative_url_root) && config.relative_url_root) ||
  134
+          (respond_to?(:request) && request.try(:script_name))
  135
+        if relative_url_root
  136
+          source = "#{relative_url_root}#{source}" unless source.starts_with?("#{relative_url_root}/")
  137
+        end
  138
+
  139
+        if host = compute_asset_host(source, options)
  140
+          source = "#{host}#{source}"
  141
+        end
  142
+
  143
+        source
  144
+      end
  145
+      alias_method :path_to_asset, :asset_path # aliased to avoid conflicts with a asset_path named route
  146
+
  147
+      # Computes the full URL to a asset in the public directory. This
  148
+      # will use +asset_path+ internally, so most of their behaviors
  149
+      # will be the same.
  150
+      def asset_url(source, options = {})
  151
+        path_to_asset(source, options.merge(:protocol => :request))
  152
+      end
  153
+      alias_method :url_to_asset, :asset_url # aliased to avoid conflicts with an asset_url named route
  154
+
  155
+      ASSET_EXTENSIONS = {
  156
+        javascript: '.js',
  157
+        stylesheet: '.css'
  158
+      }
  159
+
  160
+      # Compute extname to append to asset path. Returns nil if
  161
+      # nothing should be added.
  162
+      def compute_asset_extname(source, options = {})
  163
+        return if options[:extname] == false
  164
+        extname = options[:extname] || ASSET_EXTENSIONS[options[:type]]
  165
+        extname if extname && File.extname(source) != extname
  166
+      end
  167
+
  168
+      # Maps asset types to public directory.
  169
+      ASSET_PUBLIC_DIRECTORIES = {
  170
+        audio:      '/audios',
  171
+        font:       '/fonts',
  172
+        image:      '/images',
  173
+        javascript: '/javascripts',
  174
+        stylesheet: '/stylesheets',
  175
+        video:      '/videos'
  176
+      }
  177
+
  178
+      # Computes asset path to public directory. Plugins and
  179
+      # extensions can override this method to point to custom assets
  180
+      # or generate digested paths or query strings.
  181
+      def compute_asset_path(source, options = {})
  182
+        dir = ASSET_PUBLIC_DIRECTORIES[options[:type]] || ""
  183
+        File.join(dir, source)
  184
+      end
  185
+
  186
+      # Pick an asset host for this source. Returns +nil+ if no host is set,
  187
+      # the host if no wildcard is set, the host interpolated with the
  188
+      # numbers 0-3 if it contains <tt>%d</tt> (the number is the source hash mod 4),
  189
+      # or the value returned from invoking call on an object responding to call
  190
+      # (proc or otherwise).
  191
+      def compute_asset_host(source = "", options = {})
  192
+        request = self.request if respond_to?(:request)
  193
+        host = config.asset_host if defined? config.asset_host
  194
+        host ||= request.base_url if request && options[:protocol] == :request
  195
+        return unless host
  196
+
  197
+        if host.respond_to?(:call)
  198
+          arity = host.respond_to?(:arity) ? host.arity : host.method(:call).arity
  199
+          args = [source]
  200
+          args << request if request && (arity > 1 || arity < 0)
  201
+          host = host.call(*args)
  202
+        elsif host =~ /%d/
  203
+          host = host % (Zlib.crc32(source) % 4)
  204
+        end
  205
+
  206
+        if host =~ URI_REGEXP
  207
+          host
  208
+        else
  209
+          protocol = options[:protocol] || config.default_asset_host_protocol || (request ? :request : :relative)
  210
+          case protocol
  211
+          when :relative
  212
+            "//#{host}"
  213
+          when :request
  214
+            "#{request.protocol}#{host}"
  215
+          else
  216
+            "#{protocol}://#{host}"
  217
+          end
  218
+        end
  219
+      end
  220
+
193 221
       # Computes the path to a javascript asset in the public javascripts directory.
194 222
       # If the +source+ filename has no extension, .js will be appended (except for explicit URIs)
195 223
       # Full paths from the document root will be passed through.
@@ -200,15 +228,15 @@ module AssetUrlHelper
200 228
       #   javascript_path "/dir/xmlhr"                         # => /dir/xmlhr.js
201 229
       #   javascript_path "http://www.example.com/js/xmlhr"    # => http://www.example.com/js/xmlhr
202 230
       #   javascript_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
203  
-      def javascript_path(source)
204  
-        asset_paths.compute_public_path(source, 'javascripts', :ext => 'js')
  231
+      def javascript_path(source, options = {})
  232
+        path_to_asset(source, {type: :javascript}.merge!(options))
205 233
       end
206 234
       alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
207 235
 
208 236
       # Computes the full URL to a javascript asset in the public javascripts directory.
209 237
       # This will use +javascript_path+ internally, so most of their behaviors will be the same.
210  
-      def javascript_url(source)
211  
-        URI.join(current_host, path_to_javascript(source)).to_s
  238
+      def javascript_url(source, options = {})
  239
+        url_to_asset(source, {type: :javascript}.merge!(options))
212 240
       end
213 241
       alias_method :url_to_javascript, :javascript_url # aliased to avoid conflicts with a javascript_url named route
214 242
 
@@ -222,15 +250,15 @@ def javascript_url(source)
222 250
       #   stylesheet_path "/dir/style.css"                         # => /dir/style.css
223 251
       #   stylesheet_path "http://www.example.com/css/style"       # => http://www.example.com/css/style
224 252
       #   stylesheet_path "http://www.example.com/css/style.css"   # => http://www.example.com/css/style.css
225  
-      def stylesheet_path(source)
226  
-        asset_paths.compute_public_path(source, 'stylesheets', :ext => 'css', :protocol => :request)
  253
+      def stylesheet_path(source, options = {})
  254
+        path_to_asset(source, {type: :stylesheet}.merge!(options))
227 255
       end
228 256
       alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
229 257
 
230 258
       # Computes the full URL to a stylesheet asset in the public stylesheets directory.
231 259
       # This will use +stylesheet_path+ internally, so most of their behaviors will be the same.
232  
-      def stylesheet_url(source)
233  
-        URI.join(current_host, path_to_stylesheet(source)).to_s
  260
+      def stylesheet_url(source, options = {})
  261
+        url_to_asset(source, {type: :stylesheet}.merge!(options))
234 262
       end
235 263
       alias_method :url_to_stylesheet, :stylesheet_url # aliased to avoid conflicts with a stylesheet_url named route
236 264
 
@@ -247,15 +275,15 @@ def stylesheet_url(source)
247 275
       # If you have images as application resources this method may conflict with their named routes.
248 276
       # The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
249 277
       # plugin authors are encouraged to do so.
250  
-      def image_path(source)
251  
-        source.present? ? asset_paths.compute_public_path(source, 'images') : ""
  278
+      def image_path(source, options = {})
  279
+        path_to_asset(source, {type: :image}.merge!(options))
252 280
       end
253 281
       alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
254 282
 
255 283
       # Computes the full URL to an image asset.
256 284
       # This will use +image_path+ internally, so most of their behaviors will be the same.
257  
-      def image_url(source)
258  
-        URI.join(current_host, path_to_image(source)).to_s
  285
+      def image_url(source, options = {})
  286
+        url_to_asset(source, {type: :image}.merge!(options))
259 287
       end
260 288
       alias_method :url_to_image, :image_url # aliased to avoid conflicts with an image_url named route
261 289
 
@@ -268,15 +296,15 @@ def image_url(source)
268 296
       #   video_path("trailers/hd.avi")                               # => /videos/trailers/hd.avi
269 297
       #   video_path("/trailers/hd.avi")                              # => /trailers/hd.avi
270 298
       #   video_path("http://www.example.com/vid/hd.avi")             # => http://www.example.com/vid/hd.avi
271  
-      def video_path(source)
272  
-        asset_paths.compute_public_path(source, 'videos')
  299
+      def video_path(source, options = {})
  300
+        path_to_asset(source, {type: :video}.merge!(options))
273 301
       end
274 302
       alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
275 303
 
276 304
       # Computes the full URL to a video asset in the public videos directory.
277 305
       # This will use +video_path+ internally, so most of their behaviors will be the same.
278  
-      def video_url(source)
279  
-        URI.join(current_host, path_to_video(source)).to_s
  306
+      def video_url(source, options = {})
  307
+        url_to_asset(source, {type: :video}.merge!(options))
280 308
       end
281 309
       alias_method :url_to_video, :video_url # aliased to avoid conflicts with an video_url named route
282 310
 
@@ -289,15 +317,15 @@ def video_url(source)
289 317
       #   audio_path("sounds/horse.wav")                                 # => /audios/sounds/horse.wav
290 318
       #   audio_path("/sounds/horse.wav")                                # => /sounds/horse.wav
291 319
       #   audio_path("http://www.example.com/sounds/horse.wav")          # => http://www.example.com/sounds/horse.wav
292  
-      def audio_path(source)
293  
-        asset_paths.compute_public_path(source, 'audios')
  320
+      def audio_path(source, options = {})
  321
+        path_to_asset(source, {type: :audio}.merge!(options))
294 322
       end
295 323
       alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
296 324
 
297 325
       # Computes the full URL to an audio asset in the public audios directory.
298 326
       # This will use +audio_path+ internally, so most of their behaviors will be the same.
299  
-      def audio_url(source)
300  
-        URI.join(current_host, path_to_audio(source)).to_s
  327
+      def audio_url(source, options = {})
  328
+        url_to_asset(source, {type: :audio}.merge!(options))
301 329
       end
302 330
       alias_method :url_to_audio, :audio_url # aliased to avoid conflicts with an audio_url named route
303 331
 
@@ -309,26 +337,17 @@ def audio_url(source)
309 337
       #   font_path("dir/font.ttf")                                   # => /assets/dir/font.ttf
310 338
       #   font_path("/dir/font.ttf")                                  # => /dir/font.ttf
311 339
       #   font_path("http://www.example.com/dir/font.ttf")            # => http://www.example.com/dir/font.ttf
312  
-      def font_path(source)
313  
-        asset_paths.compute_public_path(source, 'fonts')
  340
+      def font_path(source, options = {})
  341
+        path_to_asset(source, {type: :font}.merge!(options))
314 342
       end
315 343
       alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route
316 344
 
317 345
       # Computes the full URL to a font asset.
318 346
       # This will use +font_path+ internally, so most of their behaviors will be the same.
319  
-      def font_url(source)
320  
-        URI.join(current_host, path_to_font(source)).to_s
  347
+      def font_url(source, options = {})
  348
+        url_to_asset(source, {type: :font}.merge!(options))
321 349
       end
322 350
       alias_method :url_to_font, :font_url # aliased to avoid conflicts with an font_url named route
323  
-
324  
-      private
325  
-        def asset_paths
326  
-          @asset_paths ||= AssetTagHelper::AssetPaths.new(config, controller)
327  
-        end
328  
-
329  
-        def current_host
330  
-          url_for(:only_path => false)
331  
-        end
332 351
     end
333 352
   end
334 353
 end
8  actionpack/lib/action_view/railtie.rb
@@ -20,14 +20,6 @@ class Railtie < Rails::Railtie
20 20
       ActiveSupport.on_load(:action_view) { self.logger ||= Rails.logger }
21 21
     end
22 22
 
23  
-    initializer "action_view.cache_asset_ids" do |app|
24  
-      unless app.config.cache_classes
25  
-        ActiveSupport.on_load(:action_view) do
26  
-          ActionView::Helpers::AssetTagHelper::AssetPaths.cache_asset_ids = false
27  
-        end
28  
-      end
29  
-    end
30  
-
31 23
     initializer "action_view.set_configs" do |app|
32 24
       ActiveSupport.on_load(:action_view) do
33 25
         app.config.action_view.each do |k,v|
262  actionpack/test/template/asset_tag_helper_test.rb
@@ -13,27 +13,10 @@ def config
13 13
 class AssetTagHelperTest < ActionView::TestCase
14 14
   tests ActionView::Helpers::AssetTagHelper
15 15
 
  16
+  attr_reader :request
  17
+
16 18
   def setup
17 19
     super
18  
-    silence_warnings do
19  
-      ActionView::Helpers::AssetTagHelper.send(
20  
-        :const_set,
21  
-        :JAVASCRIPTS_DIR,
22  
-        File.dirname(__FILE__) + "/../fixtures/public/javascripts"
23  
-      )
24  
-
25  
-      ActionView::Helpers::AssetTagHelper.send(
26  
-        :const_set,
27  
-        :STYLESHEETS_DIR,
28  
-        File.dirname(__FILE__) + "/../fixtures/public/stylesheets"
29  
-      )
30  
-
31  
-      ActionView::Helpers::AssetTagHelper.send(
32  
-        :const_set,
33  
-        :ASSETS_DIR,
34  
-        File.dirname(__FILE__) + "/../fixtures/public"
35  
-      )
36  
-    end
37 20
 
38 21
     @controller = BasicController.new
39 22
 
@@ -42,6 +25,7 @@ def setup
42 25
       def protocol() 'http://' end
43 26
       def ssl?() false end
44 27
       def host_with_port() 'localhost' end
  28
+      def base_url() 'http://www.example.com' end
45 29
     end.new
46 30
 
47 31
     @controller.request = @request
@@ -51,9 +35,23 @@ def url_for(*args)
51 35
     "http://www.example.com"
52 36
   end
53 37
 
54  
-  def teardown
55  
-    ENV.delete('RAILS_ASSET_ID')
56  
-  end
  38
+  AssetPathToTag = {
  39
+    %(asset_path("foo"))          => %(/foo),
  40
+    %(asset_path("style.css"))    => %(/style.css),
  41
+    %(asset_path("xmlhr.js"))     => %(/xmlhr.js),
  42
+    %(asset_path("xml.png"))      => %(/xml.png),
  43
+    %(asset_path("dir/xml.png"))  => %(/dir/xml.png),
  44
+    %(asset_path("/dir/xml.png")) => %(/dir/xml.png),
  45
+
  46
+    %(asset_path("script.min"))       => %(/script.min),
  47
+    %(asset_path("script.min.js"))    => %(/script.min.js),
  48
+    %(asset_path("style.min"))        => %(/style.min),
  49
+    %(asset_path("style.min.css"))    => %(/style.min.css),
  50
+
  51
+    %(asset_path("style", type: :stylesheet)) => %(/stylesheets/style.css),
  52
+    %(asset_path("xmlhr", type: :javascript)) => %(/javascripts/xmlhr.js),
  53
+    %(asset_path("xml.png", type: :image))    => %(/images/xml.png)
  54
+  }
57 55
 
58 56
   AutoDiscoveryToTag = {
59 57
     %(auto_discovery_link_tag) => %(<link href="http://www.example.com" rel="alternate" title="RSS" type="application/rss+xml" />),
@@ -73,7 +71,9 @@ def teardown
73 71
   JavascriptPathToTag = {
74 72
     %(javascript_path("xmlhr")) => %(/javascripts/xmlhr.js),
75 73
     %(javascript_path("super/xmlhr")) => %(/javascripts/super/xmlhr.js),
76  
-    %(javascript_path("/super/xmlhr.js")) => %(/super/xmlhr.js)
  74
+    %(javascript_path("/super/xmlhr.js")) => %(/super/xmlhr.js),
  75
+    %(javascript_path("xmlhr.min")) => %(/javascripts/xmlhr.min.js),
  76
+    %(javascript_path("xmlhr.min.js")) => %(/javascripts/xmlhr.min.js)
77 77
   }
78 78
 
79 79
   PathToJavascriptToTag = {
@@ -98,7 +98,6 @@ def teardown
98 98
     %(javascript_include_tag("bank")) => %(<script src="/javascripts/bank.js" ></script>),
99 99
     %(javascript_include_tag("bank.js")) => %(<script src="/javascripts/bank.js" ></script>),
100 100
     %(javascript_include_tag("bank", :lang => "vbscript")) => %(<script lang="vbscript" src="/javascripts/bank.js" ></script>),
101  
-    %(javascript_include_tag("common.javascript", "/elsewhere/cools")) => %(<script src="/javascripts/common.javascript" ></script>\n<script src="/elsewhere/cools.js" ></script>),
102 101
 
103 102
     %(javascript_include_tag("http://example.com/all")) => %(<script src="http://example.com/all"></script>),
104 103
     %(javascript_include_tag("http://example.com/all.js")) => %(<script src="http://example.com/all.js"></script>),
@@ -109,14 +108,17 @@ def teardown
109 108
     %(stylesheet_path("bank")) => %(/stylesheets/bank.css),
110 109
     %(stylesheet_path("bank.css")) => %(/stylesheets/bank.css),
111 110
     %(stylesheet_path('subdir/subdir')) => %(/stylesheets/subdir/subdir.css),
112  
-    %(stylesheet_path('/subdir/subdir.css')) => %(/subdir/subdir.css)
  111
+    %(stylesheet_path('/subdir/subdir.css')) => %(/subdir/subdir.css),
  112
+    %(stylesheet_path("style.min")) => %(/stylesheets/style.min.css),
  113
+    %(stylesheet_path("style.min.css")) => %(/stylesheets/style.min.css)
113 114
   }
114 115
 
115 116
   PathToStyleToTag = {
116 117
     %(path_to_stylesheet("style")) => %(/stylesheets/style.css),
117 118
     %(path_to_stylesheet("style.css")) => %(/stylesheets/style.css),
118 119
     %(path_to_stylesheet('dir/file')) => %(/stylesheets/dir/file.css),
119  
-    %(path_to_stylesheet('/dir/file.rcss')) => %(/dir/file.rcss)
  120
+    %(path_to_stylesheet('/dir/file.rcss', :extname => false)) => %(/dir/file.rcss),
  121
+    %(path_to_stylesheet('/dir/file', :extname => '.rcss')) => %(/dir/file.rcss)
120 122
   }
121 123
 
122 124
   StyleUrlToTag = {
@@ -130,7 +132,8 @@ def teardown
130 132
     %(url_to_stylesheet("style")) => %(http://www.example.com/stylesheets/style.css),
131 133
     %(url_to_stylesheet("style.css")) => %(http://www.example.com/stylesheets/style.css),
132 134
     %(url_to_stylesheet('dir/file')) => %(http://www.example.com/stylesheets/dir/file.css),
133  
-    %(url_to_stylesheet('/dir/file.rcss')) => %(http://www.example.com/dir/file.rcss)
  135
+    %(url_to_stylesheet('/dir/file.rcss', :extname => false)) => %(http://www.example.com/dir/file.rcss),
  136
+    %(url_to_stylesheet('/dir/file', :extname => '.rcss')) => %(http://www.example.com/dir/file.rcss)
134 137
   }
135 138
 
136 139
   StyleLinkToTag = {
@@ -139,7 +142,6 @@ def teardown
139 142
     %(stylesheet_link_tag("/elsewhere/file")) => %(<link href="/elsewhere/file.css" media="screen" rel="stylesheet" />),
140 143
     %(stylesheet_link_tag("subdir/subdir")) => %(<link href="/stylesheets/subdir/subdir.css" media="screen" rel="stylesheet" />),
141 144
     %(stylesheet_link_tag("bank", :media => "all")) => %(<link href="/stylesheets/bank.css" media="all" rel="stylesheet" />),
142  
-    %(stylesheet_link_tag("random.styles", "/elsewhere/file")) => %(<link href="/stylesheets/random.styles" media="screen" rel="stylesheet" />\n<link href="/elsewhere/file.css" media="screen" rel="stylesheet" />),
143 145
 
144 146
     %(stylesheet_link_tag("http://www.example.com/styles/style")) => %(<link href="http://www.example.com/styles/style" media="screen" rel="stylesheet" />),
145 147
     %(stylesheet_link_tag("http://www.example.com/styles/style.css")) => %(<link href="http://www.example.com/styles/style.css" media="screen" rel="stylesheet" />),
@@ -293,6 +295,18 @@ def test_autodiscovery_link_tag_deprecated_types
293 295
     assert_equal expected, result
294 296
   end
295 297
 
  298
+  def test_asset_path_tag
  299
+    AssetPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
  300
+  end
  301
+
  302
+  def test_compute_asset_public_path
  303
+    assert_equal "/robots.txt", compute_asset_path("robots.txt")
  304
+    assert_equal "/robots.txt", compute_asset_path("/robots.txt")
  305
+    assert_equal "/javascripts/foo.js", compute_asset_path("foo.js", :type => :javascript)
  306
+    assert_equal "/javascripts/foo.js", compute_asset_path("/foo.js", :type => :javascript)
  307
+    assert_equal "/stylesheets/foo.css", compute_asset_path("foo.css", :type => :stylesheet)
  308
+  end
  309
+
296 310
   def test_auto_discovery_link_tag
297 311
     AutoDiscoveryToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
298 312
   end
@@ -313,8 +327,7 @@ def test_url_to_javascript_alias_for_javascript_url
313 327
     UrlToJavascriptToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
314 328
   end
315 329
 
316  
-  def test_javascript_include_tag_with_blank_asset_id
317  
-    ENV["RAILS_ASSET_ID"] = ""
  330
+  def test_javascript_include_tag
318 331
     JavascriptIncludeToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
319 332
   end
320 333
 
@@ -332,17 +345,7 @@ def test_javascript_include_tag_is_html_safe
332 345
     assert javascript_include_tag("prototype").html_safe?
333 346
   end
334 347
 
335  
-  def test_all_javascript_expansion_not_include_application_js_if_not_exists
336  
-    FileUtils.mv(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.js'),
337  
-      File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.bak'))
338  
-    assert_no_match(/application\.js/, javascript_include_tag(:all))
339  
-  ensure
340  
-    FileUtils.mv(File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.bak'),
341  
-      File.join(ActionView::Helpers::AssetTagHelper::JAVASCRIPTS_DIR, 'application.js'))
342  
-  end
343  
-
344 348
   def test_stylesheet_path
345  
-    ENV["RAILS_ASSET_ID"] = ""
346 349
     StylePathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
347 350
   end
348 351
 
@@ -351,7 +354,6 @@ def test_path_to_stylesheet_alias_for_stylesheet_path
351 354
   end
352 355
 
353 356
   def test_stylesheet_url
354  
-    ENV["RAILS_ASSET_ID"] = ""
355 357
     StyleUrlToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
356 358
   end
357 359
 
@@ -360,7 +362,6 @@ def test_url_to_stylesheet_alias_for_stylesheet_url
360 362
   end
361 363
 
362 364
   def test_stylesheet_link_tag
363  
-    ENV["RAILS_ASSET_ID"] = ""
364 365
     StyleLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
365 366
   end
366 367
 
@@ -375,7 +376,6 @@ def test_stylesheet_link_tag_with_missing_source
375 376
   end
376 377
 
377 378
   def test_stylesheet_link_tag_is_html_safe
378  
-    ENV["RAILS_ASSET_ID"] = ""
379 379
     assert stylesheet_link_tag('dir/file').html_safe?
380 380
     assert stylesheet_link_tag('dir/other/file', 'dir/file2').html_safe?
381 381
   end
@@ -385,7 +385,6 @@ def test_stylesheet_link_tag_escapes_options
385 385
   end
386 386
 
387 387
   def test_stylesheet_link_tag_should_not_output_the_same_asset_twice
388  
-    ENV["RAILS_ASSET_ID"] = ""
389 388
     assert_dom_equal  %(<link href="/stylesheets/wellington.css" media="screen" rel="stylesheet" />\n<link href="/stylesheets/amsterdam.css" media="screen" rel="stylesheet" />), stylesheet_link_tag('wellington', 'wellington', 'amsterdam')
390 389
   end
391 390
 
@@ -427,21 +426,6 @@ def test_favicon_link_tag
427 426
     FaviconLinkToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
428 427
   end
429 428
 
430  
-  def test_image_tag_windows_behaviour
431  
-    old_asset_id, ENV["RAILS_ASSET_ID"] = ENV["RAILS_ASSET_ID"], "1"
432  
-    # This simulates the behavior of File#exist? on windows when testing a file ending in "."
433  
-    # If the file "rails.png" exists, windows will return true when asked if "rails.png." exists (notice trailing ".")
434  
-    # OS X, linux etc will return false in this case.
435  
-    File.stubs(:exist?).with('template/../fixtures/public/images/rails.png.').returns(true)
436  
-    assert_equal '<img alt="Rails" src="/images/rails.png?1" />', image_tag('rails.png')
437  
-  ensure
438  
-    if old_asset_id
439  
-      ENV["RAILS_ASSET_ID"] = old_asset_id
440  
-    else
441  
-      ENV.delete("RAILS_ASSET_ID")
442  
-    end
443  
-  end
444  
-
445 429
   def test_video_path
446 430
     VideoPathToTag.each { |method, tag| assert_dom_equal(tag, eval(method)) }
447 431
   end
@@ -490,27 +474,6 @@ def test_video_audio_tag_does_not_modify_options
490 474
     assert_equal({:autoplay => true}, options)
491 475
   end
492 476
 
493  
-  def test_timebased_asset_id
494  
-    expected_time = File.mtime(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).to_i.to_s
495  
-    assert_equal %(<img alt="Rails" src="/images/rails.png?#{expected_time}" />), image_tag("rails.png")
496  
-  end
497  
-
498  
-  def test_string_asset_id
499  
-    @controller.config.asset_path = "/assets.v12345%s"
500  
-
501  
-    expected_path = "/assets.v12345/images/rails.png"
502  
-    assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
503  
-  end
504  
-
505  
-  def test_proc_asset_id
506  
-    @controller.config.asset_path = Proc.new do |asset_path|
507  
-      "/assets.v12345#{asset_path}"
508  
-    end
509  
-
510  
-    expected_path = "/assets.v12345/images/rails.png"
511  
-    assert_equal %(<img alt="Rails" src="#{expected_path}" />), image_tag("rails.png")
512  
-  end
513  
-
514 477
   def test_image_tag_interpreting_email_cid_correctly
515 478
     # An inline image has no need for an alt tag to be automatically generated from the cid:
516 479
     assert_equal '<img src="cid:thi%25%25sis@acontentid" />', image_tag("cid:thi%25%25sis@acontentid")
@@ -520,37 +483,6 @@ def test_image_tag_interpreting_email_adding_optional_alt_tag
520 483
     assert_equal '<img alt="Image" src="cid:thi%25%25sis@acontentid" />', image_tag("cid:thi%25%25sis@acontentid", :alt => "Image")
521 484
   end
522 485
 
523  
-  def test_timebased_asset_id_with_relative_url_root
524  
-    @controller.config.relative_url_root = "/collaboration/hieraki"
525  
-    expected_time = File.mtime(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).to_i.to_s
526  
-    assert_equal %(<img alt="Rails" src="#{@controller.config.relative_url_root}/images/rails.png?#{expected_time}" />), image_tag("rails.png")
527  
-  end
528  
-
529  
-  # Same as above, but with script_name
530  
-  def test_timebased_asset_id_with_script_name
531  
-    @request.script_name = "/collaboration/hieraki"
532  
-    expected_time = File.mtime(File.expand_path(File.dirname(__FILE__) + "/../fixtures/public/images/rails.png")).to_i.to_s
533  
-    assert_equal %(<img alt="Rails" src="#{@request.script_name}/images/rails.png?#{expected_time}" />), image_tag("rails.png")
534  
-  end
535  
-
536  
-  def test_should_skip_asset_id_on_complete_url
537  
-    assert_equal %(<img alt="Rails" src="http://www.example.com/rails.png" />), image_tag("http://www.example.com/rails.png")
538  
-  end
539  
-
540  
-  def test_should_skip_asset_id_on_scheme_relative_url
541  
-    assert_equal %(<img alt="Rails" src="//www.example.com/rails.png" />), image_tag("//www.example.com/rails.png")
542  
-  end
543  
-
544  
-  def test_should_use_preset_asset_id
545  
-    ENV["RAILS_ASSET_ID"] = "4500"
546  
-    assert_equal %(<img alt="Rails" src="/images/rails.png?4500" />), image_tag("rails.png")
547  
-  end
548  
-
549  
-  def test_preset_empty_asset_id
550  
-    ENV["RAILS_ASSET_ID"] = ""
551  
-    assert_equal %(<img alt="Rails" src="/images/rails.png" />), image_tag("rails.png")
552  
-  end
553  
-
554 486
   def test_should_not_modify_source_string
555 487
     source = '/images/rails.png'
556 488
     copy = source.dup
@@ -559,7 +491,6 @@ def test_should_not_modify_source_string
559 491
   end
560 492
 
561 493
   def test_caching_image_path_with_caching_and_proc_asset_host_using_request
562  
-    ENV['RAILS_ASSET_ID'] = ''
563 494
     @controller.config.asset_host = Proc.new do |source, request|
564 495
       if request.ssl?
565 496
         "#{request.protocol}#{request.host_with_port}"
@@ -579,12 +510,14 @@ def test_caching_image_path_with_caching_and_proc_asset_host_using_request
579 510
 class AssetTagHelperNonVhostTest < ActionView::TestCase
580 511
   tests ActionView::Helpers::AssetTagHelper
581 512
 
  513
+  attr_reader :request
  514
+
582 515
   def setup
583 516
     super
584 517
     @controller = BasicController.new
585 518
     @controller.config.relative_url_root = "/collaboration/hieraki"
586 519
 
587  
-    @request = Struct.new(:protocol).new("gopher://")
  520
+    @request = Struct.new(:protocol, :base_url).new("gopher://", "gopher://www.example.com")
588 521
     @controller.request = @request
589 522
   end
590 523
 
@@ -599,10 +532,33 @@ def test_should_compute_proper_path
599 532
     assert_dom_equal(%(/collaboration/hieraki/images/xml.png), image_path("xml.png"))
600 533
   end
601 534
 
  535
+  def test_should_return_nothing_if_asset_host_isnt_configured
  536
+    assert_equal nil, compute_asset_host("foo")
  537
+  end
  538
+
  539
+  def test_should_current_request_host_is_always_returned_for_request
  540
+    assert_equal "gopher://www.example.com", compute_asset_host("foo", :protocol => :request)
  541
+  end
  542
+
602 543
   def test_should_ignore_relative_root_path_on_complete_url
603 544
     assert_dom_equal(%(http://www.example.com/images/xml.png), image_path("http://www.example.com/images/xml.png"))
604 545
   end
605 546
 
  547
+  def test_should_return_simple_string_asset_host
  548
+    @controller.config.asset_host = "assets.example.com"
  549
+    assert_equal "gopher://assets.example.com", compute_asset_host("foo")
  550
+  end
  551
+
  552
+  def test_should_return_relative_asset_host
  553
+    @controller.config.asset_host = "assets.example.com"
  554
+    assert_equal "//assets.example.com", compute_asset_host("foo", :protocol => :relative)
  555
+  end
  556
+
  557
+  def test_should_return_custom_protocol_asset_host
  558
+    @controller.config.asset_host = "assets.example.com"
  559
+    assert_equal "ftp://assets.example.com", compute_asset_host("foo", :protocol => "ftp")
  560
+  end
  561
+
606 562
   def test_should_compute_proper_path_with_asset_host
607 563
     @controller.config.asset_host = "assets.example.com"
608 564
     assert_dom_equal(%(<link href="http://www.example.com/collaboration/hieraki" rel="alternate" title="RSS" type="application/rss+xml" />), auto_discovery_link_tag)
@@ -635,6 +591,11 @@ def test_should_compute_proper_url_with_asset_host_and_default_protocol
635 591
     assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/images/xml.png), image_url("xml.png"))
636 592
   end
637 593
 
  594
+  def test_should_return_asset_host_with_protocol
  595
+    @controller.config.asset_host = "http://assets.example.com"
  596
+    assert_equal "http://assets.example.com", compute_asset_host("foo")
  597
+  end
  598
+
638 599
   def test_should_ignore_asset_host_on_complete_url
639 600
     @controller.config.asset_host = "http://assets.example.com"
640 601
     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"))
@@ -645,6 +606,11 @@ def test_should_ignore_asset_host_on_scheme_relative_url
645 606
     assert_dom_equal(%(<link href="//bar.example.com/stylesheets/style.css" media="screen" rel="stylesheet" />), stylesheet_link_tag("//bar.example.com/stylesheets/style.css"))
646 607
   end
647 608
 
  609
+  def test_should_wildcard_asset_host
  610
+    @controller.config.asset_host = 'http://a%d.example.com'
  611
+    assert_match(%r(http://a[0123].example.com), compute_asset_host("foo"))
  612
+  end
  613
+
648 614
   def test_should_wildcard_asset_host_between_zero_and_four
649 615
     @controller.config.asset_host = 'http://a%d.example.com'
650 616
     assert_match(%r(http://a[0123].example.com/collaboration/hieraki/images/xml.png), image_path('xml.png'))
@@ -668,3 +634,73 @@ def test_assert_css_and_js_of_the_same_name_return_correct_extension
668 634
     assert_dom_equal(%(/collaboration/hieraki/stylesheets/foo.css), stylesheet_path("foo"))
669 635
   end
670 636
 end
  637
+
  638
+class AssetUrlHelperControllerTest < ActionView::TestCase
  639
+  tests ActionView::Helpers::AssetUrlHelper
  640
+
  641
+  def setup
  642
+    super
  643
+
  644
+    @controller = BasicController.new
  645
+    @controller.extend ActionView::Helpers::AssetUrlHelper
  646
+
  647
+    @request = Class.new do
  648
+      attr_accessor :script_name
  649
+      def protocol() 'http://' end
  650
+      def ssl?() false end
  651
+      def host_with_port() 'www.example.com' end
  652
+      def base_url() 'http://www.example.com' end
  653
+    end.new
  654
+
  655
+    @controller.request = @request
  656
+  end
  657
+
  658
+  def test_asset_path
  659
+    assert_equal "/foo", @controller.asset_path("foo")
  660
+  end
  661
+
  662
+  def test_asset_url
  663
+    assert_equal "http://www.example.com/foo", @controller.asset_url("foo")
  664
+  end
  665
+end
  666
+
  667
+class AssetUrlHelperEmptyModuleTest < ActionView::TestCase
  668
+  tests ActionView::Helpers::AssetUrlHelper
  669
+
  670
+  def setup
  671
+    super
  672
+
  673
+    @module = Module.new
  674
+    @module.extend ActionView::Helpers::AssetUrlHelper
  675
+  end
  676
+
  677
+  def test_asset_path
  678
+    assert_equal "/foo", @module.asset_path("foo")
  679
+  end
  680
+
  681
+  def test_asset_url
  682
+    assert_equal "/foo", @module.asset_url("foo")