Class: Pakyow::Assets::Asset

Inherits:
Object
  • Object
show all
Extended by:
Support::ClassState
Defined in:
lib/pakyow/assets/asset.rb

Overview

Represents an asset.

Instances are created when booting in all environments, meaning the app is guaranteed access to these objects. Contents are loaded and processed eagerly. This is expected to happen under two scenarios:

1. In development, an asset is loaded and processed when it's requested.
2. In production, when assets are precompiled during a deployment.

Direct Known Subclasses

Types::CSS, Types::JS, Types::Sass, Types::Scss

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(local_path:, config:, dependencies: [], source_location: "", prefix: "/", related: []) ⇒ Asset

Returns a new instance of Asset.



91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/pakyow/assets/asset.rb', line 91

def initialize(local_path:, config:, dependencies: [], source_location: "", prefix: "/", related: [])
  @local_path, @config, @source_location, @dependencies, @related = local_path, config, source_location, dependencies, related

  @logical_path = self.class.update_path_for_emitted_type(
    String.normalize_path(
      File.join(prefix, local_path.sub(source_location, ""))
    )
  )

  @public_path = String.normalize_path(
    File.join(config.prefix, @logical_path)
  )

  @mime_type = case File.extname(@public_path)
  when ".js"
    # Resolves an issue with mini_mime returning `application/ecmascript`
    #
    "application/javascript"
  else
    MiniMime.lookup_by_filename(@public_path)&.content_type.to_s
  end

  @mime_prefix, @mime_suffix = @mime_type.split("/", 2)

  @source_map_enabled = config.source_maps

  @mutex = Mutex.new
end

Instance Attribute Details

#dependenciesObject (readonly)

Returns the value of attribute dependencies.



89
90
91
# File 'lib/pakyow/assets/asset.rb', line 89

def dependencies
  @dependencies
end

#logical_pathObject (readonly)

Returns the value of attribute logical_path.



89
90
91
# File 'lib/pakyow/assets/asset.rb', line 89

def logical_path
  @logical_path
end

#mime_suffixObject (readonly)

Returns the value of attribute mime_suffix.



89
90
91
# File 'lib/pakyow/assets/asset.rb', line 89

def mime_suffix
  @mime_suffix
end

#mime_typeObject (readonly)

Returns the value of attribute mime_type.



89
90
91
# File 'lib/pakyow/assets/asset.rb', line 89

def mime_type
  @mime_type
end

Class Method Details

.inherited(asset_class) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



52
53
54
55
# File 'lib/pakyow/assets/asset.rb', line 52

def inherited(asset_class)
  @__types << asset_class
  super
end

.loadObject

Implemented by subclasses to load any libraries they need.



47
48
49
# File 'lib/pakyow/assets/asset.rb', line 47

def load
  # intentionally empty
end

.new_from_path(path, config:, source_location: "", prefix: "/", related: []) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
# File 'lib/pakyow/assets/asset.rb', line 31

def new_from_path(path, config:, source_location: "", prefix: "/", related: [])
  asset_class = @__types.find { |type|
    type.__extensions.include?(File.extname(path))
  } || self

  asset_class.load; asset_class.new(
    local_path: path,
    source_location: source_location,
    config: config,
    prefix: prefix,
    related: related
  )
end

.update_path_for_emitted_type(path) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



58
59
60
61
62
63
64
# File 'lib/pakyow/assets/asset.rb', line 58

def update_path_for_emitted_type(path)
  if @__emits
    path.sub(File.extname(path), @__emits)
  else
    path
  end
end

Instance Method Details

#bytesizeObject



148
149
150
# File 'lib/pakyow/assets/asset.rb', line 148

def bytesize
  ensure_content(&:bytesize)
end

#disable_source_mapObject



195
196
197
198
199
# File 'lib/pakyow/assets/asset.rb', line 195

def disable_source_map
  tap do
    @source_map_enabled = false
  end
end

#each(&block) ⇒ Object



134
135
136
137
138
# File 'lib/pakyow/assets/asset.rb', line 134

def each(&block)
  ensure_content do |content|
    StringIO.new(post_process(content)).each(&block)
  end
end

#fingerprintObject



152
153
154
155
156
# File 'lib/pakyow/assets/asset.rb', line 152

def fingerprint
  [@local_path].concat(dependencies).each_with_object(Digest::MD5.new) { |path, digest|
    digest.update(Digest::MD5.file(path).hexdigest)
  }.hexdigest
end

#fingerprinted_filenameObject



158
159
160
161
# File 'lib/pakyow/assets/asset.rb', line 158

def fingerprinted_filename
  extension = File.extname(@public_path)
  File.basename(@public_path, extension) + "__" + fingerprint + extension
end

#freezeObject

Overriding and freezing after content is set lets us eagerly process the content rather than incurring that cost on boot.



123
124
125
126
127
128
129
130
131
132
# File 'lib/pakyow/assets/asset.rb', line 123

def freeze
  @freezing = true
  unless @freezing
    public_path
  end

  if instance_variable_defined?(:@content) && (!@config.fingerprint || instance_variable_defined?(:@fingerprinted_public_path))
    super
  end
end

#public_pathObject



163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
# File 'lib/pakyow/assets/asset.rb', line 163

def public_path
  if @config.fingerprint
    unless instance_variable_defined?(:@fingerprinted_public_path)
      @fingerprinted_public_path = File.join(
        File.dirname(@public_path),
        fingerprinted_filename
      )

      freeze
    end

    @fingerprinted_public_path
  else
    @public_path
  end
end

#readObject



140
141
142
143
144
145
146
# File 'lib/pakyow/assets/asset.rb', line 140

def read
  String.new.tap do |asset|
    each do |content|
      asset << content
    end
  end
end

#source_mapObject



184
185
186
187
188
189
190
191
192
193
# File 'lib/pakyow/assets/asset.rb', line 184

def source_map
  if source_map?
    SourceMap.new(
      source_map_content,
      file: File.basename(public_path)
    )
  else
    nil
  end
end

#source_map?Boolean

Returns:

  • (Boolean)


180
181
182
# File 'lib/pakyow/assets/asset.rb', line 180

def source_map?
  respond_to?(:source_map_content)
end