Skip to content

Commit 4bcb05d

Browse files
committed
Merge pull request rails#1870 from chriseppstein/asset_urls_master
Asset urls master
2 parents a0960ec + 024bed3 commit 4bcb05d

File tree

8 files changed

+216
-72
lines changed

8 files changed

+216
-72
lines changed

actionpack/lib/action_view/asset_paths.rb

Lines changed: 86 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ module ActionView
44
class AssetPaths #:nodoc:
55
attr_reader :config, :controller
66

7-
def initialize(config, controller)
7+
def initialize(config, controller = nil)
88
@config = config
99
@controller = controller
1010
end
@@ -13,21 +13,29 @@ def initialize(config, controller)
1313
# Prefix with <tt>/dir/</tt> if lacking a leading +/+. Account for relative URL
1414
# roots. Rewrite the asset path for cache-busting asset ids. Include
1515
# asset host, if configured, with the correct request protocol.
16-
def compute_public_path(source, dir, ext = nil, include_host = true)
16+
#
17+
# When include_host is true and the asset host does not specify the protocol
18+
# the protocol parameter specifies how the protocol will be added.
19+
# When :relative (default), the protocol will be determined by the client using current protocol
20+
# When :request, the protocol will be the request protocol
21+
# Otherwise, the protocol is used (E.g. :http, :https, etc)
22+
def compute_public_path(source, dir, ext = nil, include_host = true, protocol = nil)
1723
source = source.to_s
1824
return source if is_uri?(source)
1925

2026
source = rewrite_extension(source, dir, ext) if ext
2127
source = rewrite_asset_path(source, dir)
22-
23-
if controller && include_host
24-
has_request = controller.respond_to?(:request)
25-
source = rewrite_host_and_protocol(source, has_request)
26-
end
27-
28+
source = rewrite_relative_url_root(source, relative_url_root) if has_request?
29+
source = rewrite_host_and_protocol(source, protocol) if include_host
2830
source
2931
end
3032

33+
# Return the filesystem path for the source
34+
def compute_source_path(source, dir, ext)
35+
source = rewrite_extension(source, dir, ext) if ext
36+
File.join(config.assets_dir, dir, source)
37+
end
38+
3139
def is_uri?(path)
3240
path =~ %r{^[-a-z]+://|^cid:|^//}
3341
end
@@ -46,13 +54,45 @@ def rewrite_relative_url_root(source, relative_url_root)
4654
relative_url_root && !source.starts_with?("#{relative_url_root}/") ? "#{relative_url_root}#{source}" : source
4755
end
4856

49-
def rewrite_host_and_protocol(source, has_request)
50-
source = rewrite_relative_url_root(source, controller.config.relative_url_root) if has_request
57+
def has_request?
58+
controller.respond_to?(:request)
59+
end
60+
61+
def rewrite_host_and_protocol(source, protocol = nil)
5162
host = compute_asset_host(source)
52-
if has_request && host && !is_uri?(host)
53-
host = "#{controller.request.protocol}#{host}"
63+
if host && !is_uri?(host)
64+
if (protocol || default_protocol) == :request && !has_request?
65+
host = nil
66+
else
67+
host = "#{compute_protocol(protocol)}#{host}"
68+
end
69+
end
70+
host.nil? ? source : "#{host}#{source}"
71+
end
72+
73+
def compute_protocol(protocol)
74+
protocol ||= default_protocol
75+
case protocol
76+
when :relative
77+
"//"
78+
when :request
79+
unless @controller
80+
invalid_asset_host!("The protocol requested was :request. Consider using :relative instead.")
81+
end
82+
@controller.request.protocol
83+
else
84+
"#{protocol}://"
5485
end
55-
"#{host}#{source}"
86+
end
87+
88+
def default_protocol
89+
protocol = @config.action_controller.default_asset_host_protocol if @config.action_controller.present?
90+
protocol ||= @config.default_asset_host_protocol
91+
protocol || (has_request? ? :request : :relative)
92+
end
93+
94+
def invalid_asset_host!(help_message)
95+
raise ActionController::RoutingError, "This asset host cannot be computed without a request in scope. #{help_message}"
5696
end
5797

5898
# Pick an asset host for this source. Returns +nil+ if no host is set,
@@ -61,19 +101,45 @@ def rewrite_host_and_protocol(source, has_request)
61101
# or the value returned from invoking call on an object responding to call
62102
# (proc or otherwise).
63103
def compute_asset_host(source)
64-
if host = config.asset_host
104+
if host = asset_host_config
65105
if host.respond_to?(:call)
66-
case host.is_a?(Proc) ? host.arity : host.method(:call).arity
67-
when 2
68-
request = controller.respond_to?(:request) && controller.request
69-
host.call(source, request)
70-
else
71-
host.call(source)
106+
args = [source]
107+
arity = arity_of(host)
108+
if arity > 1 && !has_request?
109+
invalid_asset_host!("Remove the second argument to your asset_host Proc if you do not need the request.")
72110
end
111+
args << current_request if (arity > 1 || arity < 0) && has_request?
112+
host.call(*args)
73113
else
74114
(host =~ /%d/) ? host % (source.hash % 4) : host
75115
end
76116
end
77117
end
118+
119+
def relative_url_root
120+
config = controller.config if controller.respond_to?(:config)
121+
config ||= config.action_controller if config.action_controller.present?
122+
config ||= config
123+
config.relative_url_root
124+
end
125+
126+
def asset_host_config
127+
if config.action_controller.present?
128+
config.action_controller.asset_host
129+
else
130+
config.asset_host
131+
end
132+
end
133+
134+
# Returns the current request if one exists.
135+
def current_request
136+
controller.request if has_request?
137+
end
138+
139+
# Returns the arity of a callable
140+
def arity_of(callable)
141+
callable.respond_to?(:arity) ? callable.arity : callable.method(:call).arity
142+
end
143+
78144
end
79145
end

actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,12 +60,16 @@ def include_tag(*sources)
6060

6161
private
6262

63-
def path_to_asset(source, include_host = true)
64-
asset_paths.compute_public_path(source, asset_name.to_s.pluralize, extension, include_host)
63+
def path_to_asset(source, include_host = true, protocol = nil)
64+
asset_paths.compute_public_path(source, asset_name.to_s.pluralize, extension, include_host, protocol)
65+
end
66+
67+
def path_to_asset_source(source)
68+
asset_paths.compute_source_path(source, asset_name.to_s.pluralize, extension)
6569
end
6670

6771
def compute_paths(*args)
68-
expand_sources(*args).collect { |source| asset_paths.compute_public_path(source, asset_name.pluralize, extension, false) }
72+
expand_sources(*args).collect { |source| path_to_asset_source(source) }
6973
end
7074

7175
def expand_sources(sources, recursive)
@@ -92,7 +96,7 @@ def update_source_list(list, source)
9296

9397
def ensure_sources!(sources)
9498
sources.each do |source|
95-
asset_file_path!(path_to_asset(source, false))
99+
asset_file_path!(path_to_asset_source(source))
96100
end
97101
end
98102

@@ -123,19 +127,14 @@ def write_asset_file_contents(joined_asset_path, asset_paths)
123127

124128
# Set mtime to the latest of the combined files to allow for
125129
# consistent ETag without a shared filesystem.
126-
mt = asset_paths.map { |p| File.mtime(asset_file_path(p)) }.max
130+
mt = asset_paths.map { |p| File.mtime(asset_file_path!(p)) }.max
127131
File.utime(mt, mt, joined_asset_path)
128132
end
129133

130-
def asset_file_path(path)
131-
File.join(config.assets_dir, path.split('?').first)
132-
end
133-
134-
def asset_file_path!(path, error_if_file_is_uri = false)
135-
if asset_paths.is_uri?(path)
134+
def asset_file_path!(absolute_path, error_if_file_is_uri = false)
135+
if asset_paths.is_uri?(absolute_path)
136136
raise(Errno::ENOENT, "Asset file #{path} is uri and cannot be merged into single file") if error_if_file_is_uri
137137
else
138-
absolute_path = asset_file_path(path)
139138
raise(Errno::ENOENT, "Asset file not found at '#{absolute_path}'" ) unless File.exist?(absolute_path)
140139
return absolute_path
141140
end

actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ def extension
1616
end
1717

1818
def asset_tag(source, options)
19-
tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => ERB::Util.html_escape(path_to_asset(source)) }.merge(options), false, false)
19+
# We force the :request protocol here to avoid a double-download bug in IE7 and IE8
20+
tag("link", { "rel" => "stylesheet", "type" => Mime::CSS, "media" => "screen", "href" => ERB::Util.html_escape(path_to_asset(source, true, :request)) }.merge(options), false, false)
2021
end
2122

2223
def custom_dir
@@ -52,19 +53,15 @@ def register_stylesheet_expansion(expansions)
5253
# If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
5354
# Full paths from the document root will be passed through.
5455
# Used internally by +stylesheet_link_tag+ to build the stylesheet path.
55-
#
56+
#
5657
# ==== Examples
5758
# stylesheet_path "style" # => /stylesheets/style.css
5859
# stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css
5960
# stylesheet_path "/dir/style.css" # => /dir/style.css
6061
# stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style
6162
# stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css
6263
def stylesheet_path(source)
63-
if config.use_sprockets
64-
asset_path(source, 'css')
65-
else
66-
asset_paths.compute_public_path(source, 'stylesheets', 'css')
67-
end
64+
asset_paths.compute_public_path(source, 'stylesheets', 'css', true, :request)
6865
end
6966
alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
7067

actionpack/lib/sprockets/helpers/rails_helper.rb

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,12 @@ def asset_paths
1010
@asset_paths ||= begin
1111
config = self.config if respond_to?(:config)
1212
controller = self.controller if respond_to?(:controller)
13+
config ||= Rails.application.config
14+
if config.action_controller.present?
15+
config.action_controller.default_asset_host_protocol ||= :relative
16+
else
17+
config.default_asset_host_protocol ||= :relative
18+
end
1319
RailsHelper::AssetPaths.new(config, controller)
1420
end
1521
end
@@ -50,29 +56,34 @@ def stylesheet_link_tag(*sources)
5056
'rel' => "stylesheet",
5157
'type' => "text/css",
5258
'media' => "screen",
53-
'href' => asset_path(source, 'css', body)
59+
'href' => asset_path(source, 'css', body, :request)
5460
}.merge(options.stringify_keys)
5561

5662
tag 'link', tag_options
5763
end
5864
end.join("\n").html_safe
5965
end
6066

67+
def asset_path(source, default_ext = nil, body = false, protocol = nil)
68+
source = source.logical_path if source.respond_to?(:logical_path)
69+
path = asset_paths.compute_public_path(source, 'assets', default_ext, true, protocol)
70+
body ? "#{path}?body=1" : path
71+
end
72+
6173
private
6274
def debug_assets?
6375
params[:debug_assets] == '1' ||
6476
params[:debug_assets] == 'true'
6577
end
6678

67-
def asset_path(source, default_ext = nil, body = false)
68-
source = source.logical_path if source.respond_to?(:logical_path)
69-
path = asset_paths.compute_public_path(source, Rails.application.config.assets.prefix, default_ext, true)
70-
body ? "#{path}?body=1" : path
71-
end
72-
7379
class AssetPaths < ::ActionView::AssetPaths #:nodoc:
74-
def compute_public_path(source, dir, ext=nil, include_host=true)
75-
super(source, Rails.application.config.assets.prefix, ext, include_host)
80+
def compute_public_path(source, dir, ext=nil, include_host=true, protocol = nil)
81+
super(source, Rails.application.config.assets.prefix, ext, include_host, protocol)
82+
end
83+
84+
# Return the filesystem path for the source
85+
def compute_source_path(source, ext)
86+
asset_for(source, ext)
7687
end
7788

7889
def asset_for(source, ext)
@@ -104,7 +115,7 @@ def assets
104115

105116
# When included in Sprockets::Context, we need to ask the top-level config as the controller is not available
106117
def performing_caching?
107-
@config ? @config.perform_caching : Rails.application.config.action_controller.perform_caching
118+
config.action_controller.present? ? config.action_controller.perform_caching : config.perform_caching
108119
end
109120
end
110121
end

actionpack/test/template/asset_tag_helper_test.rb

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -862,7 +862,7 @@ def test_caching_javascript_include_tag_when_caching_off_and_missing_javascript_
862862

863863
def test_caching_stylesheet_link_tag_when_caching_on
864864
ENV["RAILS_ASSET_ID"] = ""
865-
@controller.config.asset_host = 'http://a0.example.com'
865+
@controller.config.asset_host = 'a0.example.com'
866866
config.perform_caching = true
867867

868868
assert_dom_equal(
@@ -972,7 +972,7 @@ def test_caching_stylesheet_link_tag_when_caching_off_and_missing_css_file
972972

973973
def test_caching_stylesheet_link_tag_when_caching_on_with_proc_asset_host
974974
ENV["RAILS_ASSET_ID"] = ""
975-
@controller.config.asset_host = Proc.new { |source| "http://a#{source.length}.example.com" }
975+
@controller.config.asset_host = Proc.new { |source| "a#{source.length}.example.com" }
976976
config.perform_caching = true
977977

978978
assert_equal '/stylesheets/styles.css'.length, 23
@@ -1099,13 +1099,23 @@ def test_should_ignore_relative_root_path_on_complete_url
10991099
end
11001100

11011101
def test_should_compute_proper_path_with_asset_host
1102-
@controller.config.asset_host = "http://assets.example.com"
1102+
@controller.config.asset_host = "assets.example.com"
11031103
assert_dom_equal(%(<link href="http://www.example.com/collaboration/hieraki" rel="alternate" title="RSS" type="application/rss+xml" />), auto_discovery_link_tag)
1104-
assert_dom_equal(%(http://assets.example.com/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr"))
1105-
assert_dom_equal(%(http://assets.example.com/collaboration/hieraki/stylesheets/style.css), stylesheet_path("style"))
1106-
assert_dom_equal(%(http://assets.example.com/collaboration/hieraki/images/xml.png), image_path("xml.png"))
1107-
assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='http://assets.example.com/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='http://assets.example.com/collaboration/hieraki/images/mouse.png'" src="http://assets.example.com/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
1108-
assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='http://assets.example.com/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='http://assets.example.com/collaboration/hieraki/images/mouse2.png'" src="http://assets.example.com/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
1104+
assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr"))
1105+
assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/stylesheets/style.css), stylesheet_path("style"))
1106+
assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/images/xml.png), image_path("xml.png"))
1107+
assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse.png'" src="gopher://assets.example.com/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
1108+
assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse2.png'" src="gopher://assets.example.com/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
1109+
end
1110+
1111+
def test_should_compute_proper_path_with_asset_host_and_default_protocol
1112+
@controller.config.asset_host = "assets.example.com"
1113+
@controller.config.default_asset_host_protocol = :request
1114+
assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/javascripts/xmlhr.js), javascript_path("xmlhr"))
1115+
assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/stylesheets/style.css), stylesheet_path("style"))
1116+
assert_dom_equal(%(gopher://assets.example.com/collaboration/hieraki/images/xml.png), image_path("xml.png"))
1117+
assert_dom_equal(%(<img alt="Mouse" onmouseover="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse_over.png'" onmouseout="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse.png'" src="gopher://assets.example.com/collaboration/hieraki/images/mouse.png" />), image_tag("mouse.png", :mouseover => "/images/mouse_over.png"))
1118+
assert_dom_equal(%(<img alt="Mouse2" onmouseover="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse_over2.png'" onmouseout="this.src='gopher://assets.example.com/collaboration/hieraki/images/mouse2.png'" src="gopher://assets.example.com/collaboration/hieraki/images/mouse2.png" />), image_tag("mouse2.png", :mouseover => image_path("mouse_over2.png")))
11091119
end
11101120

11111121
def test_should_ignore_asset_host_on_complete_url
@@ -1123,12 +1133,12 @@ def test_should_wildcard_asset_host_between_zero_and_four
11231133
assert_match(%r(http://a[0123].example.com/collaboration/hieraki/images/xml.png), image_path('xml.png'))
11241134
end
11251135

1126-
def test_asset_host_without_protocol_should_use_request_protocol
1136+
def test_asset_host_without_protocol_should_be_protocol_relative
11271137
@controller.config.asset_host = 'a.example.com'
11281138
assert_equal 'gopher://a.example.com/collaboration/hieraki/images/xml.png', image_path('xml.png')
11291139
end
11301140

1131-
def test_asset_host_without_protocol_should_use_request_protocol_even_if_path_present
1141+
def test_asset_host_without_protocol_should_be_protocol_relative_even_if_path_present
11321142
@controller.config.asset_host = 'a.example.com/files/go/here'
11331143
assert_equal 'gopher://a.example.com/files/go/here/collaboration/hieraki/images/xml.png', image_path('xml.png')
11341144
end

0 commit comments

Comments
 (0)