aboutsummaryrefslogtreecommitdiffstats
path: root/actionpack/lib/action_view/helpers
diff options
context:
space:
mode:
Diffstat (limited to 'actionpack/lib/action_view/helpers')
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helper.rb358
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb145
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb93
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb195
-rw-r--r--actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb151
-rw-r--r--actionpack/lib/action_view/helpers/asset_url_helper.rb355
6 files changed, 435 insertions, 862 deletions
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helper.rb b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
index 5b5fc84e90..4eac6514df 100644
--- a/actionpack/lib/action_view/helpers/asset_tag_helper.rb
+++ b/actionpack/lib/action_view/helpers/asset_tag_helper.rb
@@ -1,8 +1,6 @@
require 'active_support/core_ext/array/extract_options'
require 'active_support/core_ext/hash/keys'
-require 'action_view/helpers/asset_tag_helpers/javascript_tag_helpers'
-require 'action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers'
-require 'action_view/helpers/asset_tag_helpers/asset_paths'
+require 'action_view/helpers/asset_url_helper'
require 'action_view/helpers/tag_helper'
module ActionView
@@ -17,187 +15,87 @@ module ActionView
# stylesheet_link_tag("application")
# # => <link href="/assets/application.css?body=1" media="screen" rel="stylesheet" />
#
- #
- # === Using asset hosts
- #
- # By default, Rails links to these assets on the current host in the public
- # folder, but you can direct Rails to link to assets from a dedicated asset
- # server by setting <tt>ActionController::Base.asset_host</tt> in the application
- # configuration, typically in <tt>config/environments/production.rb</tt>.
- # For example, you'd define <tt>assets.example.com</tt> to be your asset
- # host this way, inside the <tt>configure</tt> block of your environment-specific
- # configuration files or <tt>config/application.rb</tt>:
- #
- # config.action_controller.asset_host = "assets.example.com"
- #
- # Helpers take that into account:
- #
- # image_tag("rails.png")
- # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
- # stylesheet_link_tag("application")
- # # => <link href="http://assets.example.com/assets/application.css" media="screen" rel="stylesheet" />
- #
- # Browsers typically open at most two simultaneous connections to a single
- # host, which means your assets often have to wait for other assets to finish
- # downloading. You can alleviate this by using a <tt>%d</tt> wildcard in the
- # +asset_host+. For example, "assets%d.example.com". If that wildcard is
- # present Rails distributes asset requests among the corresponding four hosts
- # "assets0.example.com", ..., "assets3.example.com". With this trick browsers
- # will open eight simultaneous connections rather than two.
- #
- # image_tag("rails.png")
- # # => <img alt="Rails" src="http://assets0.example.com/assets/rails.png" />
- # stylesheet_link_tag("application")
- # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
- #
- # To do this, you can either setup four actual hosts, or you can use wildcard
- # DNS to CNAME the wildcard to a single asset host. You can read more about
- # setting up your DNS CNAME records from your ISP.
- #
- # Note: This is purely a browser performance optimization and is not meant
- # for server load balancing. See http://www.die.net/musings/page_load_time/
- # for background.
- #
- # Alternatively, you can exert more control over the asset host by setting
- # +asset_host+ to a proc like this:
- #
- # ActionController::Base.asset_host = Proc.new { |source|
- # "http://assets#{Digest::MD5.hexdigest(source).to_i(16) % 2 + 1}.example.com"
- # }
- # image_tag("rails.png")
- # # => <img alt="Rails" src="http://assets1.example.com/assets/rails.png" />
- # stylesheet_link_tag("application")
- # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
- #
- # The example above generates "http://assets1.example.com" and
- # "http://assets2.example.com". This option is useful for example if
- # you need fewer/more than four hosts, custom host names, etc.
- #
- # As you see the proc takes a +source+ parameter. That's a string with the
- # absolute path of the asset, for example "/assets/rails.png".
- #
- # ActionController::Base.asset_host = Proc.new { |source|
- # if source.ends_with?('.css')
- # "http://stylesheets.example.com"
- # else
- # "http://assets.example.com"
- # end
- # }
- # image_tag("rails.png")
- # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
- # stylesheet_link_tag("application")
- # # => <link href="http://stylesheets.example.com/assets/application.css" media="screen" rel="stylesheet" />
- #
- # Alternatively you may ask for a second parameter +request+. That one is
- # particularly useful for serving assets from an SSL-protected page. The
- # example proc below disables asset hosting for HTTPS connections, while
- # still sending assets for plain HTTP requests from asset hosts. If you don't
- # have SSL certificates for each of the asset hosts this technique allows you
- # to avoid warnings in the client about mixed media.
- #
- # config.action_controller.asset_host = Proc.new { |source, request|
- # if request.ssl?
- # "#{request.protocol}#{request.host_with_port}"
- # else
- # "#{request.protocol}assets.example.com"
- # end
- # }
- #
- # You can also implement a custom asset host object that responds to +call+
- # and takes either one or two parameters just like the proc.
- #
- # config.action_controller.asset_host = AssetHostingWithMinimumSsl.new(
- # "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 AssetTagHelper
+ extend ActiveSupport::Concern
+
+ include AssetUrlHelper
include TagHelper
- include JavascriptTagHelpers
- include StylesheetTagHelpers
+
+ # Returns an HTML script tag for each of the +sources+ provided.
+ #
+ # Sources may be paths to JavaScript files. Relative paths are assumed to be relative
+ # to <tt>public/javascripts</tt>, full paths are assumed to be relative to the document
+ # root. Relative paths are idiomatic, use absolute paths only when needed.
+ #
+ # When passing paths, the ".js" extension is optional.
+ #
+ # You can modify the HTML attributes of the script tag by passing a hash as the
+ # last argument.
+ #
+ # javascript_include_tag "xmlhr"
+ # # => <script src="/javascripts/xmlhr.js?1284139606"></script>
+ #
+ # javascript_include_tag "xmlhr.js"
+ # # => <script src="/javascripts/xmlhr.js?1284139606"></script>
+ #
+ # javascript_include_tag "common.javascript", "/elsewhere/cools"
+ # # => <script src="/javascripts/common.javascript?1284139606"></script>
+ # # <script src="/elsewhere/cools.js?1423139606"></script>
+ #
+ # javascript_include_tag "http://www.example.com/xmlhr"
+ # # => <script src="http://www.example.com/xmlhr"></script>
+ #
+ # javascript_include_tag "http://www.example.com/xmlhr.js"
+ # # => <script src="http://www.example.com/xmlhr.js"></script>
+ #
+ def javascript_include_tag(*sources)
+ options = sources.extract_options!.stringify_keys
+ sources.uniq.map { |source|
+ tag_options = {
+ "src" => path_to_javascript(source)
+ }.merge(options)
+ content_tag(:script, "", tag_options)
+ }.join("\n").html_safe
+ end
+
+ # Returns a stylesheet link tag for the sources specified as arguments. If
+ # you don't specify an extension, <tt>.css</tt> will be appended automatically.
+ # You can modify the link attributes by passing a hash as the last argument.
+ # For historical reasons, the 'media' attribute will always be present and defaults
+ # to "screen", so you must explicitely set it to "all" for the stylesheet(s) to
+ # apply to all media types.
+ #
+ # stylesheet_link_tag "style" # =>
+ # <link href="/stylesheets/style.css" media="screen" rel="stylesheet" />
+ #
+ # stylesheet_link_tag "style.css" # =>
+ # <link href="/stylesheets/style.css" media="screen" rel="stylesheet" />
+ #
+ # stylesheet_link_tag "http://www.example.com/style.css" # =>
+ # <link href="http://www.example.com/style.css" media="screen" rel="stylesheet" />
+ #
+ # stylesheet_link_tag "style", :media => "all" # =>
+ # <link href="/stylesheets/style.css" media="all" rel="stylesheet" />
+ #
+ # stylesheet_link_tag "style", :media => "print" # =>
+ # <link href="/stylesheets/style.css" media="print" rel="stylesheet" />
+ #
+ # stylesheet_link_tag "random.styles", "/css/stylish" # =>
+ # <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" />
+ # <link href="/css/stylish.css" media="screen" rel="stylesheet" />
+ #
+ def stylesheet_link_tag(*sources)
+ options = sources.extract_options!.stringify_keys
+ sources.uniq.map { |source|
+ tag_options = {
+ "rel" => "stylesheet",
+ "media" => "screen",
+ "href" => path_to_stylesheet(source)
+ }.merge(options)
+ tag(:link, tag_options)
+ }.join("\n").html_safe
+ end
+
# Returns a link tag that browsers and news readers can use to auto-detect
# an RSS or Atom feed. The +type+ can either be <tt>:rss</tt> (default) or
# <tt>:atom</tt>. Control the link options in url_for format using the
@@ -268,93 +166,6 @@ module ActionView
}.merge(options.symbolize_keys))
end
- # Computes the path to an image asset.
- # Full paths from the document root will be passed through.
- # Used internally by +image_tag+ to build the image path:
- #
- # image_path("edit") # => "/assets/edit"
- # image_path("edit.png") # => "/assets/edit.png"
- # image_path("icons/edit.png") # => "/assets/icons/edit.png"
- # image_path("/icons/edit.png") # => "/icons/edit.png"
- # image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png"
- #
- # If you have images as application resources this method may conflict with their named routes.
- # The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
- # plugin authors are encouraged to do so.
- def image_path(source)
- source.present? ? asset_paths.compute_public_path(source, 'images') : ""
- end
- alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
-
- # Computes the full URL to an image asset.
- # This will use +image_path+ internally, so most of their behaviors will be the same.
- def image_url(source)
- URI.join(current_host, path_to_image(source)).to_s
- end
- alias_method :url_to_image, :image_url # aliased to avoid conflicts with an image_url named route
-
- # Computes the path to a video asset in the public videos directory.
- # Full paths from the document root will be passed through.
- # Used internally by +video_tag+ to build the video path.
- #
- # video_path("hd") # => /videos/hd
- # video_path("hd.avi") # => /videos/hd.avi
- # video_path("trailers/hd.avi") # => /videos/trailers/hd.avi
- # video_path("/trailers/hd.avi") # => /trailers/hd.avi
- # video_path("http://www.example.com/vid/hd.avi") # => http://www.example.com/vid/hd.avi
- def video_path(source)
- asset_paths.compute_public_path(source, 'videos')
- end
- alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
-
- # Computes the full URL to a video asset in the public videos directory.
- # This will use +video_path+ internally, so most of their behaviors will be the same.
- def video_url(source)
- URI.join(current_host, path_to_video(source)).to_s
- end
- alias_method :url_to_video, :video_url # aliased to avoid conflicts with an video_url named route
-
- # Computes the path to an audio asset in the public audios directory.
- # Full paths from the document root will be passed through.
- # Used internally by +audio_tag+ to build the audio path.
- #
- # audio_path("horse") # => /audios/horse
- # audio_path("horse.wav") # => /audios/horse.wav
- # audio_path("sounds/horse.wav") # => /audios/sounds/horse.wav
- # audio_path("/sounds/horse.wav") # => /sounds/horse.wav
- # audio_path("http://www.example.com/sounds/horse.wav") # => http://www.example.com/sounds/horse.wav
- def audio_path(source)
- asset_paths.compute_public_path(source, 'audios')
- end
- alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
-
- # Computes the full URL to an audio asset in the public audios directory.
- # This will use +audio_path+ internally, so most of their behaviors will be the same.
- def audio_url(source)
- URI.join(current_host, path_to_audio(source)).to_s
- end
- alias_method :url_to_audio, :audio_url # aliased to avoid conflicts with an audio_url named route
-
- # Computes the path to a font asset.
- # Full paths from the document root will be passed through.
- #
- # font_path("font") # => /assets/font
- # font_path("font.ttf") # => /assets/font.ttf
- # font_path("dir/font.ttf") # => /assets/dir/font.ttf
- # font_path("/dir/font.ttf") # => /dir/font.ttf
- # font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf
- def font_path(source)
- asset_paths.compute_public_path(source, 'fonts')
- end
- alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route
-
- # Computes the full URL to a font asset.
- # This will use +font_path+ internally, so most of their behaviors will be the same.
- def font_url(source)
- URI.join(current_host, path_to_font(source)).to_s
- end
- alias_method :url_to_font, :font_url # aliased to avoid conflicts with an font_url named route
-
# Returns an html image tag for the +source+. The +source+ can be a full
# path or a file.
#
@@ -462,11 +273,6 @@ module ActionView
end
private
-
- def asset_paths
- @asset_paths ||= AssetTagHelper::AssetPaths.new(config, controller)
- end
-
def multiple_sources_tag(type, sources)
options = sources.extract_options!.symbolize_keys
sources.flatten!
@@ -482,10 +288,6 @@ module ActionView
content_tag(type, nil, options)
end
end
-
- def current_host
- url_for(:only_path => false)
- end
end
end
end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb
deleted file mode 100644
index e42e49fb04..0000000000
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_include_tag.rb
+++ /dev/null
@@ -1,145 +0,0 @@
-require 'active_support/core_ext/string/inflections'
-require 'active_support/core_ext/file'
-require 'action_view/helpers/tag_helper'
-
-module ActionView
- module Helpers
- module AssetTagHelper
-
- class AssetIncludeTag #:nodoc:
- include TagHelper
-
- attr_reader :config, :asset_paths
- class_attribute :expansions
-
- def self.inherited(base)
- base.expansions = { }
- end
-
- def initialize(config, asset_paths)
- @config = config
- @asset_paths = asset_paths
- end
-
- def asset_name
- raise NotImplementedError
- end
-
- def extension
- raise NotImplementedError
- end
-
- def custom_dir
- raise NotImplementedError
- end
-
- def asset_tag(source, options)
- raise NotImplementedError
- end
-
- def include_tag(*sources)
- options = sources.extract_options!.stringify_keys
- concat = options.delete("concat")
- cache = concat || options.delete("cache")
- recursive = options.delete("recursive")
-
- if concat || (config.perform_caching && cache)
- joined_name = (cache == true ? "all" : cache) + ".#{extension}"
- joined_path = File.join((joined_name[/^#{File::SEPARATOR}/] ? config.assets_dir : custom_dir), joined_name)
- unless config.perform_caching && File.exists?(joined_path)
- write_asset_file_contents(joined_path, compute_paths(sources, recursive))
- end
- asset_tag(joined_name, options)
- else
- sources = expand_sources(sources, recursive)
- ensure_sources!(sources) if cache
- sources.collect { |source| asset_tag(source, options) }.join("\n").html_safe
- end
- end
-
- private
-
- def path_to_asset(source, options = {})
- asset_paths.compute_public_path(source, asset_name.to_s.pluralize, options.merge(:ext => extension))
- end
-
- def path_to_asset_source(source)
- asset_paths.compute_source_path(source, asset_name.to_s.pluralize, extension)
- end
-
- def compute_paths(*args)
- expand_sources(*args).collect { |source| path_to_asset_source(source) }
- end
-
- def expand_sources(sources, recursive)
- if sources.first == :all
- collect_asset_files(custom_dir, ('**' if recursive), "*.#{extension}")
- else
- sources.inject([]) do |list, source|
- determined_source = determine_source(source, expansions)
- update_source_list(list, determined_source)
- end
- end
- end
-
- def update_source_list(list, source)
- case source
- when String
- list.delete(source)
- list << source
- when Array
- updated_sources = source - list
- list.concat(updated_sources)
- end
- end
-
- def ensure_sources!(sources)
- sources.each do |source|
- asset_file_path!(path_to_asset_source(source))
- end
- end
-
- def collect_asset_files(*path)
- dir = path.first
-
- Dir[File.join(*path.compact)].collect do |file|
- file[-(file.size - dir.size - 1)..-1].sub(/\.\w+$/, '')
- end.sort
- end
-
- def determine_source(source, collection)
- case source
- when Symbol
- collection[source] || raise(ArgumentError, "No expansion found for #{source.inspect}")
- else
- source
- end
- end
-
- def join_asset_file_contents(paths)
- paths.collect { |path| File.read(asset_file_path!(path, true)) }.join("\n\n")
- end
-
- def write_asset_file_contents(joined_asset_path, asset_paths)
- FileUtils.mkdir_p(File.dirname(joined_asset_path))
- File.atomic_write(joined_asset_path) { |cache| cache.write(join_asset_file_contents(asset_paths)) }
-
- # Set mtime to the latest of the combined files to allow for
- # consistent ETag without a shared filesystem.
- mt = asset_paths.map { |p| File.mtime(asset_file_path!(p)) }.max
- File.utime(mt, mt, joined_asset_path)
- end
-
- def asset_file_path!(absolute_path, error_if_file_is_uri = false)
- if asset_paths.is_uri?(absolute_path)
- raise(Errno::ENOENT, "Asset file #{path} is uri and cannot be merged into single file") if error_if_file_is_uri
- else
- raise(Errno::ENOENT, "Asset file not found at '#{absolute_path}'" ) unless File.exist?(absolute_path)
- return absolute_path
- end
- end
- end
-
- end
- end
-end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
deleted file mode 100644
index 35f91cec18..0000000000
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/asset_paths.rb
+++ /dev/null
@@ -1,93 +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)
- source = "/#{dir}/#{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
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
deleted file mode 100644
index 139f4d19ab..0000000000
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/javascript_tag_helpers.rb
+++ /dev/null
@@ -1,195 +0,0 @@
-require 'active_support/core_ext/file'
-require 'action_view/helpers/asset_tag_helpers/asset_include_tag'
-
-module ActionView
- module Helpers
- module AssetTagHelper
-
- class JavascriptIncludeTag < AssetIncludeTag #:nodoc:
- def asset_name
- 'javascript'
- end
-
- def extension
- 'js'
- end
-
- def asset_tag(source, options)
- content_tag("script", "", { "src" => path_to_asset(source) }.merge(options))
- end
-
- def custom_dir
- config.javascripts_dir
- end
-
- private
-
- def expand_sources(sources, recursive = false)
- if sources.include?(:all)
- all_asset_files = (collect_asset_files(custom_dir, ('**' if recursive), "*.#{extension}") - ['application'])
- add_application_js(all_asset_files, sources)
- ((determine_source(:defaults, expansions).dup & all_asset_files) + all_asset_files).uniq
- else
- expanded_sources = sources.inject([]) do |list, source|
- determined_source = determine_source(source, expansions)
- update_source_list(list, determined_source)
- end
- add_application_js(expanded_sources, sources)
- expanded_sources
- end
- end
-
- def add_application_js(expanded_sources, sources)
- if (sources.include?(:defaults) || sources.include?(:all)) && File.exist?(File.join(custom_dir, "application.#{extension}"))
- expanded_sources.delete('application')
- expanded_sources << "application"
- end
- end
- end
-
-
- module JavascriptTagHelpers
- extend ActiveSupport::Concern
-
- module ClassMethods
- # Register one or more javascript files to be included when <tt>symbol</tt>
- # is passed to <tt>javascript_include_tag</tt>. This method is typically intended
- # to be called from plugin initialization to register javascript files
- # that the plugin installed in <tt>public/javascripts</tt>.
- #
- # ActionView::Helpers::AssetTagHelper.register_javascript_expansion :monkey => ["head", "body", "tail"]
- #
- # javascript_include_tag :monkey # =>
- # <script src="/javascripts/head.js"></script>
- # <script src="/javascripts/body.js"></script>
- # <script src="/javascripts/tail.js"></script>
- def register_javascript_expansion(expansions)
- js_expansions = JavascriptIncludeTag.expansions
- expansions.each do |key, values|
- js_expansions[key] = (js_expansions[key] || []) | Array(values)
- 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.
- # Used internally by javascript_include_tag to build the script path.
- #
- # javascript_path "xmlhr" # => /javascripts/xmlhr.js
- # javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js
- # javascript_path "/dir/xmlhr" # => /dir/xmlhr.js
- # javascript_path "http://www.example.com/js/xmlhr" # => http://www.example.com/js/xmlhr
- # javascript_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
- def javascript_path(source)
- asset_paths.compute_public_path(source, 'javascripts', :ext => 'js')
- end
- alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
-
- # Computes the full URL to a javascript asset in the public javascripts directory.
- # This will use +javascript_path+ internally, so most of their behaviors will be the same.
- def javascript_url(source)
- URI.join(current_host, path_to_javascript(source)).to_s
- end
- alias_method :url_to_javascript, :javascript_url # aliased to avoid conflicts with a javascript_url named route
-
- # Returns an HTML script tag for each of the +sources+ provided.
- #
- # Sources may be paths to JavaScript files. Relative paths are assumed to be relative
- # to <tt>public/javascripts</tt>, full paths are assumed to be relative to the document
- # root. Relative paths are idiomatic, use absolute paths only when needed.
- #
- # When passing paths, the ".js" extension is optional.
- #
- # If the application is not using the asset pipeline, to include the default JavaScript
- # expansion pass <tt>:defaults</tt> as source. By default, <tt>:defaults</tt> loads jQuery,
- # and that can be overridden in <tt>config/application.rb</tt>:
- #
- # config.action_view.javascript_expansions[:defaults] = %w(foo.js bar.js)
- #
- # When using <tt>:defaults</tt> or <tt>:all</tt>, if an <tt>application.js</tt> file exists
- # in <tt>public/javascripts</tt> it will be included as well at the end.
- #
- # You can modify the HTML attributes of the script tag by passing a hash as the
- # last argument.
- #
- # javascript_include_tag "xmlhr"
- # # => <script src="/javascripts/xmlhr.js?1284139606"></script>
- #
- # javascript_include_tag "xmlhr.js"
- # # => <script src="/javascripts/xmlhr.js?1284139606"></script>
- #
- # javascript_include_tag "common.javascript", "/elsewhere/cools"
- # # => <script src="/javascripts/common.javascript?1284139606"></script>
- # # <script src="/elsewhere/cools.js?1423139606"></script>
- #
- # javascript_include_tag "http://www.example.com/xmlhr"
- # # => <script src="http://www.example.com/xmlhr"></script>
- #
- # javascript_include_tag "http://www.example.com/xmlhr.js"
- # # => <script src="http://www.example.com/xmlhr.js"></script>
- #
- # javascript_include_tag :defaults
- # # => <script src="/javascripts/jquery.js?1284139606"></script>
- # # <script src="/javascripts/rails.js?1284139606"></script>
- # # <script src="/javascripts/application.js?1284139606"></script>
- #
- # Note: The application.js file is only referenced if it exists
- #
- # You can also include all JavaScripts in the +javascripts+ directory using <tt>:all</tt> as the source:
- #
- # javascript_include_tag :all
- # # => <script src="/javascripts/jquery.js?1284139606"></script>
- # # <script src="/javascripts/rails.js?1284139606"></script>
- # # <script src="/javascripts/shop.js?1284139606"></script>
- # # <script src="/javascripts/checkout.js?1284139606"></script>
- # # <script src="/javascripts/application.js?1284139606"></script>
- #
- # Note that your defaults of choice will be included first, so they will be available to all subsequently
- # included files.
- #
- # If you want Rails to search in all the subdirectories under <tt>public/javascripts</tt>, you should
- # explicitly set <tt>:recursive</tt>:
- #
- # javascript_include_tag :all, :recursive => true
- #
- # == Caching multiple JavaScripts into one
- #
- # You can also cache multiple JavaScripts into one file, which requires less HTTP connections to download
- # and can better be compressed by gzip (leading to faster transfers). Caching will only happen if
- # <tt>config.perform_caching</tt> is set to true (which is the case by default for the Rails
- # production environment, but not for the development environment).
- #
- # # assuming config.perform_caching is false
- # javascript_include_tag :all, :cache => true
- # # => <script src="/javascripts/jquery.js?1284139606"></script>
- # # <script src="/javascripts/rails.js?1284139606"></script>
- # # <script src="/javascripts/shop.js?1284139606"></script>
- # # <script src="/javascripts/checkout.js?1284139606"></script>
- # # <script src="/javascripts/application.js?1284139606"></script>
- #
- # # assuming config.perform_caching is true
- # javascript_include_tag :all, :cache => true
- # # => <script src="/javascripts/all.js?1344139789"></script>
- #
- # # assuming config.perform_caching is false
- # javascript_include_tag "jquery", "cart", "checkout", :cache => "shop"
- # # => <script src="/javascripts/jquery.js?1284139606"></script>
- # # <script src="/javascripts/cart.js?1289139157"></script>
- # # <script src="/javascripts/checkout.js?1299139816"></script>
- #
- # # assuming config.perform_caching is true
- # javascript_include_tag "jquery", "cart", "checkout", :cache => "shop"
- # # => <script src="/javascripts/shop.js?1299139816"></script>
- #
- # The <tt>:recursive</tt> option is also available for caching:
- #
- # javascript_include_tag :all, :cache => true, :recursive => true
- def javascript_include_tag(*sources)
- @javascript_include ||= JavascriptIncludeTag.new(config, asset_paths)
- @javascript_include.include_tag(*sources)
- end
- end
- end
- end
-end
diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
deleted file mode 100644
index e3a86a8889..0000000000
--- a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb
+++ /dev/null
@@ -1,151 +0,0 @@
-require 'active_support/core_ext/file'
-require 'action_view/helpers/asset_tag_helpers/asset_include_tag'
-
-module ActionView
- module Helpers
- module AssetTagHelper
-
- class StylesheetIncludeTag < AssetIncludeTag #:nodoc:
- def asset_name
- 'stylesheet'
- end
-
- def extension
- 'css'
- end
-
- def asset_tag(source, options)
- # We force the :request protocol here to avoid a double-download bug in IE7 and IE8
- tag("link", { "rel" => "stylesheet", "media" => "screen", "href" => path_to_asset(source, :protocol => :request) }.merge(options))
- end
-
- def custom_dir
- config.stylesheets_dir
- end
- end
-
-
- module StylesheetTagHelpers
- extend ActiveSupport::Concern
-
- module ClassMethods
- # Register one or more stylesheet files to be included when <tt>symbol</tt>
- # is passed to <tt>stylesheet_link_tag</tt>. This method is typically intended
- # to be called from plugin initialization to register stylesheet files
- # that the plugin installed in <tt>public/stylesheets</tt>.
- #
- # ActionView::Helpers::AssetTagHelper.register_stylesheet_expansion :monkey => ["head", "body", "tail"]
- #
- # stylesheet_link_tag :monkey # =>
- # <link href="/stylesheets/head.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/body.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/tail.css" media="screen" rel="stylesheet" />
- def register_stylesheet_expansion(expansions)
- style_expansions = StylesheetIncludeTag.expansions
- expansions.each do |key, values|
- style_expansions[key] = (style_expansions[key] || []) | Array(values)
- end
- end
- end
-
- # Computes the path to a stylesheet asset in the public stylesheets directory.
- # If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
- # Full paths from the document root will be passed through.
- # Used internally by +stylesheet_link_tag+ to build the stylesheet path.
- #
- # stylesheet_path "style" # => /stylesheets/style.css
- # stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css
- # stylesheet_path "/dir/style.css" # => /dir/style.css
- # stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style
- # stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css
- def stylesheet_path(source)
- asset_paths.compute_public_path(source, 'stylesheets', :ext => 'css', :protocol => :request)
- end
- alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
-
- # Computes the full URL to a stylesheet asset in the public stylesheets directory.
- # This will use +stylesheet_path+ internally, so most of their behaviors will be the same.
- def stylesheet_url(source)
- URI.join(current_host, path_to_stylesheet(source)).to_s
- end
- alias_method :url_to_stylesheet, :stylesheet_url # aliased to avoid conflicts with a stylesheet_url named route
-
- # Returns a stylesheet link tag for the sources specified as arguments. If
- # you don't specify an extension, <tt>.css</tt> will be appended automatically.
- # You can modify the link attributes by passing a hash as the last argument.
- # For historical reasons, the 'media' attribute will always be present and defaults
- # to "screen", so you must explicitely set it to "all" for the stylesheet(s) to
- # apply to all media types.
- #
- # stylesheet_link_tag "style" # =>
- # <link href="/stylesheets/style.css" media="screen" rel="stylesheet" />
- #
- # stylesheet_link_tag "style.css" # =>
- # <link href="/stylesheets/style.css" media="screen" rel="stylesheet" />
- #
- # stylesheet_link_tag "http://www.example.com/style.css" # =>
- # <link href="http://www.example.com/style.css" media="screen" rel="stylesheet" />
- #
- # stylesheet_link_tag "style", :media => "all" # =>
- # <link href="/stylesheets/style.css" media="all" rel="stylesheet" />
- #
- # stylesheet_link_tag "style", :media => "print" # =>
- # <link href="/stylesheets/style.css" media="print" rel="stylesheet" />
- #
- # stylesheet_link_tag "random.styles", "/css/stylish" # =>
- # <link href="/stylesheets/random.styles" media="screen" rel="stylesheet" />
- # <link href="/css/stylish.css" media="screen" rel="stylesheet" />
- #
- # You can also include all styles in the stylesheets directory using <tt>:all</tt> as the source:
- #
- # stylesheet_link_tag :all # =>
- # <link href="/stylesheets/style1.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/styleB.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" />
- #
- # If you want Rails to search in all the subdirectories under stylesheets, you should explicitly set <tt>:recursive</tt>:
- #
- # stylesheet_link_tag :all, :recursive => true
- #
- # == Caching multiple stylesheets into one
- #
- # You can also cache multiple stylesheets into one file, which requires less HTTP connections and can better be
- # compressed by gzip (leading to faster transfers). Caching will only happen if +config.perform_caching+
- # is set to true (which is the case by default for the Rails production environment, but not for the development
- # environment). Examples:
- #
- # stylesheet_link_tag :all, :cache => true # when config.perform_caching is false =>
- # <link href="/stylesheets/style1.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/styleB.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/styleX2.css" media="screen" rel="stylesheet" />
- #
- # stylesheet_link_tag :all, :cache => true # when config.perform_caching is true =>
- # <link href="/stylesheets/all.css" media="screen" rel="stylesheet" />
- #
- # stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when config.perform_caching is false =>
- # <link href="/stylesheets/shop.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/cart.css" media="screen" rel="stylesheet" />
- # <link href="/stylesheets/checkout.css" media="screen" rel="stylesheet" />
- #
- # stylesheet_link_tag "shop", "cart", "checkout", :cache => "payment" # when config.perform_caching is true =>
- # <link href="/stylesheets/payment.css" media="screen" rel="stylesheet" />
- #
- # The <tt>:recursive</tt> option is also available for caching:
- #
- # stylesheet_link_tag :all, :cache => true, :recursive => true
- #
- # To force concatenation (even in development mode) set <tt>:concat</tt> to true. This is useful if
- # you have too many stylesheets for IE to load.
- #
- # stylesheet_link_tag :all, :concat => true
- #
- def stylesheet_link_tag(*sources)
- @stylesheet_include ||= StylesheetIncludeTag.new(config, asset_paths)
- @stylesheet_include.include_tag(*sources)
- end
-
- end
-
- end
- end
-end
diff --git a/actionpack/lib/action_view/helpers/asset_url_helper.rb b/actionpack/lib/action_view/helpers/asset_url_helper.rb
new file mode 100644
index 0000000000..0bb5e739bb
--- /dev/null
+++ b/actionpack/lib/action_view/helpers/asset_url_helper.rb
@@ -0,0 +1,355 @@
+require 'zlib'
+
+module ActionView
+ # = Action View Asset URL Helpers
+ module Helpers #:nodoc:
+ # This module provides methods for generating asset paths and
+ # urls.
+ #
+ # image_path("rails.png")
+ # # => "/assets/rails.png"
+ #
+ # image_url("rails.png")
+ # # => "http://www.example.com/assets/rails.png"
+ #
+ # === Using asset hosts
+ #
+ # By default, Rails links to these assets on the current host in the public
+ # folder, but you can direct Rails to link to assets from a dedicated asset
+ # server by setting <tt>ActionController::Base.asset_host</tt> in the application
+ # configuration, typically in <tt>config/environments/production.rb</tt>.
+ # For example, you'd define <tt>assets.example.com</tt> to be your asset
+ # host this way, inside the <tt>configure</tt> block of your environment-specific
+ # configuration files or <tt>config/application.rb</tt>:
+ #
+ # config.action_controller.asset_host = "assets.example.com"
+ #
+ # Helpers take that into account:
+ #
+ # image_tag("rails.png")
+ # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
+ # stylesheet_link_tag("application")
+ # # => <link href="http://assets.example.com/assets/application.css" media="screen" rel="stylesheet" />
+ #
+ # Browsers typically open at most two simultaneous connections to a single
+ # host, which means your assets often have to wait for other assets to finish
+ # downloading. You can alleviate this by using a <tt>%d</tt> wildcard in the
+ # +asset_host+. For example, "assets%d.example.com". If that wildcard is
+ # present Rails distributes asset requests among the corresponding four hosts
+ # "assets0.example.com", ..., "assets3.example.com". With this trick browsers
+ # will open eight simultaneous connections rather than two.
+ #
+ # image_tag("rails.png")
+ # # => <img alt="Rails" src="http://assets0.example.com/assets/rails.png" />
+ # stylesheet_link_tag("application")
+ # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
+ #
+ # To do this, you can either setup four actual hosts, or you can use wildcard
+ # DNS to CNAME the wildcard to a single asset host. You can read more about
+ # setting up your DNS CNAME records from your ISP.
+ #
+ # Note: This is purely a browser performance optimization and is not meant
+ # for server load balancing. See http://www.die.net/musings/page_load_time/
+ # for background.
+ #
+ # Alternatively, you can exert more control over the asset host by setting
+ # +asset_host+ to a proc like this:
+ #
+ # ActionController::Base.asset_host = Proc.new { |source|
+ # "http://assets#{Digest::MD5.hexdigest(source).to_i(16) % 2 + 1}.example.com"
+ # }
+ # image_tag("rails.png")
+ # # => <img alt="Rails" src="http://assets1.example.com/assets/rails.png" />
+ # stylesheet_link_tag("application")
+ # # => <link href="http://assets2.example.com/assets/application.css" media="screen" rel="stylesheet" />
+ #
+ # The example above generates "http://assets1.example.com" and
+ # "http://assets2.example.com". This option is useful for example if
+ # you need fewer/more than four hosts, custom host names, etc.
+ #
+ # As you see the proc takes a +source+ parameter. That's a string with the
+ # absolute path of the asset, for example "/assets/rails.png".
+ #
+ # ActionController::Base.asset_host = Proc.new { |source|
+ # if source.ends_with?('.css')
+ # "http://stylesheets.example.com"
+ # else
+ # "http://assets.example.com"
+ # end
+ # }
+ # image_tag("rails.png")
+ # # => <img alt="Rails" src="http://assets.example.com/assets/rails.png" />
+ # stylesheet_link_tag("application")
+ # # => <link href="http://stylesheets.example.com/assets/application.css" media="screen" rel="stylesheet" />
+ #
+ # Alternatively you may ask for a second parameter +request+. That one is
+ # particularly useful for serving assets from an SSL-protected page. The
+ # example proc below disables asset hosting for HTTPS connections, while
+ # still sending assets for plain HTTP requests from asset hosts. If you don't
+ # have SSL certificates for each of the asset hosts this technique allows you
+ # to avoid warnings in the client about mixed media.
+ #
+ # config.action_controller.asset_host = Proc.new { |source, request|
+ # if request.ssl?
+ # "#{request.protocol}#{request.host_with_port}"
+ # else
+ # "#{request.protocol}assets.example.com"
+ # end
+ # }
+ #
+ # You can also implement a custom asset host object that responds to +call+
+ # and takes either one or two parameters just like the proc.
+ #
+ # config.action_controller.asset_host = AssetHostingWithMinimumSsl.new(
+ # "http://asset%d.example.com", "https://asset1.example.com"
+ # )
+ #
+ module AssetUrlHelper
+ URI_REGEXP = %r{^[-a-z]+://|^(?:cid|data):|^//}
+
+ # Computes the path to asset in public directory. If :type
+ # options is set, a file extension will be appended and scoped
+ # to the corresponding public directory.
+ #
+ # All other asset *_path helpers delegate through this method.
+ #
+ # asset_path "application.js" # => /application.js
+ # asset_path "application", type: :javascript # => /javascripts/application.js
+ # 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?
+ return source if source =~ URI_REGEXP
+
+ tail, source = source[/([\?#].+)$/], source.sub(/([\?#].+)$/, '')
+
+ if extname = compute_asset_extname(source, options)
+ source = "#{source}#{extname}"
+ end
+
+ if source[0] != ?/
+ source = compute_asset_path(source, options)
+ end
+
+ relative_url_root = (defined?(config.relative_url_root) && config.relative_url_root) ||
+ (respond_to?(:request) && request.try(:script_name))
+ if relative_url_root
+ 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}#{tail}"
+ end
+ alias_method :path_to_asset, :asset_path # aliased to avoid conflicts with a asset_path named route
+
+ # Computes the full URL to a asset in the public directory. This
+ # will use +asset_path+ internally, so most of their behaviors
+ # will be the same.
+ def asset_url(source, options = {})
+ 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
+
+ ASSET_EXTENSIONS = {
+ javascript: '.js',
+ stylesheet: '.css'
+ }
+
+ # Compute extname to append to asset path. Returns nil if
+ # nothing should be added.
+ def compute_asset_extname(source, options = {})
+ return if options[:extname] == false
+ extname = options[:extname] || ASSET_EXTENSIONS[options[:type]]
+ extname if extname && File.extname(source) != extname
+ end
+
+ # 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 = {})
+ request = self.request if respond_to?(:request)
+ host = config.asset_host if defined? 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.
+ # Used internally by javascript_include_tag to build the script path.
+ #
+ # javascript_path "xmlhr" # => /javascripts/xmlhr.js
+ # javascript_path "dir/xmlhr.js" # => /javascripts/dir/xmlhr.js
+ # javascript_path "/dir/xmlhr" # => /dir/xmlhr.js
+ # javascript_path "http://www.example.com/js/xmlhr" # => http://www.example.com/js/xmlhr
+ # javascript_path "http://www.example.com/js/xmlhr.js" # => http://www.example.com/js/xmlhr.js
+ def javascript_path(source, options = {})
+ path_to_asset(source, {type: :javascript}.merge!(options))
+ end
+ alias_method :path_to_javascript, :javascript_path # aliased to avoid conflicts with a javascript_path named route
+
+ # Computes the full URL to a javascript asset in the public javascripts directory.
+ # This will use +javascript_path+ internally, so most of their behaviors will be the same.
+ def javascript_url(source, options = {})
+ url_to_asset(source, {type: :javascript}.merge!(options))
+ end
+ alias_method :url_to_javascript, :javascript_url # aliased to avoid conflicts with a javascript_url named route
+
+ # Computes the path to a stylesheet asset in the public stylesheets directory.
+ # If the +source+ filename has no extension, <tt>.css</tt> will be appended (except for explicit URIs).
+ # Full paths from the document root will be passed through.
+ # Used internally by +stylesheet_link_tag+ to build the stylesheet path.
+ #
+ # stylesheet_path "style" # => /stylesheets/style.css
+ # stylesheet_path "dir/style.css" # => /stylesheets/dir/style.css
+ # stylesheet_path "/dir/style.css" # => /dir/style.css
+ # stylesheet_path "http://www.example.com/css/style" # => http://www.example.com/css/style
+ # stylesheet_path "http://www.example.com/css/style.css" # => http://www.example.com/css/style.css
+ def stylesheet_path(source, options = {})
+ path_to_asset(source, {type: :stylesheet}.merge!(options))
+ end
+ alias_method :path_to_stylesheet, :stylesheet_path # aliased to avoid conflicts with a stylesheet_path named route
+
+ # Computes the full URL to a stylesheet asset in the public stylesheets directory.
+ # This will use +stylesheet_path+ internally, so most of their behaviors will be the same.
+ def stylesheet_url(source, options = {})
+ url_to_asset(source, {type: :stylesheet}.merge!(options))
+ end
+ alias_method :url_to_stylesheet, :stylesheet_url # aliased to avoid conflicts with a stylesheet_url named route
+
+ # Computes the path to an image asset.
+ # Full paths from the document root will be passed through.
+ # Used internally by +image_tag+ to build the image path:
+ #
+ # image_path("edit") # => "/assets/edit"
+ # image_path("edit.png") # => "/assets/edit.png"
+ # image_path("icons/edit.png") # => "/assets/icons/edit.png"
+ # image_path("/icons/edit.png") # => "/icons/edit.png"
+ # image_path("http://www.example.com/img/edit.png") # => "http://www.example.com/img/edit.png"
+ #
+ # If you have images as application resources this method may conflict with their named routes.
+ # The alias +path_to_image+ is provided to avoid that. Rails uses the alias internally, and
+ # plugin authors are encouraged to do so.
+ def image_path(source, options = {})
+ path_to_asset(source, {type: :image}.merge!(options))
+ end
+ alias_method :path_to_image, :image_path # aliased to avoid conflicts with an image_path named route
+
+ # Computes the full URL to an image asset.
+ # This will use +image_path+ internally, so most of their behaviors will be the same.
+ def image_url(source, options = {})
+ url_to_asset(source, {type: :image}.merge!(options))
+ end
+ alias_method :url_to_image, :image_url # aliased to avoid conflicts with an image_url named route
+
+ # Computes the path to a video asset in the public videos directory.
+ # Full paths from the document root will be passed through.
+ # Used internally by +video_tag+ to build the video path.
+ #
+ # video_path("hd") # => /videos/hd
+ # video_path("hd.avi") # => /videos/hd.avi
+ # video_path("trailers/hd.avi") # => /videos/trailers/hd.avi
+ # video_path("/trailers/hd.avi") # => /trailers/hd.avi
+ # video_path("http://www.example.com/vid/hd.avi") # => http://www.example.com/vid/hd.avi
+ def video_path(source, options = {})
+ path_to_asset(source, {type: :video}.merge!(options))
+ end
+ alias_method :path_to_video, :video_path # aliased to avoid conflicts with a video_path named route
+
+ # Computes the full URL to a video asset in the public videos directory.
+ # This will use +video_path+ internally, so most of their behaviors will be the same.
+ def video_url(source, options = {})
+ url_to_asset(source, {type: :video}.merge!(options))
+ end
+ alias_method :url_to_video, :video_url # aliased to avoid conflicts with an video_url named route
+
+ # Computes the path to an audio asset in the public audios directory.
+ # Full paths from the document root will be passed through.
+ # Used internally by +audio_tag+ to build the audio path.
+ #
+ # audio_path("horse") # => /audios/horse
+ # audio_path("horse.wav") # => /audios/horse.wav
+ # audio_path("sounds/horse.wav") # => /audios/sounds/horse.wav
+ # audio_path("/sounds/horse.wav") # => /sounds/horse.wav
+ # audio_path("http://www.example.com/sounds/horse.wav") # => http://www.example.com/sounds/horse.wav
+ def audio_path(source, options = {})
+ path_to_asset(source, {type: :audio}.merge!(options))
+ end
+ alias_method :path_to_audio, :audio_path # aliased to avoid conflicts with an audio_path named route
+
+ # Computes the full URL to an audio asset in the public audios directory.
+ # This will use +audio_path+ internally, so most of their behaviors will be the same.
+ def audio_url(source, options = {})
+ url_to_asset(source, {type: :audio}.merge!(options))
+ end
+ alias_method :url_to_audio, :audio_url # aliased to avoid conflicts with an audio_url named route
+
+ # Computes the path to a font asset.
+ # Full paths from the document root will be passed through.
+ #
+ # font_path("font") # => /assets/font
+ # font_path("font.ttf") # => /assets/font.ttf
+ # font_path("dir/font.ttf") # => /assets/dir/font.ttf
+ # font_path("/dir/font.ttf") # => /dir/font.ttf
+ # font_path("http://www.example.com/dir/font.ttf") # => http://www.example.com/dir/font.ttf
+ def font_path(source, options = {})
+ path_to_asset(source, {type: :font}.merge!(options))
+ end
+ alias_method :path_to_font, :font_path # aliased to avoid conflicts with an font_path named route
+
+ # Computes the full URL to a font asset.
+ # This will use +font_path+ internally, so most of their behaviors will be the same.
+ def font_url(source, options = {})
+ url_to_asset(source, {type: :font}.merge!(options))
+ end
+ alias_method :url_to_font, :font_url # aliased to avoid conflicts with an font_url named route
+ end
+ end
+end