lib/hanami/assets.rb in hanami-assets-1.3.5 vs lib/hanami/assets.rb in hanami-assets-2.1.0.beta2

- old
+ new

@@ -1,175 +1,93 @@ # frozen_string_literal: true -require "hanami/utils/class_attribute" +require "json" +require "zeitwerk" # Hanami # # @since 0.1.0 module Hanami # Assets management for Ruby web applications # # @since 0.1.0 - module Assets - # Base error for Hanami::Assets - # - # All the errors defined in this framework MUST inherit from it. - # - # @since 0.1.0 - class Error < ::StandardError + class Assets + # @since 2.1.0 + # @api private + def self.gem_loader + @gem_loader ||= Zeitwerk::Loader.new.tap do |loader| + root = File.expand_path("..", __dir__) + loader.tag = "hanami-assets" + loader.push_dir(root) + loader.ignore( + "#{root}/hanami-assets.rb", + "#{root}/hanami/assets/version.rb", + "#{root}/hanami/assets/errors.rb" + ) + loader.inflector = Zeitwerk::GemInflector.new("#{root}/hanami-assets.rb") + end end - require "hanami/assets/version" - require "hanami/assets/configuration" - require "hanami/assets/config/global_sources" - require "hanami/assets/helpers" + gem_loader.setup + require_relative "assets/version" + require_relative "assets/errors" - include Utils::ClassAttribute - - # Configuration - # - # @since 0.1.0 + # @since 2.1.0 # @api private - class_attribute :configuration - self.configuration = Configuration.new + SEPARATOR = "/" + private_constant :SEPARATOR - # Configure framework - # - # @param blk [Proc] configuration code block - # - # @return self - # - # @since 0.1.0 - # - # @see Hanami::Assets::Configuration - def self.configure(&blk) - configuration.instance_eval(&blk) - self - end + attr_reader :config - # Prepare assets for deploys - # - # @since 0.1.0 - def self.deploy - require "hanami/assets/precompiler" - require "hanami/assets/bundler" - - Precompiler.new(configuration, duplicates).run - Bundler.new(configuration, duplicates).run + # @since 2.1.0 + # @api public + def initialize(config:) + @config = config end - # Precompile assets - # - # @since 0.4.0 - def self.precompile(configurations) - require "hanami/assets/precompiler" - require "hanami/assets/bundler" + # @since 2.1.0 + # @api public + def [](path) + asset_attrs = manifest + .fetch(path) { raise AssetMissingError.new(path) } + .transform_keys(&:to_sym) + .tap { |attrs| + # The `url` attribute we receive from the manifest is actually a path; rename it as such + # so our `Asset` attributes make more sense on their own. + attrs[:path] = attrs.delete(:url) + } - Precompiler.new(configuration, configurations).run - Bundler.new(configuration, configurations).run + Asset.new( + **asset_attrs, + base_url: config.base_url + ) end - # Preload the framework - # - # This MUST be used in production mode - # - # @since 0.1.0 - # - # @example Direct Invocation - # require 'hanami/assets' - # - # Hanami::Assets.load! - # - # @example Load Via Configuration Block - # require 'hanami/assets' - # - # Hanami::Assets.configure do - # # ... - # end.load! - def self.load! - configuration.load! + # @since 2.1.0 + # @api public + def subresource_integrity? + config.subresource_integrity.any? end - # Global assets sources - # - # This is designed for third party integration gems with frontend frameworks - # like Bootstrap, Ember.js or React. - # - # Developers can maintain gems that ship static assets for these frameworks - # and make them available to Hanami::Assets. - # - # @return [Hanami::Assets::Config::GlobalSources] - # - # @since 0.1.0 - # - # @example Ember.js Integration - # # lib/hanami/emberjs.rb (third party gem) - # require 'hanami/assets' - # - # Hanami::Assets.sources << '/path/to/emberjs/assets' - def self.sources - synchronize do - @@sources ||= Config::GlobalSources.new # rubocop:disable Style/ClassVars - end + # @since 2.1.0 + # @api public + def crossorigin?(source_path) + config.crossorigin?(source_path) end - # Duplicate the framework and generate modules for the target application - # - # @param _mod [Module] the Ruby namespace of the application - # @param blk [Proc] an optional block to configure the framework - # - # @return [Module] a copy of Hanami::Assets - # - # @since 0.1.0 - # - # @see Hanami::Assets#dupe - # @see Hanami::Assets::Configuration - def self.duplicate(_mod, &blk) - dupe.tap do |duplicated| - duplicated.configure(&blk) if block_given? - duplicates << duplicated - end - end + private - # Duplicate Hanami::Assets in order to create a new separated instance - # of the framework. - # - # The new instance of the framework will be completely decoupled from the - # original. It will inherit the configuration, but all the changes that - # happen after the duplication, won't be reflected on the other copies. - # - # @return [Module] a copy of Hanami::Assets - # - # @since 0.1.0 - # @api private - def self.dupe - dup.tap do |duplicated| - duplicated.configuration = configuration.duplicate - end - end + def manifest + return @manifest if instance_variable_defined?(:@manifest) - # Keep track of duplicated frameworks - # - # @return [Array] a collection of duplicated frameworks - # - # @since 0.1.0 - # @api private - # - # @see Hanami::Assets#duplicate - # @see Hanami::Assets#dupe - def self.duplicates - synchronize do - @@duplicates ||= [] # rubocop:disable Style/ClassVars + unless config.manifest_path + raise ConfigError, "no manifest_path configured" end - end - class << self - private - - # @since 0.1.0 - # @api private - def synchronize(&blk) - Mutex.new.synchronize(&blk) + unless File.exist?(config.manifest_path) + raise ManifestMissingError.new(config.manifest_path) end + + @manifest = JSON.parse(File.read(config.manifest_path)) end end end