# frozen_string_literal: true module Bridgetown class Configuration class ConfigurationDSL < Bridgetown::FrontMatter::RubyFrontMatter attr_reader :context # @yieldself [ConfigurationDSL] def init(name, require_gem: true, require_initializer: true, **kwargs, &block) # rubocop:todo Metrics return if @scope.initializers.key?(name.to_sym) && @scope.initializers[name.to_sym].completed initializer = _setup_initializer( name:, require_gem:, require_initializer: ) return unless initializer.nil? || initializer.completed == false set :init_params do block ? set(name, &block) : set(name, kwargs) end if initializer.nil? Bridgetown.logger.warn("Initializing:", "The `#{name}' initializer could not be found") return end Bridgetown.logger.debug "Initializing:", name @scope.initializers[name.to_sym].block.( self, **@scope.init_params[name].transform_keys(&:to_sym) ) initializer.completed = true end def only(*context, &) return unless context.any? { _1 == @context } instance_exec(&) end def except(*context, &) return if context.any? { _1 == @context } instance_exec(&) end def hook( owner, event, priority: Bridgetown::Hooks::DEFAULT_PRIORITY, & ) Bridgetown::Hooks.register_one(owner, event, priority:, reloadable: false, &) end def source_manifest(**kwargs) @scope.source_manifests << SourceManifest.new(**kwargs) end def builder(klass = nil, &) return klass.register if klass.is_a?(Class) && klass < Bridgetown::Builder unless klass.is_a?(Symbol) raise "You must supply a constant symbol to register an inline builder" end Object.const_set( klass, Class.new(Bridgetown::Builder, &).tap(&:register) ) end def roda(&block) @scope.roda_initializers << block end def timezone(new_timezone) @data[:timezone] = new_timezone Bridgetown.set_timezone(new_timezone) end def method_missing(key, *value, &block) # rubocop:disable Style/MissingRespondToMissing return get(key) if value.empty? && block.nil? set(key, value[0], &block) end def get(key) unless @data.key?(key) Bridgetown.logger.debug("Initializing:", "Uh oh, missing key `#{key}' in configuration") end super end def set(key, value = nil, &block) # Handle nested data within a block if block value = self.class.new(scope: @scope).tap do |fm| fm.instance_exec(&block) end.to_h end key = key.to_s.delete_suffix("=") if key.to_s.ends_with?("=") @data[key] = if @data[key].is_a?(Hash) && value.is_a?(Hash) Bridgetown::Utils.deep_merge_hashes(@data[key], value) else value end end def reflect(name, require_gem: true, require_initializer: true) initializer = _setup_initializer( name:, require_gem:, require_initializer: ) if initializer.nil? Bridgetown.logger.info("Reflection:", "The `#{name}' initializer could not be found") return end Bridgetown.logger.info( "Reflection:", "The #{name.to_s.yellow} initializer accepts the following options:" ) initializer.block.parameters.each do |param| next if param[0] == :opt Bridgetown.logger.info("", "* #{param[1].to_s.cyan}#{" (required)" if param[0] == :keyreq}") end end # @return [Bridgetown::Configuration::Initializer] def _setup_initializer(name:, require_gem:, require_initializer:) Bridgetown::PluginManager.require_gem(name) if require_gem && !_in_require_denylist?(name) if require_initializer init_file_name = File.join(@scope.root_dir, "config", "#{name}.rb") load(init_file_name) if File.exist?(init_file_name) end @scope.initializers[name.to_sym] end def _in_require_denylist?(name) REQUIRE_DENYLIST.include?(name.to_sym) end # Initializers that are part of the Bridgetown boot sequence. Site owners can override # defaults by running any of these manually…init is no-op if the initializer was already run. def _run_builtins! init :streamlined end end end end