lib/hanami/assets/helpers.rb in hanami-assets-0.2.1 vs lib/hanami/assets/helpers.rb in hanami-assets-0.3.0
- old
+ new
@@ -11,11 +11,11 @@
# Include this helper in a view
#
# @since 0.1.0
#
# @see http://www.rubydoc.info/gems/hanami-helpers/Hanami/Helpers/HtmlHelper
- module Helpers
+ module Helpers # rubocop:disable Metrics/ModuleLength
# @since 0.1.0
# @api private
NEW_LINE_SEPARATOR = "\n".freeze
# @since 0.1.0
@@ -52,10 +52,18 @@
# @since 0.1.0
# @api private
DEFAULT_FAVICON = 'favicon.ico'.freeze
+ # @since 0.3.0
+ # @api private
+ CROSSORIGIN_ANONYMOUS = 'anonymous'.freeze
+
+ # @since 0.3.0
+ # @api private
+ ABSOLUTE_URL_MATCHER = URI::Parser.new.make_regexp
+
include Hanami::Helpers::HtmlHelper
# Inject helpers into the given class
#
# @since 0.1.0
@@ -106,10 +114,34 @@
# <%= javascript 'application', 'dashboard' %>
#
# # <script src="/assets/application.js" type="text/javascript"></script>
# # <script src="/assets/dashboard.js" type="text/javascript"></script>
#
+ # @example Asynchronous Execution
+ #
+ # <%= javascript 'application', async: true %>
+ #
+ # # <script src="/assets/application.js" type="text/javascript" async="async"></script>
+ #
+ # @example Subresource Integrity
+ #
+ # <%= javascript 'application' %>
+ #
+ # # <script src="/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js" type="text/javascript" integrity="sha384-oqVu...Y8wC" crossorigin="anonymous"></script>
+ #
+ # @example Subresource Integrity for 3rd Party Scripts
+ #
+ # <%= javascript 'https://example.com/assets/example.js', integrity: 'sha384-oqVu...Y8wC' %>
+ #
+ # # <script src="https://example.com/assets/example.js" type="text/javascript" integrity="sha384-oqVu...Y8wC" crossorigin="anonymous"></script>
+ #
+ # @example Deferred Execution
+ #
+ # <%= javascript 'application', defer: true %>
+ #
+ # # <script src="/assets/application.js" type="text/javascript" defer="defer"></script>
+ #
# @example Absolute URL
#
# <%= javascript 'https://code.jquery.com/jquery-2.1.4.min.js' %>
#
# # <script src="https://code.jquery.com/jquery-2.1.4.min.js" type="text/javascript"></script>
@@ -123,13 +155,22 @@
# @example CDN Mode
#
# <%= javascript 'application' %>
#
# # <script src="https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.js" type="text/javascript"></script>
- def javascript(*sources)
+ def javascript(*sources, **options)
_safe_tags(*sources) do |source|
- html.script(src: _typed_asset_path(source, JAVASCRIPT_EXT), type: JAVASCRIPT_MIME_TYPE).to_s
+ tag_options = options.dup
+ tag_options[:src] ||= _typed_asset_path(source, JAVASCRIPT_EXT)
+ tag_options[:type] ||= JAVASCRIPT_MIME_TYPE
+
+ if _subresource_integrity? || tag_options.include?(:integrity)
+ tag_options[:integrity] ||= _subresource_integrity_value(source, JAVASCRIPT_EXT)
+ tag_options[:crossorigin] ||= CROSSORIGIN_ANONYMOUS
+ end
+
+ html.script(**tag_options).to_s
end
end
# Generate <tt>link</tt> tag for given source(s)
#
@@ -167,10 +208,22 @@
# <%= stylesheet 'application', 'dashboard' %>
#
# # <link href="/assets/application.css" type="text/css" rel="stylesheet">
# # <link href="/assets/dashboard.css" type="text/css" rel="stylesheet">
#
+ # @example Subresource Integrity
+ #
+ # <%= stylesheet 'application' %>
+ #
+ # # <link href="/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.css" type="text/css" integrity="sha384-oqVu...Y8wC" crossorigin="anonymous"></script>
+ #
+ # @example Subresource Integrity for 3rd Party Assets
+ #
+ # <%= stylesheet 'https://example.com/assets/example.css', integrity: 'sha384-oqVu...Y8wC' %>
+ #
+ # # <link href="https://example.com/assets/example.css" type="text/css" rel="stylesheet" integrity="sha384-oqVu...Y8wC" crossorigin="anonymous"></script>
+ #
# @example Absolute URL
#
# <%= stylesheet 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css' %>
#
# # <link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css" type="text/css" rel="stylesheet">
@@ -184,13 +237,23 @@
# @example CDN Mode
#
# <%= stylesheet 'application' %>
#
# # <link href="https://assets.bookshelf.org/assets/application-28a6b886de2372ee3922fcaf3f78f2d8.css" type="text/css" rel="stylesheet">
- def stylesheet(*sources)
+ def stylesheet(*sources, **options) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
_safe_tags(*sources) do |source|
- html.link(href: _typed_asset_path(source, STYLESHEET_EXT), type: STYLESHEET_MIME_TYPE, rel: STYLESHEET_REL).to_s
+ tag_options = options.dup
+ tag_options[:href] ||= _typed_asset_path(source, STYLESHEET_EXT)
+ tag_options[:type] ||= STYLESHEET_MIME_TYPE
+ tag_options[:rel] ||= STYLESHEET_REL
+
+ if _subresource_integrity? || tag_options.include?(:integrity)
+ tag_options[:integrity] ||= _subresource_integrity_value(source, STYLESHEET_EXT)
+ tag_options[:crossorigin] ||= CROSSORIGIN_ANONYMOUS
+ end
+
+ html.link(**tag_options).to_s
end
end
# Generate <tt>img</tt> tag for given source
#
@@ -668,26 +731,35 @@
# @since 0.1.0
# @api private
def _asset_url(source)
_push_promise(
- _absolute_url?(source) ?
+ _absolute_url?(source) ? # rubocop:disable Style/MultilineTernaryOperator
source : yield
)
end
# @since 0.1.0
# @api private
def _typed_asset_path(source, ext)
- source = "#{ source }#{ ext }" unless source.match(/#{ Regexp.escape(ext) }\z/)
+ source = "#{source}#{ext}" unless source =~ /#{Regexp.escape(ext)}\z/
asset_path(source)
end
+ def _subresource_integrity?
+ !!self.class.assets_configuration.subresource_integrity # rubocop:disable Style/DoubleNegation
+ end
+
+ def _subresource_integrity_value(source, ext)
+ source = "#{source}#{ext}" unless source =~ /#{Regexp.escape(ext)}\z/
+ self.class.assets_configuration.subresource_integrity_value(source) unless _absolute_url?(source)
+ end
+
# @since 0.1.0
# @api private
def _absolute_url?(source)
- URI.regexp.match(source)
+ ABSOLUTE_URL_MATCHER.match(source)
end
# @since 0.1.0
# @api private
def _relative_url(source)
@@ -700,10 +772,10 @@
self.class.assets_configuration.asset_url(source)
end
# @since 0.1.0
# @api private
- def _source_options(src, options, &blk)
+ def _source_options(src, options, &_blk)
options ||= {}
if src.respond_to?(:to_hash)
options = src.to_hash
elsif src