Class: Darkroom::Asset
- Inherits:
-
Object
- Object
- Darkroom::Asset
- Defined in:
- lib/darkroom/asset.rb
Overview
Represents an asset.
Constant Summary collapse
- EXTENSION_REGEX =
/(?=\.\w+)/.freeze
- DEFAULT_QUOTE =
'\''- DISALLOWED_PATH_CHARS =
'\'"`=<>? '- INVALID_PATH_REGEX =
/[#{DISALLOWED_PATH_CHARS}]/.freeze
- PATH_REGEX =
/(?<path>[^#{DISALLOWED_PATH_CHARS}]*)/.freeze
- QUOTED_PATH_REGEX =
/(?<quote>['"])#{PATH_REGEX.source}\k<quote>/.freeze
- REFERENCE_REGEX =
/ (?<quote>['"]?) (?<quoted>#{PATH_REGEX.source}\?asset-(?<entity>path|content)(=(?<format>\w*))?) \k<quote> /x.freeze
- BUILT_IN_PARSE_KINDS =
[:import, :reference].freeze
- REFERENCE_FORMATS =
First item of each set is used as default, so order is important.
{ 'path' => Set.new(%w[versioned unversioned]), 'content' => Set.new(%w[base64 utf8 displace]), }.freeze
Instance Attribute Summary collapse
-
#errors ⇒ Object
readonly
Returns the value of attribute errors.
-
#path ⇒ Object
readonly
Returns the value of attribute path.
-
#path_unversioned ⇒ Object
readonly
Returns the value of attribute path_unversioned.
Instance Method Summary collapse
-
#binary? ⇒ Boolean
Public: Check if the asset’s content is binary.
-
#content(minified: @minify) ⇒ Object
Public: Get the full asset content, including imports and asset reference content substitutions.
-
#content_type ⇒ Object
Public: Get the HTTP MIME type string for this asset.
-
#entry? ⇒ Boolean
Public: Check if the asset is an entry point.
-
#error ⇒ Object
Public: Get a single error wrapper object for all errors.
-
#error? ⇒ Boolean
Public: Check if one or more errors were encountered the last time the asset was processed.
-
#fingerprint ⇒ Object
Public: Get an MD5 hash of the asset’s content.
-
#font? ⇒ Boolean
Public: Check if the asset is a font.
-
#headers(versioned: true) ⇒ Object
Public: Get the asset’s HTTP headers.
-
#image? ⇒ Boolean
Public: Check if the asset is an image.
-
#initialize(path, file, darkroom, prefix: nil, entry: true, minify: false, intermediate: false) ⇒ Asset
constructor
Public: Create a new instance.
-
#inspect ⇒ Object
Public: Get a high-level object info string about this Asset instance.
-
#integrity(algorithm = :sha384) ⇒ Object
Public: Get a subresource integrity string (SHA digest).
-
#path_versioned ⇒ Object
Public: Get the versioned path of the asset (includes the fingerprint).
-
#process ⇒ Object
Public: Process the asset if it’s been modified since the last run (see #modified? for how modification is determined).
Constructor Details
#initialize(path, file, darkroom, prefix: nil, entry: true, minify: false, intermediate: false) ⇒ Asset
Public: Create a new instance.
path - String path this asset will be referenced by (e.g. /js/app.js). file - String absolute path of file on disk. darkroom - Darkroom instance that the asset is a member of. prefix: - String prefix to apply to unversioned and versioned paths. entry: - Boolean specifying if the asset is an entry point (i.e. accessible externally). minify: - Boolean specifying if the asset should be minified when processed. intermediate: - Boolean specifying if the asset exists solely to provide an intermediate form (e.g.
compiled) for another Asset instance.
50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 |
# File 'lib/darkroom/asset.rb', line 50 def initialize(path, file, darkroom, prefix: nil, entry: true, minify: false, intermediate: false) @path = path @dir = File.dirname(path) @file = file @darkroom = darkroom @prefix = prefix @entry = entry @minify = minify @path_unversioned = "#{@prefix}#{@path}" @extension = File.extname(@path).downcase @delegate = Darkroom.delegate(@extension) or raise(UnrecognizedExtensionError.new(@path)) @ran = Set.new if @delegate.compile_delegate && !intermediate @delegate = @delegate.compile_delegate @intermediate_asset = Asset.new( @path, @file, @darkroom, prefix: @prefix, entry: false, minify: false, intermediate: true, ) end require_libs clear end |
Instance Attribute Details
#errors ⇒ Object (readonly)
Returns the value of attribute errors.
38 39 40 |
# File 'lib/darkroom/asset.rb', line 38 def errors @errors end |
#path ⇒ Object (readonly)
Returns the value of attribute path.
38 39 40 |
# File 'lib/darkroom/asset.rb', line 38 def path @path end |
#path_unversioned ⇒ Object (readonly)
Returns the value of attribute path_unversioned.
38 39 40 |
# File 'lib/darkroom/asset.rb', line 38 def path_unversioned @path_unversioned end |
Instance Method Details
#binary? ⇒ Boolean
Public: Check if the asset’s content is binary.
Returns the boolean result.
104 105 106 107 108 109 110 |
# File 'lib/darkroom/asset.rb', line 104 def binary? return @binary if defined?(@binary) type, subtype = content_type.split('/') @binary = type != 'text' && !subtype.include?('json') && !subtype.include?('xml') end |
#content(minified: @minify) ⇒ Object
Public: Get the full asset content, including imports and asset reference content substitutions.
minified: - Boolean specifying if the minified version is desired.
Returns the String asset content, minified if requested (and the asset is minifiable).
202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 |
# File 'lib/darkroom/asset.rb', line 202 def content(minified: @minify) unless ran?(:content) compile @content = if imports.empty? @own_content else (0..imports.size).inject(+'') do |content, i| own_content = (imports[i] || self).own_content content << "\n" unless (content[-1] == "\n" && own_content[0] == "\n") || content.empty? content << own_content end end begin finalized = @delegate.finalize_handler&.call( parse_data: @parse_data, path: @path, content: @content, ) @content = finalized if finalized.kind_of?(String) rescue StandardError => e @errors << e end end if @delegate.minify_handler && !@content_minified && (minified || @minify) begin @content_minified = @delegate.minify_handler.call( parse_data: @parse_data, path: @path, content: @content, ) rescue StandardError => e @errors << e end end @fingerprint ||= Digest::MD5.hexdigest((@minify && @content_minified) || @content).freeze @path_versioned ||= "#{@prefix}#{@path.sub(EXTENSION_REGEX, "-#{@fingerprint}")}" (minified && @content_minified) || @content ensure @content.freeze @content_minified.freeze end |
#content_type ⇒ Object
Public: Get the HTTP MIME type string for this asset.
Returns the String content type from the asset’s Delegate.
97 98 99 |
# File 'lib/darkroom/asset.rb', line 97 def content_type @delegate.content_type end |
#entry? ⇒ Boolean
Public: Check if the asset is an entry point.
Returns the boolean result.
129 130 131 |
# File 'lib/darkroom/asset.rb', line 129 def entry? @entry end |
#error ⇒ Object
Public: Get a single error wrapper object for all errors.
Returns a ProcessingError if one or more errors exit or nil otherwise.
143 144 145 |
# File 'lib/darkroom/asset.rb', line 143 def error @error ||= error? ? ProcessingError.new(@errors) : nil end |
#error? ⇒ Boolean
Public: Check if one or more errors were encountered the last time the asset was processed.
Returns the boolean result.
136 137 138 |
# File 'lib/darkroom/asset.rb', line 136 def error? @errors && !@errors.empty? end |
#fingerprint ⇒ Object
Public: Get an MD5 hash of the asset’s content.
Returns the String hash.
150 151 152 153 154 |
# File 'lib/darkroom/asset.rb', line 150 def fingerprint content @fingerprint end |
#font? ⇒ Boolean
Public: Check if the asset is a font.
Returns the boolean result.
115 116 117 |
# File 'lib/darkroom/asset.rb', line 115 def font? defined?(@is_font) ? @is_font : (@is_font = content_type.start_with?('font/')) end |
#headers(versioned: true) ⇒ Object
Public: Get the asset’s HTTP headers.
versioned: - Boolean indicating if this is for the versioned or unversioned asset path. If true,
a Cache-Control header with max-age is included; if false, an ETag header is used.
Returns a Hash of String HTTP header names and String values.
171 172 173 174 175 176 177 |
# File 'lib/darkroom/asset.rb', line 171 def headers(versioned: true) { 'Content-Type' => content_type, 'Cache-Control' => ('public, max-age=31536000' if versioned), 'ETag' => ("\"#{fingerprint}\"" unless versioned), }.compact! end |
#image? ⇒ Boolean
Public: Check if the asset is an image.
Returns the boolean result.
122 123 124 |
# File 'lib/darkroom/asset.rb', line 122 def image? defined?(@is_image) ? @is_image : (@is_image = content_type.start_with?('image/')) end |
#inspect ⇒ Object
Public: Get a high-level object info string about this Asset instance.
Returns the String.
255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 |
# File 'lib/darkroom/asset.rb', line 255 def inspect "#<#{self.class} " \ "@delegate=#{@delegate.inspect}, " \ "@dir=#{@dir.inspect}, " \ "@entry=#{@entry.inspect}, " \ "@errors=#{@errors.inspect}, " \ "@extension=#{@extension.inspect}, " \ "@file=#{@file.inspect}, " \ "@fingerprint=#{@fingerprint.inspect}, " \ "@minify=#{@minify.inspect}, " \ "@modified=#{@modified.inspect}, " \ "@mtime=#{@mtime.inspect}, " \ "@path=#{@path.inspect}, " \ "@path_unversioned=#{@path_unversioned.inspect}, " \ "@path_versioned=#{@path_versioned.inspect}, " \ "@prefix=#{@prefix.inspect}" \ '>' end |
#integrity(algorithm = :sha384) ⇒ Object
Public: Get a subresource integrity string (SHA digest).
algorithm - Symbol hash algorithm name to use to generate the integrity string (must be one of
:sha256, :sha384, :sha512).
Returns the String SHA digest. Raises RuntimeError if the request algorithm is not valid.
186 187 188 189 190 191 192 193 194 195 |
# File 'lib/darkroom/asset.rb', line 186 def integrity(algorithm = :sha384) @integrity[algorithm] ||= "#{algorithm}-#{Base64.strict_encode64( case algorithm when :sha256 then Digest::SHA256.digest(content) when :sha384 then Digest::SHA384.digest(content) when :sha512 then Digest::SHA512.digest(content) else raise("Unrecognized integrity algorithm: #{algorithm}") end )}".freeze end |
#path_versioned ⇒ Object
Public: Get the versioned path of the asset (includes the fingerprint).
Returns the String versioned path.
159 160 161 162 163 |
# File 'lib/darkroom/asset.rb', line 159 def path_versioned content @path_versioned end |
#process ⇒ Object
Public: Process the asset if it’s been modified since the last run (see #modified? for how modification is determined). The asset file is read from disk, references are substituted (if supported for the asset type), content is compiled (if required), imports are prefixed to the asset’s own content (if supported), and content is minified (if supported and enabled and the asset is an entry point).
Returns nothing.
87 88 89 90 91 92 |
# File 'lib/darkroom/asset.rb', line 87 def process return if ran?(:process) compile content if entry? end |