module ActionView::Helpers::AssetTagHelper
def audio_tag(*sources)
audio_tag("sound.wav", "sound.mid")
# =>
audio_tag("sound.wav", autoplay: true, controls: true)
# =>
audio_tag("sound.wav")
# =>
audio_tag("sound")
parameter.
When the last parameter is a hash you can add HTML attributes using that
directory.
+sources+ can be full paths or files that exist in your public audios
tag with nested source tags for each source will be returned. The
a single audio tag will be returned. If +sources+ is an array, an audio
Returns an HTML audio tag for the +sources+. If +sources+ is a string,
def audio_tag(*sources) multiple_sources_tag_builder("audio", sources) end
def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})
auto_discovery_link_tag(:rss, "http://www.example.com/feed.rss", {title: "Example RSS"})
# =>
auto_discovery_link_tag(:rss, {controller: "news", action: "feed"})
# =>
auto_discovery_link_tag(:rss, {action: "feed"}, {title: "My RSS"})
# =>
auto_discovery_link_tag(:rss, {action: "feed"})
# =>
auto_discovery_link_tag(:json)
# =>
auto_discovery_link_tag(:atom)
# =>
auto_discovery_link_tag
==== Examples
* :title - Specify the title of the link, defaults to the +type+
* :type - Override the auto-generated mime type
* :rel - Specify the relation of this link, defaults to "alternate"
==== Options
using the +url_options+. You can modify the LINK tag itself in +tag_options+.
:atom, or :json. Control the link options in url_for format
an RSS, Atom, or JSON feed. The +type+ can be :rss (default),
Returns a link tag that browsers and feed readers can use to auto-detect
def auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {}) if !(type == :rss || type == :atom || type == :json) && tag_options[:type].blank? raise ArgumentError.new("You should pass :type tag_option key explicitly, because you have passed #{type} type other than :rss, :atom, or :json.") end tag( "link", "rel" => tag_options[:rel] || "alternate", "type" => tag_options[:type] || Template::Types[type].to_s, "title" => tag_options[:title] || type.to_s.upcase, "href" => url_options.is_a?(Hash) ? url_for(url_options.merge(only_path: false)) : url_options ) end
def check_for_image_tag_errors(options)
def check_for_image_tag_errors(options) if options[:size] && (options[:height] || options[:width]) raise ArgumentError, "Cannot pass a :size option with a :height or :width option" end end
def extract_dimensions(size)
def extract_dimensions(size) size = size.to_s if /\A(\d+|\d+.\d+)x(\d+|\d+.\d+)\z/.match?(size) size.split("x") elsif /\A(\d+|\d+.\d+)\z/.match?(size) [size, size] end end
def favicon_link_tag(source = "favicon.ico", options = {})
favicon_link_tag 'mb-icon.png', rel: 'apple-touch-icon', type: 'image/png'
The following call would generate such a tag:
will be used if you add the page to the home screen of an iOS device.
Mobile Safari looks for a different link tag, pointing to an image that
# =>
favicon_link_tag 'myicon.ico'
# =>
favicon_link_tag
respectively:
to override their defaults, "icon" and "image/x-icon"
defaults to "favicon.ico", and also supports +:rel+ and +:type+ options
The helper gets the name of the favicon file as first argument, which
using this helper to generate its corresponding link tag.
their favicon storing the file under app/assets/images, and
To have better control applications may let the asset pipeline manage
request succeeds. If the favicon changes it is hard to get it updated.
ask for /favicon.ico automatically, and cache the file if the
If a page has no link like the one generated by this helper, browsers
Returns a link tag for a favicon managed by the asset pipeline.
def favicon_link_tag(source = "favicon.ico", options = {}) tag("link", { rel: "icon", type: "image/x-icon", href: path_to_image(source, skip_pipeline: options.delete(:skip_pipeline)) }.merge!(options.symbolize_keys)) end
def image_tag(source, options = {})
image_tag(user.avatar.variant(resize_to_limit: [100, 100]), size: '100')
# =>
image_tag(user.avatar.variant(resize_to_limit: [100, 100]))
# =>
image_tag(user.avatar)
Active Storage blobs (images that are uploaded by the users of your app):
# =>
image_tag("pic.jpg", srcset: [["pic_1024.jpg", "1024w"], ["pic_1980.jpg", "1980w"]], sizes: "100vw")
# =>
image_tag("icon.png", srcset: { "icon_2x.png" => "2x", "icon_4x.png" => "4x" })
# =>
image_tag("/icons/icon.gif", data: { title: 'Rails Application' })
# =>
image_tag("/icons/icon.gif", class: "menu_icon")
# =>
image_tag("/icons/icon.gif", height: '32', width: '32')
# =>
image_tag("/icons/icon.gif", size: "16")
# =>
image_tag("icon.png", size: "16x10", alt: "Edit Entry")
# =>
image_tag("icon.png")
# =>
image_tag("icon")
Assets (images that are part of your app):
==== Examples
pairs, each image path will be expanded before the list is formatted as a string.
* :srcset - If supplied as a hash or array of [source, descriptor]
:size will be ignored if the value is not in the correct format.
width="30" and height="45", and "50" becomes width="50" and height="50".
* :size - Supplied as "{Width}x{Height}" or "{Number}", so "30x45" becomes
additional keys for convenience and conformance:
You can add HTML attributes using the +options+. The +options+ supports
==== Options
path, a file, or an Active Storage attachment.
Returns an HTML image tag for the +source+. The +source+ can be a full
def image_tag(source, options = {}) options = options.symbolize_keys check_for_image_tag_errors(options) skip_pipeline = options.delete(:skip_pipeline) options[:src] = resolve_image_source(source, skip_pipeline) if options[:srcset] && !options[:srcset].is_a?(String) options[:srcset] = options[:srcset].map do |src_path, size| src_path = path_to_image(src_path, skip_pipeline: skip_pipeline) "#{src_path} #{size}" end.join(", ") end options[:width], options[:height] = extract_dimensions(options.delete(:size)) if options[:size] options[:loading] ||= image_loading if image_loading options[:decoding] ||= image_decoding if image_decoding tag("img", options) end
def javascript_include_tag(*sources)
javascript_include_tag "http://www.example.com/xmlhr.js", nonce: true
# =>
javascript_include_tag "http://www.example.com/xmlhr.js"
# =>
javascript_include_tag "http://www.example.com/xmlhr"
#
# =>
javascript_include_tag "common.javascript", "/elsewhere/cools"
# =>
javascript_include_tag "xmlhr.js"
# =>
javascript_include_tag "template.jst", extname: false
# =>
javascript_include_tag "xmlhr", host: "localhost", protocol: "https"
# =>
javascript_include_tag "xmlhr"
==== Examples
you have Content Security Policy enabled.
* :nonce - When set to true, adds an automatic nonce value if
when it is set to true.
* :skip_pipeline - This option is used to bypass the asset pipeline
that path.
* :host - When a relative URL is provided the host is added to the
applies when a relative URL and +host+ options are provided.
* :protocol - Sets the protocol of the generated URL. This option only
already exists. This only applies for relative URLs.
* :extname - Append an extension to the generated URL unless the extension
parameter. The following options are supported:
When the last parameter is a hash you can add HTML attributes using that
==== Options
automatically pushed.
If the server supports Early Hints, header links for these assets will be
source, and include other JavaScript or CoffeeScript files inside the manifest.
When the Asset Pipeline is enabled, you can pass the name of your manifest as
last argument.
You can modify the HTML attributes of the script tag by passing a hash as the
appended to the path extname: false can be set on the options.
When passing paths, the ".js" extension is optional. If you do not want ".js"
root. Relative paths are idiomatic, use absolute paths only when needed.
to assets/javascripts, full paths are assumed to be relative to the document
Sources may be paths to JavaScript files. Relative paths are assumed to be relative
Returns an HTML script tag for each of the +sources+ provided.
def javascript_include_tag(*sources) options = sources.extract_options!.stringify_keys path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys preload_links = [] nopush = options["nopush"].nil? ? true : options.delete("nopush") crossorigin = options.delete("crossorigin") crossorigin = "anonymous" if crossorigin == true integrity = options["integrity"] rel = options["type"] == "module" ? "modulepreload" : "preload" sources_tags = sources.uniq.map { |source| href = path_to_javascript(source, path_options) if preload_links_header && !options["defer"] && href.present? && !href.start_with?("data:") preload_link = "<#{href}>; rel=#{rel}; as=script" preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil? preload_link += "; integrity=#{integrity}" unless integrity.nil? preload_link += "; nopush" if nopush preload_links << preload_link end tag_options = { "src" => href, "crossorigin" => crossorigin }.merge!(options) if tag_options["nonce"] == true tag_options["nonce"] = content_security_policy_nonce end content_tag("script", "", tag_options) }.join("\n").html_safe if preload_links_header send_preload_links_header(preload_links) end sources_tags end
def multiple_sources_tag_builder(type, sources)
def multiple_sources_tag_builder(type, sources) options = sources.extract_options!.symbolize_keys skip_pipeline = options.delete(:skip_pipeline) sources.flatten! yield options if block_given? if sources.size > 1 content_tag(type, options) do safe_join sources.map { |source| tag("source", src: send("path_to_#{type}", source, skip_pipeline: skip_pipeline)) } end else options[:src] = send("path_to_#{type}", sources.first, skip_pipeline: skip_pipeline) content_tag(type, nil, options) end end
def preload_link_tag(source, options = {})
# =>
preload_link_tag("/media/audio.ogg", nopush: true)
# =>
preload_link_tag("//example.com/font.woff2", crossorigin: "use-credentials")
# =>
preload_link_tag("//example.com/font.woff2")
# =>
preload_link_tag("worker.js", as: "worker")
# =>
preload_link_tag(post_path(format: :json), as: "fetch")
# =>
preload_link_tag("/videos/video.webm")
# =>
preload_link_tag("custom_theme.css")
==== Examples
* :integrity - Specify the integrity attribute.
* :nopush - Specify if the use of server push is not desired for the resource. Defaults to +false+.
* :crossorigin - Specify the crossorigin attribute, required to load cross-origin resources.
* :as - Override the auto-generated value for as attribute, calculated using +source+ extension and mime type.
* :type - Override the auto-generated mime type, defaults to the mime type for +source+ extension.
==== Options
a full path, or an URI.
The +source+ can be the path of a resource managed by asset pipeline,
Returns a link tag that browsers can use to preload the +source+.
def preload_link_tag(source, options = {}) href = path_to_asset(source, skip_pipeline: options.delete(:skip_pipeline)) extname = File.extname(source).downcase.delete(".") mime_type = options.delete(:type) || Template::Types[extname]&.to_s as_type = options.delete(:as) || resolve_link_as(extname, mime_type) crossorigin = options.delete(:crossorigin) crossorigin = "anonymous" if crossorigin == true || (crossorigin.blank? && as_type == "font") integrity = options[:integrity] nopush = options.delete(:nopush) || false rel = mime_type == "module" ? "modulepreload" : "preload" link_tag = tag.link(**{ rel: rel, href: href, as: as_type, type: mime_type, crossorigin: crossorigin }.merge!(options.symbolize_keys)) preload_link = "<#{href}>; rel=#{rel}; as=#{as_type}" preload_link += "; type=#{mime_type}" if mime_type preload_link += "; crossorigin=#{crossorigin}" if crossorigin preload_link += "; integrity=#{integrity}" if integrity preload_link += "; nopush" if nopush send_preload_links_header([preload_link]) link_tag end
def resolve_image_source(source, skip_pipeline)
def resolve_image_source(source, skip_pipeline) if source.is_a?(Symbol) || source.is_a?(String) path_to_image(source, skip_pipeline: skip_pipeline) else polymorphic_url(source) end rescue NoMethodError => e raise ArgumentError, "Can't resolve image into URL: #{e}" end
def resolve_link_as(extname, mime_type)
def resolve_link_as(extname, mime_type) case extname when "js" then "script" when "css" then "style" when "vtt" then "track" else mime_type.to_s.split("/").first.presence_in(%w(audio video font image)) end end
def send_preload_links_header(preload_links, max_header_size: MAX_HEADER_SIZE)
def send_preload_links_header(preload_links, max_header_size: MAX_HEADER_SIZE) return if preload_links.empty? return if respond_to?(:response) && response&.sending? if respond_to?(:request) && request request.send_early_hints("Link" => preload_links.join("\n")) end if respond_to?(:response) && response header = response.headers["Link"] header = header ? header.dup : +"" # rindex count characters not bytes, but we assume non-ascii characters # are rare in urls, and we have a 192 bytes margin. last_line_offset = header.rindex("\n") last_line_size = if last_line_offset header.bytesize - last_line_offset else header.bytesize end preload_links.each do |link| if link.bytesize + last_line_size + 1 < max_header_size unless header.empty? header << "," last_line_size += 1 end else header << "\n" last_line_size = 0 end header << link last_line_size += link.bytesize end response.headers["Link"] = header end end
def stylesheet_link_tag(*sources)
# =>
stylesheet_link_tag "random.styles", "/css/stylish"
# =>
stylesheet_link_tag "style", media: "print"
# =>
stylesheet_link_tag "style", media: "all"
# =>
stylesheet_link_tag "style.less", extname: false, skip_pipeline: true, rel: "stylesheet/less"
# =>
stylesheet_link_tag "http://www.example.com/style.css"
# =>
stylesheet_link_tag "style.css"
# =>
stylesheet_link_tag "style"
==== Examples
when it is set to true.
* :skip_pipeline - This option is used to bypass the asset pipeline
that path.
* :host - When a relative URL is provided the host is added to the
applies when a relative URL and +host+ options are provided.
* :protocol - Sets the protocol of the generated URL. This option only
already exists. This only applies for relative URLs.
* :extname - Append an extension to the generated URL unless the extension
==== Options
automatically pushed.
If the server supports Early Hints, header links for these assets will be
You can modify the link attributes by passing a hash as the last argument.
set extname: false in the options.
If you do not want .css appended to the path,
If you don't specify an extension, .css will be appended automatically.
When passing paths, the .css extension is optional.
Returns a stylesheet link tag for the sources specified as arguments.
def stylesheet_link_tag(*sources) options = sources.extract_options!.stringify_keys path_options = options.extract!("protocol", "extname", "host", "skip_pipeline").symbolize_keys preload_links = [] crossorigin = options.delete("crossorigin") crossorigin = "anonymous" if crossorigin == true nopush = options["nopush"].nil? ? true : options.delete("nopush") integrity = options["integrity"] sources_tags = sources.uniq.map { |source| href = path_to_stylesheet(source, path_options) if preload_links_header && href.present? && !href.start_with?("data:") preload_link = "<#{href}>; rel=preload; as=style" preload_link += "; crossorigin=#{crossorigin}" unless crossorigin.nil? preload_link += "; integrity=#{integrity}" unless integrity.nil? preload_link += "; nopush" if nopush preload_links << preload_link end tag_options = { "rel" => "stylesheet", "crossorigin" => crossorigin, "href" => href }.merge!(options) if apply_stylesheet_media_default && tag_options["media"].blank? tag_options["media"] = "screen" end tag(:link, tag_options) }.join("\n").html_safe if preload_links_header send_preload_links_header(preload_links) end sources_tags end
def video_tag(*sources)
video_tag(["trailer.ogg", "trailer.flv"], size: "160x120")
# =>
video_tag(["trailer.ogg", "trailer.flv"])
# =>
video_tag("trailer.ogg", "trailer.flv")
# =>
video_tag("/trailers/hd.avi", height: '32', width: '32')
# =>
video_tag("/trailers/hd.avi", size: "16")
# =>
video_tag("/trailers/hd.avi", size: "16x16")
# =>
video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png", poster_skip_pipeline: true)
# =>
video_tag("trailer.m4v", size: "16x10", poster: "screenshot.png")
# =>
video_tag("trailer.ogg", controls: true, preload: 'none')
# =>
video_tag("trailer.ogg")
# =>
video_tag("trailer")
==== Examples
the :poster option instead using an asset in the public folder.
* :poster_skip_pipeline will bypass the asset pipeline when using
:size will be ignored if the value is not in the correct format.
width="30" and height="45", and "50" becomes width="50" and height="50".
* :size - Supplied as "{Width}x{Height}" or "{Number}", so "30x45" becomes
before the video loads. The path is calculated like the +src+ of +image_tag+.
* :poster - Set an image (like a screenshot) to be shown
parameter. The following options are supported:
When the last parameter is a hash you can add HTML attributes using that
==== Options
directory.
+sources+ can be full paths or files that exist in your public videos
tag with nested source tags for each source will be returned. The
a single video tag will be returned. If +sources+ is an array, a video
Returns an HTML video tag for the +sources+. If +sources+ is a string,
def video_tag(*sources) options = sources.extract_options!.symbolize_keys public_poster_folder = options.delete(:poster_skip_pipeline) sources << options multiple_sources_tag_builder("video", sources) do |tag_options| tag_options[:poster] = path_to_image(tag_options[:poster], skip_pipeline: public_poster_folder) if tag_options[:poster] tag_options[:width], tag_options[:height] = extract_dimensions(tag_options.delete(:size)) if tag_options[:size] end end