From c499b54294f78f52ac49cf8b8ca0983f81dccbba Mon Sep 17 00:00:00 2001 From: Jared White Date: Mon, 1 Nov 2021 13:33:34 -0700 Subject: [PATCH 01/12] First pass at a process-wide autoloading system --- .../concerns/site/configurable.rb | 7 +- .../concerns/site/extensible.rb | 3 +- .../lib/bridgetown-core/concerns/site/ssr.rb | 4 +- .../lib/bridgetown-core/configuration.rb | 16 ++-- .../lib/bridgetown-core/liquid_renderer.rb | 2 +- .../lib/bridgetown-core/plugin_manager.rb | 34 +-------- .../lib/bridgetown-core/rack/boot.rb | 76 +++++++++++++------ .../lib/bridgetown-core/rack/roda.rb | 3 +- bridgetown-core/lib/bridgetown-core/site.rb | 20 +++-- bridgetown-core/lib/bridgetown-core/utils.rb | 1 + .../bridgetown-core/utils/loaders_manager.rb | 63 +++++++++++++++ .../lib/bridgetown-core/watcher.rb | 3 +- 12 files changed, 157 insertions(+), 75 deletions(-) create mode 100644 bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb diff --git a/bridgetown-core/lib/bridgetown-core/concerns/site/configurable.rb b/bridgetown-core/lib/bridgetown-core/concerns/site/configurable.rb index 0fb73826f..d56a5eb3b 100644 --- a/bridgetown-core/lib/bridgetown-core/concerns/site/configurable.rb +++ b/bridgetown-core/lib/bridgetown-core/concerns/site/configurable.rb @@ -155,7 +155,7 @@ def configure_cache Bridgetown::Cache.disable_disk_cache! if config["disable_disk_cache"] end - def configure_component_paths + def configure_component_paths # rubocop:todo Metrics/AbcSize # Loop through plugins paths first plugin_components_load_paths = Bridgetown::PluginManager.source_manifests .map(&:components).compact @@ -172,7 +172,10 @@ def configure_component_paths end end - @components_load_paths = plugin_components_load_paths + local_components_load_paths + config.components_load_paths = plugin_components_load_paths + local_components_load_paths + # Because "first constant wins" in Zeitwerk, we need to load the local + # source components _before_ we load any from plugins + config.autoload_paths += config.components_load_paths.reverse end def configure_file_read_opts diff --git a/bridgetown-core/lib/bridgetown-core/concerns/site/extensible.rb b/bridgetown-core/lib/bridgetown-core/concerns/site/extensible.rb index c72aca90b..5eb8b6069 100644 --- a/bridgetown-core/lib/bridgetown-core/concerns/site/extensible.rb +++ b/bridgetown-core/lib/bridgetown-core/concerns/site/extensible.rb @@ -3,13 +3,14 @@ class Bridgetown::Site module Extensible # Load necessary libraries, plugins, converters, and generators. + # This is only ever run once for the lifecycle of the site object. # @see Converter # @see Generator # @see PluginManager # @return [void] def setup plugin_manager.require_plugin_files - plugin_manager.setup_component_loaders + loaders_manager.setup_loaders self.converters = instantiate_subclasses(Bridgetown::Converter) self.generators = instantiate_subclasses(Bridgetown::Generator) end diff --git a/bridgetown-core/lib/bridgetown-core/concerns/site/ssr.rb b/bridgetown-core/lib/bridgetown-core/concerns/site/ssr.rb index c18ad06a6..1ce30b29b 100644 --- a/bridgetown-core/lib/bridgetown-core/concerns/site/ssr.rb +++ b/bridgetown-core/lib/bridgetown-core/concerns/site/ssr.rb @@ -8,14 +8,14 @@ def self.included(base) module ClassMethods # Establish an SSR pipeline for a persistent backend process - def start_ssr! # rubocop:todo Metrics/AbcSize + def start_ssr!(loaders_manager: nil) # rubocop:todo Metrics/AbcSize if Bridgetown::Current.site raise Bridgetown::Errors::FatalException, "Bridgetown SSR already started! " \ "Check your Rack app for threading issues" end Bridgetown::PluginManager.require_from_bundler - site = new(Bridgetown::Current.preloaded_configuration) + site = new(Bridgetown::Current.preloaded_configuration, loaders_manager: loaders_manager) site.enable_ssr Bridgetown::Hooks.trigger :site, :pre_read, site diff --git a/bridgetown-core/lib/bridgetown-core/configuration.rb b/bridgetown-core/lib/bridgetown-core/configuration.rb index 1cc52baa8..3aecd7177 100644 --- a/bridgetown-core/lib/bridgetown-core/configuration.rb +++ b/bridgetown-core/lib/bridgetown-core/configuration.rb @@ -25,6 +25,8 @@ class Configuration < HashWithDotAccess::Hash "taxonomies" => { category: { key: "categories", title: "Category" }, tag: { key: "tags", title: "Tag" }, }, + "autoload_paths" => [], + "plugins_use_zeitwerk" => true, # Handling Reading "include" => [".htaccess", "_redirects", ".well-known"], @@ -109,21 +111,21 @@ def get_config_value_with_override(config_key, override) override[config_key] || self[config_key] || DEFAULTS[config_key] end - # Public: Directory of the top-level root where config files are located + # Directory of the top-level root where config files are located # - # override - the command-line options hash + # @param override [Hash] options hash which will override value if key is present # - # Returns the path to the Bridgetown root directory - def root_dir(override = "") + # @return [String] path to the Bridgetown root directory + def root_dir(override = {}) get_config_value_with_override("root_dir", override) end # Public: Directory of the Bridgetown source folder # - # override - the command-line options hash + # @param override [Hash] options hash which will override value if key is present # - # Returns the path to the Bridgetown source directory - def source(override = "") + # @return [String] path to the Bridgetown source directory + def source(override = {}) get_config_value_with_override("source", override) end diff --git a/bridgetown-core/lib/bridgetown-core/liquid_renderer.rb b/bridgetown-core/lib/bridgetown-core/liquid_renderer.rb index a49506818..164ecec20 100644 --- a/bridgetown-core/lib/bridgetown-core/liquid_renderer.rb +++ b/bridgetown-core/lib/bridgetown-core/liquid_renderer.rb @@ -15,7 +15,7 @@ def initialize(site) # Set up Liquid file system access to components for the Render tag Liquid::Template.file_system = LiquidRenderer::FileSystem.new( - @site.components_load_paths, "%s.liquid" + @site.config.components_load_paths, "%s.liquid" ) Liquid::Template.file_system.site = site diff --git a/bridgetown-core/lib/bridgetown-core/plugin_manager.rb b/bridgetown-core/lib/bridgetown-core/plugin_manager.rb index 6b4493ddb..0463362db 100644 --- a/bridgetown-core/lib/bridgetown-core/plugin_manager.rb +++ b/bridgetown-core/lib/bridgetown-core/plugin_manager.rb @@ -5,7 +5,7 @@ class PluginManager PLUGINS_GROUP = :bridgetown_plugins YARN_DEPENDENCY_REGEXP = %r!(.+)@([^@]*)$!.freeze - attr_reader :site, :component_loaders + attr_reader :site, :loaders_manager @source_manifests = Set.new @registered_plugins = Set.new @@ -37,7 +37,7 @@ class << self # Returns nothing def initialize(site) @site = site - @component_loaders = {} + @loaders_manager = Bridgetown::Utils::LoadersManager.new(site.config) end def self.require_from_bundler @@ -158,35 +158,5 @@ def plugins_path Array(site.config["plugins_dir"]).map { |d| File.expand_path(d) } end end - - def setup_component_loaders - unless @component_loaders.keys.empty? - @component_loaders.each do |_path, loader| - loader.unload - end - @component_loaders = {} - end - - # Because "first constant wins" in Zeitwerk, we need to load the local - # source components _before_ we load any from plugins - site.components_load_paths.reverse_each do |load_path| - next unless Dir.exist? load_path - - begin - @component_loaders[load_path] = Zeitwerk::Loader.new - @component_loaders[load_path].push_dir(load_path) - @component_loaders[load_path].enable_reloading if load_path.start_with?(site.root_dir) - @component_loaders[load_path].ignore(File.join(load_path, "**", "*.js.rb")) - @component_loaders[load_path].setup - rescue Zeitwerk::Error # rubocop:disable Lint/SuppressedException - end - end - end - - def reload_component_loaders - @component_loaders.each do |path, loader| - loader.reload if path.start_with?(site.root_dir) - end - end end end diff --git a/bridgetown-core/lib/bridgetown-core/rack/boot.rb b/bridgetown-core/lib/bridgetown-core/rack/boot.rb index 47210febc..a5d1dc94a 100644 --- a/bridgetown-core/lib/bridgetown-core/rack/boot.rb +++ b/bridgetown-core/lib/bridgetown-core/rack/boot.rb @@ -14,9 +14,17 @@ module Bridgetown module Rack - def self.boot - autoload_server_folder(root: Dir.pwd) - RodaApp.opts[:bridgetown_preloaded_config] = Bridgetown::Current.preloaded_configuration + class << self + # @return [Bridgetown::Utils::LoadersManager] + attr_accessor :loaders_manager + end + + def self.boot(roda_app = nil) + self.loaders_manager = + Bridgetown::Utils::LoadersManager.new(Bridgetown::Current.preloaded_configuration) + autoload_server_folder + (roda_app || RodaApp).opts[:bridgetown_preloaded_config] = + Bridgetown::Current.preloaded_configuration rescue Roda::RodaError => e if e.message.include?("sessions plugin :secret option") raise Bridgetown::Errors::InvalidConfigurationError, @@ -27,29 +35,51 @@ def self.boot raise e end - def self.autoload_server_folder(root:) + # @param root [String] root of Bridgetown site, defaults to config value + def self.autoload_server_folder( # rubocop:todo Metrics/MethodLength + root: Bridgetown::Current.preloaded_configuration.root_dir + ) server_folder = File.join(root, "server") - loader = Zeitwerk::Loader.new - loader.push_dir server_folder - loader.enable_reloading unless ENV["BRIDGETOWN_ENV"] == "production" - loader.setup - loader.eager_load - loader.do_not_eager_load(File.join(server_folder, "roda_app.rb")) - - unless ENV["BRIDGETOWN_ENV"] == "production" - begin - Listen.to(server_folder) do |_modified, _added, _removed| - loader.reload - loader.eager_load - Bridgetown::Rack::Routes.reload_subclasses - end.start - # interrupt isn't handled well by the listener - rescue ThreadError # rubocop:disable Lint/SuppressedException + # Bridgetown::Current.preloaded_configuration.autoload_paths << server_folder + + Bridgetown::Hooks.register_one( + :loader, :post_setup, reloadable: false + ) do |loader, load_path| + next unless load_path == server_folder + + loader.eager_load + loader.do_not_eager_load(File.join(server_folder, "roda_app.rb")) + + unless ENV["BRIDGETOWN_ENV"] == "production" + begin + Listen.to(server_folder) do |_modified, _added, _removed| + loader.reload + loader.eager_load + Bridgetown::Rack::Routes.reload_subclasses + end.start + # interrupt isn't handled well by the listener + rescue ThreadError # rubocop:disable Lint/SuppressedException + end end end - rescue Zeitwerk::Error - # We assume if there's an error it's because Zeitwerk already registered this folder, - # so it's fine to swallow the error + + Bridgetown::Hooks.register_one( + :loader, :post_reload, reloadable: false + ) do |loader, load_path| + next unless load_path == server_folder + + loader.eager_load + Bridgetown::Rack::Routes.reload_subclasses + end + + loaders_manager.setup_loaders([server_folder]) end + + # loader = Zeitwerk::Loader.new + # loader.push_dir server_folder + # loader.enable_reloading unless ENV["BRIDGETOWN_ENV"] == "production" + # loader.setup + # loader.eager_load + # loader.do_not_eager_load(File.join(server_folder, "roda_app.rb")) end end diff --git a/bridgetown-core/lib/bridgetown-core/rack/roda.rb b/bridgetown-core/lib/bridgetown-core/rack/roda.rb index 3891fbf15..973186d57 100644 --- a/bridgetown-core/lib/bridgetown-core/rack/roda.rb +++ b/bridgetown-core/lib/bridgetown-core/rack/roda.rb @@ -6,7 +6,8 @@ class Roda module RodaPlugins module BridgetownSSR def self.configure(app, _opts = {}, &block) - app.opts[:bridgetown_site] = Bridgetown::Site.start_ssr!(&block) + app.opts[:bridgetown_site] = + Bridgetown::Site.start_ssr!(loaders_manager: Bridgetown::Rack.loaders_manager, &block) end end diff --git a/bridgetown-core/lib/bridgetown-core/site.rb b/bridgetown-core/lib/bridgetown-core/site.rb index 80d03629a..17d14fca8 100644 --- a/bridgetown-core/lib/bridgetown-core/site.rb +++ b/bridgetown-core/lib/bridgetown-core/site.rb @@ -13,9 +13,10 @@ class Site include SSR include Writable - attr_reader :root_dir, :source, :dest, :cache_dir, :config, - :liquid_renderer, :components_load_paths, - :includes_load_paths + attr_reader :root_dir, :source, :dest, :cache_dir, :config, :liquid_renderer + + # @return [Bridgetown::Utils::LoadersManager] + attr_reader :loaders_manager # All files not pages/documents or structured data in the source folder # @return [Array] @@ -33,11 +34,20 @@ class Site # Initialize a new Site. # - # config - A Hash containing site configuration details. - def initialize(config) + # @param config [Bridgetown::Configuration] + # @param loaders_manager [Bridgetown::Utils::LoadersManager] initialized if none provided + def initialize(config, loaders_manager: nil) self.config = config locale + loaders_manager = if loaders_manager + loaders_manager.config = self.config + loaders_manager + else + Bridgetown::Utils::LoadersManager.new(self.config) + end + @loaders_manager = loaders_manager + @plugin_manager = PluginManager.new(self) @cleaner = Cleaner.new(self) @reader = Reader.new(self) diff --git a/bridgetown-core/lib/bridgetown-core/utils.rb b/bridgetown-core/lib/bridgetown-core/utils.rb index 386a7e936..97cdab383 100644 --- a/bridgetown-core/lib/bridgetown-core/utils.rb +++ b/bridgetown-core/lib/bridgetown-core/utils.rb @@ -5,6 +5,7 @@ module Utils # rubocop:todo Metrics/ModuleLength extend self autoload :Ansi, "bridgetown-core/utils/ansi" autoload :Aux, "bridgetown-core/utils/aux" + autoload :LoadersManager, "bridgetown-core/utils/loaders_manager" autoload :RequireGems, "bridgetown-core/utils/require_gems" autoload :RubyExec, "bridgetown-core/utils/ruby_exec" autoload :RubyFrontMatter, "bridgetown-core/utils/ruby_front_matter" diff --git a/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb b/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb new file mode 100644 index 000000000..c804c16d4 --- /dev/null +++ b/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb @@ -0,0 +1,63 @@ +# frozen_string_literal: true + +module Bridgetown + module Utils + class LoadersManager + attr_accessor :config + + attr_reader :loaders, :root_dir + + # @param config [Bridgetown::Configuration] + # @param root_dir [String] root of the current site + def initialize(config, root_dir = Dir.pwd) + @config = config + @loaders = {} + @root_dir = root_dir + end + + def unload_loaders + return if @loaders.keys.empty? + + @loaders.each do |_path, loader| + loader.unload + end + @loaders = {} + end + + def reloading_enabled?(load_path) + load_path.start_with?(root_dir) && ENV["BRIDGETOWN_ENV"] != "production" + end + + def setup_loaders(autoload_paths = []) + (autoload_paths.presence || config.autoload_paths).each do |load_path| + if @loaders.key?(load_path) + raise "Zeitwerk loader already added for `#{load_path}'. Please check your config" + end + + next unless Dir.exist? load_path + + loader = Zeitwerk::Loader.new + begin + loader.push_dir(load_path) + rescue Zeitwerk::Error + next + end + loader.enable_reloading if reloading_enabled?(load_path) + loader.ignore(File.join(load_path, "**", "*.js.rb")) + Bridgetown::Hooks.trigger :loader, :pre_setup, loader, load_path + loader.setup + Bridgetown::Hooks.trigger :loader, :post_setup, loader, load_path + @loaders[load_path] = loader + end + end + + def reload_loaders + @loaders.each do |load_path, loader| + Bridgetown::Hooks.trigger :loader, :pre_reload, loader, load_path + loader.reload if reloading_enabled?(load_path) + Bridgetown::Hooks.trigger :loader, :post_reload, loader, load_path + end + end + end + end +end diff --git a/bridgetown-core/lib/bridgetown-core/watcher.rb b/bridgetown-core/lib/bridgetown-core/watcher.rb index 70dd9f801..66e8c71b7 100644 --- a/bridgetown-core/lib/bridgetown-core/watcher.rb +++ b/bridgetown-core/lib/bridgetown-core/watcher.rb @@ -123,13 +123,14 @@ def sleep_forever loop { sleep 1000 } end + # @param site [Bridgetown::Site] def process(site, time, options) begin I18n.reload! # make sure any locale files get read again Bridgetown::Hooks.trigger :site, :pre_reload, site Bridgetown::Hooks.clear_reloadable_hooks site.plugin_manager.reload_plugin_files - site.plugin_manager.reload_component_loaders + site.loaders_manager.reload_loaders site.process Bridgetown.logger.info "Done! 🎉", "#{"Completed".green} in less than" \ " #{(Time.now - time).ceil(2)} seconds." From a8707d9a35ea91d9a5a67c36d009a85d3331a9d9 Mon Sep 17 00:00:00 2001 From: Jared White Date: Tue, 2 Nov 2021 10:23:25 -0700 Subject: [PATCH 02/12] Hook the watcher into the SSR pipeline --- .../lib/bridgetown-core/concerns/site/ssr.rb | 52 +++++++++++++------ .../lib/bridgetown-core/configuration.rb | 1 + .../lib/bridgetown-core/watcher.rb | 16 ++++-- 3 files changed, 50 insertions(+), 19 deletions(-) diff --git a/bridgetown-core/lib/bridgetown-core/concerns/site/ssr.rb b/bridgetown-core/lib/bridgetown-core/concerns/site/ssr.rb index 1ce30b29b..b52eb4229 100644 --- a/bridgetown-core/lib/bridgetown-core/concerns/site/ssr.rb +++ b/bridgetown-core/lib/bridgetown-core/concerns/site/ssr.rb @@ -8,7 +8,7 @@ def self.included(base) module ClassMethods # Establish an SSR pipeline for a persistent backend process - def start_ssr!(loaders_manager: nil) # rubocop:todo Metrics/AbcSize + def start_ssr!(loaders_manager: nil, &block) if Bridgetown::Current.site raise Bridgetown::Errors::FatalException, "Bridgetown SSR already started! " \ "Check your Rack app for threading issues" @@ -17,20 +17,7 @@ def start_ssr!(loaders_manager: nil) # rubocop:todo Metrics/AbcSize Bridgetown::PluginManager.require_from_bundler site = new(Bridgetown::Current.preloaded_configuration, loaders_manager: loaders_manager) site.enable_ssr - - Bridgetown::Hooks.trigger :site, :pre_read, site - site.defaults_reader.tap do |d| - d.path_defaults.clear - d.read - end - site.layouts = Bridgetown::LayoutReader.new(site).read - site.collections.data.tap do |coll| - coll.read - site.data = coll.merge_data_resources - end - Bridgetown::Hooks.trigger :site, :post_read, site - - yield(site) if block_given? # provide additional setup hook + site.ssr_setup(&block) site end @@ -45,6 +32,41 @@ def enable_ssr @ssr_enabled = true end + def ssr_setup(&block) # rubocop:disable Metrics/AbcSize + config.serving = true + Bridgetown::Hooks.trigger :site, :pre_read, self + defaults_reader.tap do |d| + d.path_defaults.clear + d.read + end + reader.read_layouts + collections.data.tap do |coll| + coll.read + self.data = coll.merge_data_resources + end + Bridgetown::Hooks.trigger :site, :post_read, self + + hook = block&.(self) # provide additional setup hook + return if Bridgetown.env.production? + + @ssr_reload_hook = hook if hook.is_a?(Proc) && hook.lambda? + Bridgetown::Watcher.watch(self, config) + end + + def ssr_reload + reset soft: true + reader.read_layouts + + collections.data.tap do |coll| + coll.resources.clear + coll.read + coll.merge_data_resources.each do |k, v| + data[k] = v + end + end + @ssr_reload_hook.() if @ssr_reload_hook.is_a?(Proc) + end + def disable_ssr Bridgetown.logger.info "SSR:", "now disabled." @ssr_enabled = false diff --git a/bridgetown-core/lib/bridgetown-core/configuration.rb b/bridgetown-core/lib/bridgetown-core/configuration.rb index 3aecd7177..b1873b376 100644 --- a/bridgetown-core/lib/bridgetown-core/configuration.rb +++ b/bridgetown-core/lib/bridgetown-core/configuration.rb @@ -242,6 +242,7 @@ def merge_environment_specific_options! self[k] = self[Bridgetown.environment][k] end delete(Bridgetown.environment) + autoload_paths.map! { |load_path| File.expand_path(load_path, root_dir) } self end diff --git a/bridgetown-core/lib/bridgetown-core/watcher.rb b/bridgetown-core/lib/bridgetown-core/watcher.rb index 66e8c71b7..a208e3b84 100644 --- a/bridgetown-core/lib/bridgetown-core/watcher.rb +++ b/bridgetown-core/lib/bridgetown-core/watcher.rb @@ -41,10 +41,12 @@ def build_listener(site, options) Dir.exist?(path) end + paths_to_watch = (plugin_paths_to_watch + options.autoload_paths).uniq + Listen.to( options["source"], webpack_path, - *plugin_paths_to_watch, + *paths_to_watch, ignore: listen_ignore_paths(options), force_polling: options["force_polling"], &listen_handler(site, options) @@ -57,10 +59,12 @@ def listen_handler(site, options) c = modified + added + removed n = c.length - Bridgetown.logger.info "Regenerating…" - Bridgetown.logger.info "", "#{n} file(s) changed at #{t.strftime("%Y-%m-%d %H:%M:%S")}" + unless site.ssr? + Bridgetown.logger.info "Regenerating…" + Bridgetown.logger.info "", "#{n} file(s) changed at #{t.strftime("%Y-%m-%d %H:%M:%S")}" + c.each { |path| Bridgetown.logger.info "", path["#{site.root_dir}/".length..-1] } + end - c.each { |path| Bridgetown.logger.info "", path["#{site.root_dir}/".length..-1] } process(site, t, options) end end @@ -127,10 +131,14 @@ def sleep_forever def process(site, time, options) begin I18n.reload! # make sure any locale files get read again + Bridgetown::Current.site = site # needed in SSR mode apparently Bridgetown::Hooks.trigger :site, :pre_reload, site Bridgetown::Hooks.clear_reloadable_hooks site.plugin_manager.reload_plugin_files site.loaders_manager.reload_loaders + + return site.ssr_reload if site.ssr? + site.process Bridgetown.logger.info "Done! 🎉", "#{"Completed".green} in less than" \ " #{(Time.now - time).ceil(2)} seconds." From 44f27a6b59d208e079dc6807efffbe5e54a72cc2 Mon Sep 17 00:00:00 2001 From: Jared White Date: Tue, 2 Nov 2021 11:12:02 -0700 Subject: [PATCH 03/12] Officially use Zeitwerk for plugins dir --- .../lib/bridgetown-core/configuration.rb | 25 ++++++++++++++++++- .../lib/bridgetown-core/plugin_manager.rb | 4 +++ .../lib/bridgetown-core/rack/boot.rb | 7 ------ .../bridgetown-core/utils/loaders_manager.rb | 6 ++++- 4 files changed, 33 insertions(+), 9 deletions(-) diff --git a/bridgetown-core/lib/bridgetown-core/configuration.rb b/bridgetown-core/lib/bridgetown-core/configuration.rb index b1873b376..c478fab02 100644 --- a/bridgetown-core/lib/bridgetown-core/configuration.rb +++ b/bridgetown-core/lib/bridgetown-core/configuration.rb @@ -26,6 +26,7 @@ class Configuration < HashWithDotAccess::Hash category: { key: "categories", title: "Category" }, tag: { key: "tags", title: "Tag" }, }, "autoload_paths" => [], + "eager_load_paths" => [], "plugins_use_zeitwerk" => true, # Handling Reading @@ -100,6 +101,7 @@ class << self def from(user_config, starting_defaults = DEFAULTS) Utils.deep_merge_hashes(starting_defaults.deep_dup, Configuration[user_config]) .merge_environment_specific_options! + .setup_load_paths! .setup_locales .add_default_collections .add_default_excludes @@ -242,7 +244,28 @@ def merge_environment_specific_options! self[k] = self[Bridgetown.environment][k] end delete(Bridgetown.environment) - autoload_paths.map! { |load_path| File.expand_path(load_path, root_dir) } + + self + end + + def setup_load_paths! + if self[:plugins_use_zeitwerk] + autoload_paths.unshift({ + path: self[:plugins_dir], + eager: true, + }) + end + + autoload_paths.map! do |load_path| + if load_path.is_a?(Hash) + expanded = File.expand_path(load_path[:path], root_dir) + self[:eager_load_paths] << expanded if load_path[:eager] + next expanded + end + + File.expand_path(load_path, root_dir) + end + self end diff --git a/bridgetown-core/lib/bridgetown-core/plugin_manager.rb b/bridgetown-core/lib/bridgetown-core/plugin_manager.rb index 0463362db..e0d48ad0f 100644 --- a/bridgetown-core/lib/bridgetown-core/plugin_manager.rb +++ b/bridgetown-core/lib/bridgetown-core/plugin_manager.rb @@ -132,12 +132,16 @@ def require_plugin_files sorted_plugin_files.each do |plugin_file| self.class.add_registered_plugin plugin_file end + next if site.config[:plugins_use_zeitwerk] + Bridgetown::Utils::RequireGems.require_with_graceful_fail(sorted_plugin_files) end end # Reload .rb plugin files via the watcher def reload_plugin_files + return if site.config[:plugins_use_zeitwerk] + plugins_path.each do |plugin_search_path| plugin_files = Utils.safe_glob(plugin_search_path, File.join("**", "*.rb")) Array(plugin_files).each do |name| diff --git a/bridgetown-core/lib/bridgetown-core/rack/boot.rb b/bridgetown-core/lib/bridgetown-core/rack/boot.rb index a5d1dc94a..62b9898ba 100644 --- a/bridgetown-core/lib/bridgetown-core/rack/boot.rb +++ b/bridgetown-core/lib/bridgetown-core/rack/boot.rb @@ -74,12 +74,5 @@ def self.autoload_server_folder( # rubocop:todo Metrics/MethodLength loaders_manager.setup_loaders([server_folder]) end - - # loader = Zeitwerk::Loader.new - # loader.push_dir server_folder - # loader.enable_reloading unless ENV["BRIDGETOWN_ENV"] == "production" - # loader.setup - # loader.eager_load - # loader.do_not_eager_load(File.join(server_folder, "roda_app.rb")) end end diff --git a/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb b/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb index c804c16d4..3fab1f2a9 100644 --- a/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb +++ b/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb @@ -46,6 +46,7 @@ def setup_loaders(autoload_paths = []) loader.ignore(File.join(load_path, "**", "*.js.rb")) Bridgetown::Hooks.trigger :loader, :pre_setup, loader, load_path loader.setup + loader.eager_load if config.eager_load_paths.include?(load_path) Bridgetown::Hooks.trigger :loader, :post_setup, loader, load_path @loaders[load_path] = loader end @@ -53,8 +54,11 @@ def setup_loaders(autoload_paths = []) def reload_loaders @loaders.each do |load_path, loader| + next unless reloading_enabled?(load_path) + Bridgetown::Hooks.trigger :loader, :pre_reload, loader, load_path - loader.reload if reloading_enabled?(load_path) + loader.reload + loader.eager_load if config.eager_load_paths.include?(load_path) Bridgetown::Hooks.trigger :loader, :post_reload, loader, load_path end end From b0e0dea5c3961d1f5944a34f1b702d7daa570238 Mon Sep 17 00:00:00 2001 From: Jared White Date: Wed, 3 Nov 2021 09:23:24 -0700 Subject: [PATCH 04/12] Ensure test suite is passing --- bridgetown-core/features/hooks.feature | 136 +++++++++++------- bridgetown-core/lib/bridgetown-core/utils.rb | 8 +- .../bridgetown-core/utils/loaders_manager.rb | 2 +- bridgetown-core/test/helper.rb | 4 +- ...nsion.rb => testing_resource_extension.rb} | 10 +- bridgetown-core/test/test_configuration.rb | 47 +++--- bridgetown-core/test/test_kramdown.rb | 1 - bridgetown-core/test/test_layout_reader.rb | 4 +- bridgetown-core/test/test_resource.rb | 4 +- 9 files changed, 121 insertions(+), 95 deletions(-) rename bridgetown-core/test/resources/plugins/{resource_extension.rb => testing_resource_extension.rb} (58%) diff --git a/bridgetown-core/features/hooks.feature b/bridgetown-core/features/hooks.feature index 1a9b052ba..046144ed1 100644 --- a/bridgetown-core/features/hooks.feature +++ b/bridgetown-core/features/hooks.feature @@ -6,11 +6,13 @@ Feature: Hooks Given I have a plugins directory And I have a "plugins/ext.rb" file with content: """ - Bridgetown::Hooks.register :site, :after_reset do |site| - pg = Bridgetown::GeneratedPage.new(site, site.source, "/", "foo.html") - pg.content = "mytinypage" + module Ext + Bridgetown::Hooks.register :site, :after_reset do |site| + pg = Bridgetown::GeneratedPage.new(site, site.source, "/", "foo.html") + pg.content = "mytinypage" - site.generated_pages << pg + site.generated_pages << pg + end end """ When I run bridgetown build @@ -24,8 +26,10 @@ Feature: Hooks And I have a "page2.html" page that contains "page2" And I have a "plugins/ext.rb" file with content: """ - Bridgetown::Hooks.register :site, :post_read do |site| - site.collections.pages.resources.delete_if { |p| p.relative_path.basename.to_s == 'page1.html' } + module Ext + Bridgetown::Hooks.register :site, :post_read do |site| + site.collections.pages.resources.delete_if { |p| p.relative_path.basename.to_s == 'page1.html' } + end end """ When I run bridgetown build @@ -38,10 +42,12 @@ Feature: Hooks Given I have a plugins directory And I have a "plugins/ext.rb" file with content: """ - Bridgetown::Hooks.register :site, :post_write do |site| - firstpage = site.collections.pages.resources.first - content = File.read firstpage.destination.output_path - File.write(File.join(site.dest, 'firstpage.html'), content) + module Ext + Bridgetown::Hooks.register :site, :post_write do |site| + firstpage = site.collections.pages.resources.first + content = File.read firstpage.destination.output_path + File.write(File.join(site.dest, 'firstpage.html'), content) + end end """ And I have a "page1.html" page that contains "page1" @@ -55,8 +61,10 @@ Feature: Hooks And I have a "index.html" page that contains "WRAP ME" And I have a "plugins/ext.rb" file with content: """ - Bridgetown::Hooks.register :pages, :post_render do |page| - page.output = "{{{{{ #{page.output.chomp} }}}}}" + module Ext + Bridgetown::Hooks.register :pages, :post_render do |page| + page.output = "{{{{{ #{page.output.chomp} }}}}}" + end end """ When I run bridgetown build @@ -67,10 +75,12 @@ Feature: Hooks And I have a "index.html" page that contains "HELLO FROM A PAGE" And I have a "plugins/ext.rb" file with content: """ - Bridgetown::Hooks.register :pages, :post_write do |page| - require 'fileutils' - filename = page.destination.output_path - FileUtils.mv(filename, "#{filename}.moved") + module Ext + Bridgetown::Hooks.register :pages, :post_write do |page| + require 'fileutils' + filename = page.destination.output_path + FileUtils.mv(filename, "#{filename}.moved") + end end """ When I run bridgetown build @@ -80,9 +90,11 @@ Feature: Hooks Given I have a plugins directory And I have a "plugins/ext.rb" file with content: """ - Bridgetown::Hooks.register :posts, :post_init do |post| - post.data['harold'] = "content for entry1.".tr!('abcdefghijklmnopqrstuvwxyz', - 'nopqrstuvwxyzabcdefghijklm') + module Ext + Bridgetown::Hooks.register :posts, :post_init do |post| + post.data['harold'] = "content for entry1.".tr!('abcdefghijklmnopqrstuvwxyz', + 'nopqrstuvwxyzabcdefghijklm') + end end """ And I have a _posts directory @@ -100,11 +112,13 @@ Feature: Hooks """ # Add myvar = 'old' to posts before 2015-03-15, and myvar = 'new' for # others - Bridgetown::Hooks.register :posts, :pre_render do |post| - if post.date < Time.new(2015, 3, 15) - post.data.myvar = 'old' - else - post.data.myvar = 'new' + module Ext + Bridgetown::Hooks.register :posts, :pre_render do |post| + if post.date < Time.new(2015, 3, 15) + post.data.myvar = 'old' + else + post.data.myvar = 'new' + end end end """ @@ -122,8 +136,10 @@ Feature: Hooks And I have a "plugins/ext.rb" file with content: """ # Replace content after rendering - Bridgetown::Hooks.register :posts, :post_render do |post| - post.output.gsub! /42/, 'the answer to life, the universe and everything' + module Ext + Bridgetown::Hooks.register :posts, :post_render do |post| + post.output.gsub! /42/, 'the answer to life, the universe and everything' + end end """ And I have a _posts directory @@ -140,10 +156,12 @@ Feature: Hooks And I have a "plugins/ext.rb" file with content: """ # Log all post filesystem writes - Bridgetown::Hooks.register :posts, :post_write do |post| - filename = post.destination.output_path - open('output/post-build.log', 'a') do |f| - f.puts "Wrote #{filename} at #{Time.now}" + module Ext + Bridgetown::Hooks.register :posts, :post_write do |post| + filename = post.destination.output_path + open('output/post-build.log', 'a') do |f| + f.puts "Wrote #{filename} at #{Time.now}" + end end end """ @@ -160,8 +178,10 @@ Feature: Hooks Given I have a plugins directory And I have a "plugins/ext.rb" file with content: """ - Bridgetown::Hooks.register [:pages, :posts], :post_render do |owner| - owner.output = "{{{{{ #{owner.output.chomp} }}}}}" + module Ext + Bridgetown::Hooks.register [:pages, :posts], :post_render do |owner| + owner.output = "{{{{{ #{owner.output.chomp} }}}}}" + end end """ And I have a "index.html" page that contains "WRAP ME" @@ -177,21 +197,23 @@ Feature: Hooks Given I have a plugins directory And I have a "plugins/ext.rb" file with content: """ - Bridgetown::Hooks.register :pages, :post_render, priority: :normal do |owner| - # first normal runs second - owner.output = "1 #{owner.output.chomp}" - end - Bridgetown::Hooks.register :pages, :post_render, priority: :high do |owner| - # high runs first - owner.output = "2 #{owner.output.chomp}" - end - Bridgetown::Hooks.register :pages, :post_render do |owner| - # second normal runs third (normal is default) - owner.output = "3 #{owner.output.chomp}" - end - Bridgetown::Hooks.register :pages, :post_render, priority: :low do |owner| - # low runs last - owner.output = "4 #{owner.output.chomp}" + module Ext + Bridgetown::Hooks.register :pages, :post_render, priority: :normal do |owner| + # first normal runs second + owner.output = "1 #{owner.output.chomp}" + end + Bridgetown::Hooks.register :pages, :post_render, priority: :high do |owner| + # high runs first + owner.output = "2 #{owner.output.chomp}" + end + Bridgetown::Hooks.register :pages, :post_render do |owner| + # second normal runs third (normal is default) + owner.output = "3 #{owner.output.chomp}" + end + Bridgetown::Hooks.register :pages, :post_render, priority: :low do |owner| + # low runs last + owner.output = "4 #{owner.output.chomp}" + end end """ And I have a "index.html" page that contains "WRAP ME" @@ -202,8 +224,10 @@ Feature: Hooks Given I have a plugins directory And I have a "plugins/ext.rb" file with content: """ - Bridgetown::Hooks.register :resources, :pre_render do |doc, payload| - doc.data['text'] = doc.data['text'] << ' are belong to us' if doc.data['text'] + module Ext + Bridgetown::Hooks.register :resources, :pre_render do |doc, payload| + doc.data['text'] = doc.data['text'] << ' are belong to us' if doc.data['text'] + end end """ And I have a "bridgetown.config.yml" file that contains "collections: [ memes ]" @@ -229,8 +253,10 @@ Feature: Hooks Given I have a plugins directory And I have a "plugins/ext.rb" file with content: """ - Bridgetown::Hooks.register :resources, :post_render do |doc| - doc.output.gsub! /

/, '

' + module Ext + Bridgetown::Hooks.register :resources, :post_render do |doc| + doc.output.gsub! /

/, '

' + end end """ And I have a "bridgetown.config.yml" file with content: @@ -256,9 +282,11 @@ Feature: Hooks Given I have a plugins directory And I have a "plugins/ext.rb" file with content: """ - Bridgetown::Hooks.register :resources, :post_write do |doc| - open('output/document-build.log', 'a') do |f| - f.puts "Wrote document #{doc.collection.resources.index doc} at #{Time.now}" + module Ext + Bridgetown::Hooks.register :resources, :post_write do |doc| + open('output/document-build.log', 'a') do |f| + f.puts "Wrote document #{doc.collection.resources.index doc} at #{Time.now}" + end end end """ diff --git a/bridgetown-core/lib/bridgetown-core/utils.rb b/bridgetown-core/lib/bridgetown-core/utils.rb index 97cdab383..2b8a03fb8 100644 --- a/bridgetown-core/lib/bridgetown-core/utils.rb +++ b/bridgetown-core/lib/bridgetown-core/utils.rb @@ -121,15 +121,11 @@ def parse_date(input, msg = "Input could not be parsed.") # @return [Boolean] if the YAML front matter is present. # rubocop: disable Naming/PredicateName def has_yaml_header?(file) - File.open(file, "rb", &:readline).match? Bridgetown::FrontMatterImporter::YAML_HEADER - rescue EOFError - false + File.open(file, "rb", &:gets)&.match?(Bridgetown::FrontMatterImporter::YAML_HEADER) || false end def has_rbfm_header?(file) - File.open(file, "rb", &:readline).match? Bridgetown::FrontMatterImporter::RUBY_HEADER - rescue EOFError - false + File.open(file, "rb", &:gets)&.match?(Bridgetown::FrontMatterImporter::RUBY_HEADER) || false end # Determine whether the given content string contains Liquid Tags or Vaiables diff --git a/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb b/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb index 3fab1f2a9..9e5b469f5 100644 --- a/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb +++ b/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb @@ -28,7 +28,7 @@ def reloading_enabled?(load_path) load_path.start_with?(root_dir) && ENV["BRIDGETOWN_ENV"] != "production" end - def setup_loaders(autoload_paths = []) + def setup_loaders(autoload_paths = []) # rubocop:todo Metrics/CyclomaticComplexity (autoload_paths.presence || config.autoload_paths).each do |load_path| if @loaders.key?(load_path) raise "Zeitwerk loader already added for `#{load_path}'. Please check your config" diff --git a/bridgetown-core/test/helper.rb b/bridgetown-core/test/helper.rb index caf8491b4..88059a542 100644 --- a/bridgetown-core/test/helper.rb +++ b/bridgetown-core/test/helper.rb @@ -135,10 +135,10 @@ def fixture_site(overrides = {}) def resources_site(overrides = {}) overrides["content_engine"] = "resource" overrides["available_locales"] ||= %w[en fr] + overrides["plugins_dir"] = resources_root_dir("plugins") new_config = site_configuration(overrides) new_config.root_dir = resources_root_dir new_config.source = resources_root_dir("src") - new_config.plugins_dir = resources_root_dir("plugins") Bridgetown::Site.new new_config end @@ -159,6 +159,8 @@ def site_configuration(overrides = {}) full_overrides = Utils.deep_merge_hashes({ "destination" => dest_dir, "plugins_dir" => site_root_dir("plugins"), }, overrides) + full_overrides["plugins_use_zeitwerk"] = false if overrides["plugins_use_zeitwerk"].nil? + Configuration.from(full_overrides.merge( "root_dir" => site_root_dir, "source" => source_dir diff --git a/bridgetown-core/test/resources/plugins/resource_extension.rb b/bridgetown-core/test/resources/plugins/testing_resource_extension.rb similarity index 58% rename from bridgetown-core/test/resources/plugins/resource_extension.rb rename to bridgetown-core/test/resources/plugins/testing_resource_extension.rb index 6a116e160..daaa1d73a 100644 --- a/bridgetown-core/test/resources/plugins/resource_extension.rb +++ b/bridgetown-core/test/resources/plugins/testing_resource_extension.rb @@ -1,26 +1,26 @@ # frozen_string_literal: true -module TestResourceExtension +module TestingResourceExtension def self.return_string "return value" end module LiquidResource def heres_a_liquid_method - "Liquid #{TestResourceExtension.return_string}" + "Liquid #{TestingResourceExtension.return_string}" end end module RubyResource def heres_a_method(arg = nil) - "Ruby #{TestResourceExtension.return_string}! #{arg}" + "Ruby #{TestingResourceExtension.return_string}! #{arg}" end end end -Bridgetown::Resource.register_extension TestResourceExtension +Bridgetown::Resource.register_extension TestingResourceExtension -module TestSummaryService +module TestingSummaryService module RubyResource def summary_extension_output "SUMMARY! #{content.strip[0..10]} DONE" diff --git a/bridgetown-core/test/test_configuration.rb b/bridgetown-core/test/test_configuration.rb index 274566595..07d696a9d 100644 --- a/bridgetown-core/test/test_configuration.rb +++ b/bridgetown-core/test/test_configuration.rb @@ -4,12 +4,21 @@ require "colorator" class TestConfiguration < BridgetownUnitTest - test_config = { - "root_dir" => site_root_dir, - "plugins_dir" => site_root_dir("plugins"), - "source" => source_dir, - "destination" => dest_dir, - } + def test_config + @test_config ||= { + "root_dir" => site_root_dir, + "plugins_dir" => site_root_dir("plugins"), + "source" => source_dir, + "destination" => dest_dir, + } + end + + def default_config_fixture(overrides = {}) + Bridgetown.configuration(test_config.merge(overrides)).tap do |config| + config.autoload_paths = config.eager_load_paths = [] + config.plugins_use_zeitwerk = false + end + end context ".from" do should "create a Configuration object" do @@ -284,13 +293,13 @@ class TestConfiguration < BridgetownUnitTest allow($stderr).to receive(:puts).with( Colorator.yellow("Configuration file: none") ) - assert_equal site_configuration, Bridgetown.configuration(test_config) + assert_equal site_configuration, default_config_fixture end should "load configuration as hash" do allow(Bridgetown::YAMLParser).to receive(:load_file).with(@path).and_return({}) allow($stdout).to receive(:puts).with("Configuration file: #{@path}") - assert_equal site_configuration, Bridgetown.configuration(test_config) + assert_equal site_configuration, default_config_fixture end should "fire warning with bad config" do @@ -304,7 +313,7 @@ class TestConfiguration < BridgetownUnitTest allow($stderr) .to receive(:puts) .and_return(Colorator.yellow("Configuration file: (INVALID) #{@path}")) - assert_equal site_configuration, Bridgetown.configuration(test_config) + assert_equal site_configuration, default_config_fixture end should "fire warning when user-specified config file isn't there" do @@ -336,7 +345,7 @@ class TestConfiguration < BridgetownUnitTest should "load default plus posts config if no config_file is set" do allow(Bridgetown::YAMLParser).to receive(:load_file).with(@paths[:default]).and_return({}) allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}") - assert_equal site_configuration, Bridgetown.configuration(test_config) + assert_equal site_configuration, default_config_fixture end should "load different config if specified" do @@ -350,7 +359,7 @@ class TestConfiguration < BridgetownUnitTest "baseurl" => "http://example.com", "config" => @paths[:other] ), - Bridgetown.configuration(test_config.merge("config" => @paths[:other])) + default_config_fixture({ "config" => @paths[:other] }) end should "load different config if specified with symbol key" do @@ -365,7 +374,7 @@ class TestConfiguration < BridgetownUnitTest "baseurl" => "http://example.com", "config" => @paths[:other] ), - Bridgetown.configuration(test_config.merge(config: @paths[:other])) + default_config_fixture({ config: @paths[:other] }) end should "load default config if path passed is empty" do @@ -373,7 +382,7 @@ class TestConfiguration < BridgetownUnitTest allow($stdout).to receive(:puts).with("Configuration file: #{@paths[:default]}") assert_equal \ site_configuration("config" => [@paths[:empty]]), - Bridgetown.configuration(test_config.merge("config" => [@paths[:empty]])) + default_config_fixture({ "config" => [@paths[:empty]] }) end should "successfully load a TOML file" do @@ -384,7 +393,7 @@ class TestConfiguration < BridgetownUnitTest "title" => "My magnificent site, wut", "config" => [@paths[:toml]] ), - Bridgetown.configuration(test_config.merge("config" => [@paths[:toml]])) + default_config_fixture({ "config" => [@paths[:toml]] }) Bridgetown.logger.log_level = :info end @@ -401,11 +410,7 @@ class TestConfiguration < BridgetownUnitTest site_configuration( "config" => [@paths[:default], @paths[:other], @paths[:toml]] ), - Bridgetown.configuration( - test_config.merge( - "config" => [@paths[:default], @paths[:other], @paths[:toml]] - ) - ) + default_config_fixture({ "config" => [@paths[:default], @paths[:other], @paths[:toml]] }) ) end @@ -429,9 +434,7 @@ class TestConfiguration < BridgetownUnitTest "baseurl" => "http://example.com", "config" => [@paths[:default], @paths[:other]] ), - Bridgetown.configuration( - test_config.merge("config" => [@paths[:default], @paths[:other]]) - ) + default_config_fixture({ "config" => [@paths[:default], @paths[:other]] }) end end diff --git a/bridgetown-core/test/test_kramdown.rb b/bridgetown-core/test/test_kramdown.rb index 902180d33..73b12d58c 100644 --- a/bridgetown-core/test/test_kramdown.rb +++ b/bridgetown-core/test/test_kramdown.rb @@ -44,7 +44,6 @@ def fixture_converter(config) @syntax_highlighter_opts_config_keys = \ @config["kramdown"]["syntax_highlighter_opts"].keys - @config = Bridgetown.configuration(@config) @converter = fixture_converter(@config) end diff --git a/bridgetown-core/test/test_layout_reader.rb b/bridgetown-core/test/test_layout_reader.rb index d32778578..e8689e495 100644 --- a/bridgetown-core/test/test_layout_reader.rb +++ b/bridgetown-core/test/test_layout_reader.rb @@ -5,9 +5,7 @@ class TestLayoutReader < BridgetownUnitTest context "reading layouts" do setup do - config = Bridgetown::Configuration::DEFAULTS.merge("source" => source_dir, - "destination" => dest_dir) - @site = fixture_site(config) + @site = fixture_site end should "read layouts" do diff --git a/bridgetown-core/test/test_resource.rb b/bridgetown-core/test/test_resource.rb index 19dae7872..be2b33881 100644 --- a/bridgetown-core/test/test_resource.rb +++ b/bridgetown-core/test/test_resource.rb @@ -387,13 +387,13 @@ class TestResource < BridgetownUnitTest should "work in a Ruby template" do assert_equal "That's **great**!", @resource.summary - @resource.singleton_class.include TestSummaryService::RubyResource + @resource.singleton_class.include TestingSummaryService::RubyResource assert_equal "SUMMARY! That's **gr DONE", @resource.summary end should "work in a Liquid template" do - @resource.singleton_class.include TestSummaryService::RubyResource + @resource.singleton_class.include TestingSummaryService::RubyResource @site.render assert_includes @resource.output, "Summary: :SUMMARY! That’s **gr DONE:" From 9b697317cb59ca89e909191b94f47be2944f3c54 Mon Sep 17 00:00:00 2001 From: Jared White Date: Wed, 3 Nov 2021 09:35:18 -0700 Subject: [PATCH 05/12] Ensure website and various configuration automations are Zeitwerk compatible --- .../configurations/minitesting.rb | 34 +++++++++++-------- .../configurations/purgecss.rb | 4 +-- .../plugins/builders/edge_versions.rb | 2 +- .../{purgecss_builder.rb => purgecss.rb} | 8 +++-- bridgetown-website/plugins/builders/tags.rb | 4 +-- bridgetown-website/plugins/test_output.rb | 17 ++++++---- bridgetown-website/plugins/upcaser.rb | 17 ---------- bridgetown-website/plugins/url_filters.rb | 9 ----- 8 files changed, 39 insertions(+), 56 deletions(-) rename bridgetown-website/plugins/builders/{purgecss_builder.rb => purgecss.rb} (78%) delete mode 100644 bridgetown-website/plugins/upcaser.rb delete mode 100644 bridgetown-website/plugins/url_filters.rb diff --git a/bridgetown-core/lib/bridgetown-core/configurations/minitesting.rb b/bridgetown-core/lib/bridgetown-core/configurations/minitesting.rb index efe7ce40f..2c4f4ad7c 100644 --- a/bridgetown-core/lib/bridgetown-core/configurations/minitesting.rb +++ b/bridgetown-core/lib/bridgetown-core/configurations/minitesting.rb @@ -2,7 +2,7 @@ # rubocop:disable all -say_status :minitesting, "Adding test gems, package.json scripts, and examples" +say_status :minitesting, "Adding test gems and examples" append_to_file "Gemfile" do <<~GEMS @@ -18,12 +18,6 @@ GEMS end -new_scripts = ' "test": "BRIDGETOWN_ENV=test yarn build",' -new_scripts += "\n" + ' "deploy:test": "bundle install --with test && yarn deploy"' -package_json = "package.json" -script_regex = %r{"scripts": \{(\s+".*,?)*} -inject_into_file(package_json, ",\n" + new_scripts, after: script_regex) - create_file "test/helper.rb" do <<~RUBY require "nokogiri" @@ -32,23 +26,29 @@ require "minitest/profile" require "shoulda" require "rails-dom-testing" + # Report with color. Minitest::Reporters.use! [ Minitest::Reporters::DefaultReporter.new( color: true ), ] + Minitest::Test.class_eval do include Rails::Dom::Testing::Assertions + def site @site ||= Bridgetown.sites.first end + def nokogiri(input) input.respond_to?(:output) ? Nokogiri::HTML(input.output) : Nokogiri::HTML(input) end + def document_root(root) @document_root = root.is_a?(Nokogiri::XML::Document) ? root : nokogiri(root) end + def document_root_element if @document_root.nil? raise "Call `document_root' with a Nokogiri document before testing your assertions" @@ -62,12 +62,14 @@ def document_root_element create_file "test/test_homepage.rb" do <<~RUBY require_relative "./helper" + class TestHomepage < Minitest::Test context "homepage" do setup do page = site.collections.pages.resources.find { |doc| doc.relative_url == "/" } document_root page end + should "exist" do assert_select "body" end @@ -78,18 +80,20 @@ class TestHomepage < Minitest::Test create_file "plugins/test_output.rb" do <<~RUBY - unless Bridgetown.environment == "development" - Bridgetown::Hooks.register :site, :post_write do - # Load test suite to run on exit - require "nokogiri" - Dir["test/**/*.rb"].each { |file| require_relative("../\#{file}") } - rescue LoadError - Bridgetown.logger.warn "Testing:", "To run tests, you must first run \`bundle install --with test\`" + module TestOutput + unless Bridgetown.env.development? + Bridgetown::Hooks.register :site, :post_write do + # Load test suite to run on exit + require "nokogiri" + Dir["test/**/*.rb"].each { |file| require_relative("../\#{file}") } + rescue LoadError + Bridgetown.logger.warn "Testing:", "To run tests, you must first run \`bundle install --with test\`" + end end end RUBY end -say_status :minitesting, "All set! To get started, look at test/test_homepage.rb and then run \`yarn test\`" +say_status :minitesting, "All set! To get started, look at test/test_homepage.rb and then run \`bin/bridgetown test\`" # rubocop:enable all \ No newline at end of file diff --git a/bridgetown-core/lib/bridgetown-core/configurations/purgecss.rb b/bridgetown-core/lib/bridgetown-core/configurations/purgecss.rb index 3c6566e8a..3abd41eca 100644 --- a/bridgetown-core/lib/bridgetown-core/configurations/purgecss.rb +++ b/bridgetown-core/lib/bridgetown-core/configurations/purgecss.rb @@ -6,9 +6,9 @@ run "yarn add -D purgecss" -create_builder "purgecss_builder.rb" do +create_builder "purgecss.rb" do <<~RUBY - class PurgeCSS < SiteBuilder + class Builders::Purgecss < SiteBuilder def build unless config[:watch] # don't run in "watch mode" hook :site, :post_write do diff --git a/bridgetown-website/plugins/builders/edge_versions.rb b/bridgetown-website/plugins/builders/edge_versions.rb index a971fe5e2..0bb40104f 100644 --- a/bridgetown-website/plugins/builders/edge_versions.rb +++ b/bridgetown-website/plugins/builders/edge_versions.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class EdgeVersions < SiteBuilder +class Builders::EdgeVersions < SiteBuilder def build hook :site, :post_read do if Bridgetown::VERSION.include?("alpha") || Bridgetown::VERSION.include?("beta") diff --git a/bridgetown-website/plugins/builders/purgecss_builder.rb b/bridgetown-website/plugins/builders/purgecss.rb similarity index 78% rename from bridgetown-website/plugins/builders/purgecss_builder.rb rename to bridgetown-website/plugins/builders/purgecss.rb index d23acffa0..fd239adb6 100644 --- a/bridgetown-website/plugins/builders/purgecss_builder.rb +++ b/bridgetown-website/plugins/builders/purgecss.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -class PurgeCSS < SiteBuilder +class Builders::Purgecss < SiteBuilder def build unless config[:watch] # don't run in "watch mode" hook :site, :post_write do @@ -24,9 +24,11 @@ def build system "./node_modules/.bin/purgecss -c purgecss.config.js -css #{css_path}" newsize = File.stat(css_path).size / 1000 if newsize < oldsize - Bridgetown.logger.info "PurgeCSS", "Done! File size reduced from #{oldsize}kB to #{newsize}kB" + Bridgetown.logger.info "PurgeCSS", + "Done! File size reduced from #{oldsize}kB to #{newsize}kB" else - Bridgetown.logger.info "PurgeCSS", "Done. No apparent change in file size (#{newsize}kB)." + Bridgetown.logger.info "PurgeCSS", + "Done. No apparent change in file size (#{newsize}kB)." end end end diff --git a/bridgetown-website/plugins/builders/tags.rb b/bridgetown-website/plugins/builders/tags.rb index d3d7e9d68..4889455c9 100644 --- a/bridgetown-website/plugins/builders/tags.rb +++ b/bridgetown-website/plugins/builders/tags.rb @@ -1,12 +1,12 @@ # frozen_string_literal: true -class TagsBuilder < SiteBuilder +class Builders::Tags < SiteBuilder def build liquid_tag "toc", :toc_template helper "toc", :toc_template end - def toc_template(attributes=nil, tag=nil) + def toc_template(_attributes = nil, _tag = nil) <<~TAG ## Table of Contents {:.no_toc} diff --git a/bridgetown-website/plugins/test_output.rb b/bridgetown-website/plugins/test_output.rb index 032bc19db..7f322966d 100644 --- a/bridgetown-website/plugins/test_output.rb +++ b/bridgetown-website/plugins/test_output.rb @@ -1,11 +1,14 @@ # frozen_string_literal: true -unless Bridgetown.environment == "development" - Bridgetown::Hooks.register :site, :post_write do - # Load test suite to run on exit - require "nokogiri" - Dir["test/**/*.rb"].each { |file| require_relative("../#{file}") } - rescue LoadError - Bridgetown.logger.warn "Testing:", "To run tests, you must first run `bundle install --with test`" +module TestOutput + unless Bridgetown.env.development? + Bridgetown::Hooks.register :site, :post_write do + # Load test suite to run on exit + require "nokogiri" + Dir["test/**/*.rb"].each { |file| require_relative("../#{file}") } + rescue LoadError + Bridgetown.logger.warn "Testing:", + "To run tests, you must first run `bundle install --with test`" + end end end diff --git a/bridgetown-website/plugins/upcaser.rb b/bridgetown-website/plugins/upcaser.rb deleted file mode 100644 index 22484d456..000000000 --- a/bridgetown-website/plugins/upcaser.rb +++ /dev/null @@ -1,17 +0,0 @@ -module MySite - class UpcaseConverter < Bridgetown::Converter - priority :low - - def matches(ext) - ext =~ /^\.upcase$/i - end - - def output_ext(ext) - ".html" - end - - def convert(content) - content.upcase - end - end -end diff --git a/bridgetown-website/plugins/url_filters.rb b/bridgetown-website/plugins/url_filters.rb deleted file mode 100644 index d2ef1e3d8..000000000 --- a/bridgetown-website/plugins/url_filters.rb +++ /dev/null @@ -1,9 +0,0 @@ -module MySite - module UrlFilters - def cache_busting_url(input) - "http://www.example.com/#{input}?#{Time.now.to_i}" - end - end -end - -Liquid::Template.register_filter(MySite::UrlFilters) \ No newline at end of file From aa9eb7fd0dcf3adabdaa6b93b068b20cd3dbea51 Mon Sep 17 00:00:00 2001 From: Jared White Date: Wed, 3 Nov 2021 09:44:06 -0700 Subject: [PATCH 06/12] Builder gem tests: ensure Zeitwerk unloads the loaders --- bridgetown-builder/test/test_generators.rb | 1 + bridgetown-builder/test/test_hooks.rb | 2 ++ bridgetown-builder/test/test_method_symbols.rb | 1 + 3 files changed, 4 insertions(+) diff --git a/bridgetown-builder/test/test_generators.rb b/bridgetown-builder/test/test_generators.rb index 7d9b0bf88..f03416dc5 100644 --- a/bridgetown-builder/test/test_generators.rb +++ b/bridgetown-builder/test/test_generators.rb @@ -21,6 +21,7 @@ class TestGenerators < BridgetownUnitTest should "be loaded on site setup" do @site.reset @site.data[:site_metadata] = { title: "Initial Value" } + @site.loaders_manager.unload_loaders @site.setup @site.generate diff --git a/bridgetown-builder/test/test_hooks.rb b/bridgetown-builder/test/test_hooks.rb index 3953006d5..3a60ae0d4 100644 --- a/bridgetown-builder/test/test_hooks.rb +++ b/bridgetown-builder/test/test_hooks.rb @@ -42,6 +42,7 @@ class TestHooks < BridgetownUnitTest should "be triggered" do @site.reset + @site.loaders_manager.unload_loaders @site.setup Bridgetown::Hooks.trigger :site, :pre_read, @site @@ -69,6 +70,7 @@ class TestHooks < BridgetownUnitTest should "be loaded" do @site.reset + @site.loaders_manager.unload_loaders @site.setup Bridgetown::Hooks.trigger :site, :pre_read, @site diff --git a/bridgetown-builder/test/test_method_symbols.rb b/bridgetown-builder/test/test_method_symbols.rb index 7d26d49c7..5ed4511ea 100644 --- a/bridgetown-builder/test/test_method_symbols.rb +++ b/bridgetown-builder/test/test_method_symbols.rb @@ -43,6 +43,7 @@ class TestMethodSymbols < BridgetownUnitTest should "load generator on site generate" do @site.reset @site.data[:site_metadata] = { title: "Initial Value in Method Symbols" } + @site.loaders_manager.unload_loaders @site.setup assert_equal "Initial Value in Method Symbols", @site.metadata[:title] From 5fff08830fab424defb6bb33272229cf411af184 Mon Sep 17 00:00:00 2001 From: Jared White Date: Wed, 3 Nov 2021 10:02:51 -0700 Subject: [PATCH 07/12] Write Zeitwerk documentation --- bridgetown-website/src/_docs/plugins.md | 88 ++++++++++++++++--------- 1 file changed, 56 insertions(+), 32 deletions(-) diff --git a/bridgetown-website/src/_docs/plugins.md b/bridgetown-website/src/_docs/plugins.md index bd4125837..d239e84da 100644 --- a/bridgetown-website/src/_docs/plugins.md +++ b/bridgetown-website/src/_docs/plugins.md @@ -7,7 +7,7 @@ category: plugins --- Plugins allow you to extend Bridgetown's behavior to fit your needs. You can -write plugins yourself directly in your website codebase, or install gem-based +write plugins yourself directly in your website codebase, or install plugin-based plugins and [themes](/docs/themes) for a limitless source of new features and capabilities. @@ -15,15 +15,15 @@ Be sure to [check out our growing list of official and third-party plugins](/plugins/) for ways to jazz up your website. -Whenever you need more information about the plugins installed on your site and what they're doing, you can use the `bridgetown plugins list` command. You can also copy content out of gem-based plugins with the `bridgetown plugins cd` command. [Read the command reference for further details.](/docs/commands/plugins) +Whenever you need more information about the plugins installed on your site and what they're doing, you can use the `bridgetown plugins list` command. You can also copy content out of plugin-based plugins with the `bridgetown plugins cd` command. [Read the command reference for further details.](/docs/commands/plugins) {% rendercontent "docs/note", title: "Turn Your Plugins into Gems", extra_margin: true %} If you'd like to maintain plugin separation from your site source code, share functionality across multiple projects, and manage dependencies, -you can create a Ruby gem for private or public distribution. This is also +you can create a Ruby plugin for private or public distribution. This is also how you'd create a [Bridgetown theme](/docs/themes). -[Read further instructions below on how to create and publish a gem.](#creating-a-gem){:data-no-swup="true"} +[Read further instructions below on how to create and publish a plugin.](#creating-a-plugin){:data-no-swup="true"} {% endrendercontent %} {% toc %} @@ -34,7 +34,7 @@ There are three methods of adding plugins to your site build. 1. In your site's root folder (aka where your config file lives), make a `plugins` folder. Write your custom plugins and save them here. Any file ending in `.rb` inside this folder will be loaded automatically before Bridgetown generates your site. -2. Add gem-based plugins to the `bridgetown_plugins` Bundler group in your `Gemfile` by running a command such as: +2. Add plugin-based plugins to the `bridgetown_plugins` Bundler group in your `Gemfile` by running a command such as: ```sh bundle add bridgetown-feed -g bridgetown_plugins ``` @@ -44,26 +44,18 @@ plugins along with other set up and configuration: ```sh bin/bridgetown apply https://github.com/bridgetownrb/bridgetown-cloudinary ``` - + ## Introduction to the Builder API -**_New_** in Bridgetown 0.14 is the Builder API (also sometimes referred to as the Unified Plugins API). This is a brand-new way of writing plugins for both custom plugins as well as gem-based plugins. Most previous techniques of writing plugins (registering Liquid tags and filters, generators, etc.) have been rebranded as the Legacy API. This API isn't going away any time soon as it provides the underlying functionality for the Builder API. However, we recommend all new plugin development center around the Builder API going forward. +The Builder API (also sometimes referred to as the Unified Plugins API) is our preferred method of writing plugins for both custom plugins as well as plugin-based plugins. Previous techniques of writing plugins (registering Liquid tags and filters, generators, etc.) are known as the Legacy API. This API isn't going away any time soon as it provides the underlying functionality for the Builder API. However, we recommend all new plugin development center around the Builder API going forward. ### Local Custom Plugins -For local plugins, simply create a new `SiteBuilder` class in your `plugins` folder: - -```ruby -# plugins/site_builder.rb -class SiteBuilder < Bridgetown::Builder -end -``` - -Then in `plugins/builders`, you can create one or more subclasses of `SiteBuilder` and write your plugin code within the `build` method which is called automatically by Bridgetown early on in the build process (specifically during the `pre_read` event before content has been loaded from the file system). +The `SiteBuilder` class in your `plugins` folder provides the a superclass you can inherit from to create a new builder. In `plugins/builders`, you can create one or more subclasses of `SiteBuilder` and write your plugin code within the `build` method which is called automatically by Bridgetown early on in the build process (specifically during the `pre_read` event before content has been loaded from the file system). ```ruby # plugins/builders/add_some_tags.rb -class AddSomeTags < SiteBuilder +class Builders::AddSomeTags < SiteBuilder def build liquid_tag "cool_stuff", :cool_tag end @@ -79,7 +71,7 @@ Builders provide a couple of instance methods you can use to reference important So for example you could add data with a generator: ```ruby -class AddNewData < SiteBuilder +class Builders::AddNewData < SiteBuilder def build generator do site.data[:new_data] = {new: "New stuff"} @@ -101,7 +93,7 @@ And then reference that data in any Liquid template: The `config` instance method is available to access the Bridgetown site configuration object, and along with that you can optionally define a default configuration that will be included in the config object—and can be overridden by config settings directly in `bridgetown.config.yml`. For example: ```ruby -class BuilderWithConfiguration < SiteBuilder +class Builders::BuilderWithConfiguration < SiteBuilder CONFIG_DEFAULTS = { custom_config: { my_setting: 123 @@ -122,7 +114,7 @@ end ### Gem-based Plugins -For a gem-based plugin, all you have to do is subclass directly from `Bridgetown::Builder` and then use the `register` class method to register the builder with Bridgetown when the gem loads. Example: +For a plugin-based plugin, all you have to do is subclass directly from `Bridgetown::Builder` and then use the `register` class method to register the builder with Bridgetown when the plugin loads. Example: ```ruby module Bridgetown @@ -145,7 +137,7 @@ end Bridgetown::MyNiftyPlugin::Builder.register ``` -[Read further instructions below on how to create and publish a gem.](#creating-a-gem){:data-no-swup="true"} +[Read further instructions below on how to create and publish a plugin.](#creating-a-plugin){:data-no-swup="true"} ## Internal Ruby API @@ -210,29 +202,61 @@ module MySite end ``` +## Zeitwerk and Autoloading + +Bridgetown 1.0 brings with it a new autoloading mechanism using [Zeitwerk](https://github.com/fxn/zeitwerk), the same code loader used by Rails and many other Ruby-based projects. Zeitwerk uses a specific naming convension so the paths of your Ruby files and the namespaces/modules/classes of your Ruby code are aligned. For example: + +``` +plugins/my_plugin.rb -> MyPlugin +plugins/my_plugin/foo.rb -> MyPlugin::Foo +plugins/my_plugin/bar_baz.rb -> MyPlugin::BarBaz +plugins/my_plugin/woo/zoo.rb -> MyPlugin::Woo::Zoo +``` + +You can read more about [Zeitwerk's file conventions here](https://github.com/fxn/zeitwerk#file-structure). + +{% rendercontent "docs/note", title: "Take Me Back" %} + If you run into any problems with Zeitwerk after upgrading your Bridgetown project from pre-1.0, you can switch to the previous plugin loading method by adding `plugins_use_zeitwerk: false` to your `bridgetown.config.yml`. +{% endrendercontent %} + +In addition to the `plugins` folder provided by default, **you can add your own folders** with autoloading support! Simply add to the `autoload_paths` key in your config YAML: + +```yaml +autoload_paths: + - loadme +``` + +Now any Ruby file in your project's `./loadme` folder will be autoloaded. By default, files in your custom folders not "eager loaded", meaning that the Ruby code isn't actually processed unless/until you access the class or module name of the file somewhere in your code elsewhere. This can improve performance in certain cases. However, if you need to rely on the fact that your Ruby code is always loaded when the site is instantiated, simply set `eager` to true in your config: + +```yaml +autoload_paths: + - path: loadme + eager: true +``` + ## Creating a Gem -The `bridgetown plugins new NAME` command will create an entire gem scaffold -for you to customize and publish to the [RubyGems.org](https://rubygems.org) +The `bridgetown plugins new NAME` command will create an entire plugin scaffold +for you to customize and publish to the [RubyGems.org](https://rubyplugins.org) and [NPM](https://www.npmjs.com) registries. This is a great way to provide [themes](/docs/themes), builders, and other sorts of add-on functionality to -Bridgetown websites. You'll want to make sure you update the `gemspec`, +Bridgetown websites. You'll want to make sure you update the `pluginspec`, `package.json`, `README.md`, and `CHANGELOG.md` files as you work on your plugin to ensure all the necessary metadata and user documentation is present and accounted for. -Make sure you [follow these instructions](/docs/plugins/gems-and-webpack/) to +Make sure you [follow these instructions](/docs/plugins/plugins-and-webpack/) to integrate your plugin's frontend code with the users' Webpack setup. Also read up on [Source Manifests](/docs/plugins/source-manifests/) if you have layouts, -components, pages, static files, and other content you would like your gem to +components, pages, static files, and other content you would like your plugin to provide. -You can also provide an automation via your gem's GitHub repository by adding +You can also provide an automation via your plugin's GitHub repository by adding `bridgetown.automation.rb` to the root of your repo. This is a great way to provide advanced and interactive setup for your plugin. [More information on automations here.](/docs/automations) -When you're ready, publish your plugin to the [RubyGems.org](https://rubygems.org) +When you're ready, publish your plugin to the [RubyGems.org](https://rubyplugins.org) and [NPM](https://www.npmjs.com) registries. There are instructions on how to do so in the sample README that is present in your new plugin folder under the heading **Releasing**. Of course you will also need to make sure you've uploaded @@ -241,7 +265,7 @@ your plugin to [GitHub](https://github.com) so it can be included in our wide. Plus it's a great way to solicit feedback and improvements in the form of open source code collaboration and discussion. -As always, if you have any questions or need support in creating your gem, +As always, if you have any questions or need support in creating your plugin, [check out our community resources](/docs/community). {% rendercontent "docs/note", title: "Testing Your Plugin" %} @@ -250,7 +274,7 @@ Bridgetown site. The easiest way to do that is to use a relative local path in the test site's `Gemfile`. ```ruby -gem "my-plugin", :path => "../my-plugin", :group => :bridgetown_plugins +plugin "my-plugin", :path => "../my-plugin", :group => :bridgetown_plugins ``` You would do something similar in your test site's `package.json` as well (be sure to run [yarn link](https://classic.yarnpkg.com/en/docs/cli/link) so Yarn knows not to install your local path into `node_modules`): @@ -263,10 +287,10 @@ You would do something similar in your test site's `package.json` as well (be su ``` You may need to restart your server at times to pick up changes you make -to your gem (unfortunately hot-reload doesn't always work with gem-based plugins). +to your plugin (unfortunately hot-reload doesn't always work with plugin-based plugins). Finally, you should try writing some [RSpec tests](https://relishapp.com/rspec) -in the `spec` folder of your gem. These tests could ensure your tags, filters, +in the `spec` folder of your plugin. These tests could ensure your tags, filters, and other content are working as expected and won't break in the future as code gets updated. {% endrendercontent %} From 7efe017f9f562b9cf267cce329d7af7750373595 Mon Sep 17 00:00:00 2001 From: Jared White Date: Wed, 3 Nov 2021 20:53:15 -0700 Subject: [PATCH 08/12] Simplify/refactor how model subclasses are found for init --- .../lib/bridgetown-core/model/base.rb | 49 +++++++++---------- .../bridgetown-core/model/builder_origin.rb | 14 +++--- .../lib/bridgetown-core/model/origin.rb | 11 ++++- 3 files changed, 41 insertions(+), 33 deletions(-) diff --git a/bridgetown-core/lib/bridgetown-core/model/base.rb b/bridgetown-core/lib/bridgetown-core/model/base.rb index 66ccb49b1..f614ceff5 100644 --- a/bridgetown-core/lib/bridgetown-core/model/base.rb +++ b/bridgetown-core/lib/bridgetown-core/model/base.rb @@ -9,40 +9,37 @@ class Base extend ActiveModel::Callbacks # also extends with DescendantsTracker define_model_callbacks :load, :save, :destroy - def self.loads_id?(id) - name == ActiveSupport::Inflector.classify( - URI.parse(id).host.chomp(".collection") - ) - end + class << self + def find(id) + unless Bridgetown::Current.site + raise "A Bridgetown site must be initialized and added to Current" + end - def self.find(id) - unless Bridgetown::Current.site - raise "A Bridgetown site must be initialized and added to Current" + origin = origin_for_id(id) + klass_for_id(id, origin: origin).new(origin.read) end - model_klass = klass_for_id(id) - model_klass.new(read_data_for_id(id)) - end - - def self.klass_for_id(id) - descendants.find do |klass| - klass.loads_id?(id) - end || self - end + def origin_for_id(id) + scheme = URI.parse(id).scheme + origin_klass = Origin.descendants.find do |klass| + klass.handle_scheme?(scheme) + end - def self.read_data_for_id(id) - origin_for_id(id).read - end + raise "No origin could be found for #{id}" unless origin_klass - def self.origin_for_id(id) - scheme = URI.parse(id).scheme - origin_klass = Origin.descendants.find do |klass| - klass.handle_scheme?(scheme) + origin_klass.new(id) end - raise "No origin could be found for #{id}" unless origin_klass + def klass_for_id(id, origin: nil) + Bridgetown::Model::Base.descendants.find do |klass| + klass.will_load_id?(id, origin: origin) + end || Bridgetown::Model::Base + end - origin_klass.new(id) + def will_load_id?(id, origin: nil) + origin ||= origin_for_id(id) + origin.verify_model?(self) + end end class << self diff --git a/bridgetown-core/lib/bridgetown-core/model/builder_origin.rb b/bridgetown-core/lib/bridgetown-core/model/builder_origin.rb index adaab1cff..1bd2fa432 100644 --- a/bridgetown-core/lib/bridgetown-core/model/builder_origin.rb +++ b/bridgetown-core/lib/bridgetown-core/model/builder_origin.rb @@ -6,12 +6,14 @@ class BuilderOrigin < Origin # @return [Pathname] attr_reader :relative_path - def self.handle_scheme?(scheme) - scheme == "builder" - end - - def self.id_for_builder_path(builder, path) - "builder://#{builder.class.name.gsub("::", ".")}/#{path}" + class << self + def handle_scheme?(scheme) + scheme == "builder" + end + + def id_for_builder_path(builder, path) + "builder://#{builder.class.name.gsub("::", ".")}/#{path}" + end end def initialize(id) diff --git a/bridgetown-core/lib/bridgetown-core/model/origin.rb b/bridgetown-core/lib/bridgetown-core/model/origin.rb index b66137a66..2e73b6490 100644 --- a/bridgetown-core/lib/bridgetown-core/model/origin.rb +++ b/bridgetown-core/lib/bridgetown-core/model/origin.rb @@ -11,7 +11,7 @@ class Origin # @return [String] attr_accessor :id - # Override in subclass + # You must implement in subclasses def self.handle_scheme?(_scheme) false end @@ -20,6 +20,15 @@ def initialize(id) self.id = id end + # You can override in subclass + def verify_model?(klass) + collection_name = URI.parse(id).host.chomp(".collection") + + return klass.collection_name.to_s == collection_name if klass.respond_to?(:collection_name) + + klass.name == ActiveSupport::Inflector.classify(collection_name) + end + def read raise "Implement #read in a subclass of Bridgetown::Model::Origin" end From d284140561c6c83cf0889ef04389f235a8a6bfce Mon Sep 17 00:00:00 2001 From: Jared White Date: Wed, 3 Nov 2021 21:03:28 -0700 Subject: [PATCH 09/12] Fix plugins documentation (put the "gem" back) --- bridgetown-website/src/_docs/plugins.md | 34 ++++++++++++------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/bridgetown-website/src/_docs/plugins.md b/bridgetown-website/src/_docs/plugins.md index d239e84da..70eb75146 100644 --- a/bridgetown-website/src/_docs/plugins.md +++ b/bridgetown-website/src/_docs/plugins.md @@ -7,7 +7,7 @@ category: plugins --- Plugins allow you to extend Bridgetown's behavior to fit your needs. You can -write plugins yourself directly in your website codebase, or install plugin-based +write plugins yourself directly in your website codebase, or install gem-based plugins and [themes](/docs/themes) for a limitless source of new features and capabilities. @@ -15,15 +15,15 @@ Be sure to [check out our growing list of official and third-party plugins](/plugins/) for ways to jazz up your website. -Whenever you need more information about the plugins installed on your site and what they're doing, you can use the `bridgetown plugins list` command. You can also copy content out of plugin-based plugins with the `bridgetown plugins cd` command. [Read the command reference for further details.](/docs/commands/plugins) +Whenever you need more information about the plugins installed on your site and what they're doing, you can use the `bridgetown plugins list` command. You can also copy content out of gem-based plugins with the `bridgetown plugins cd` command. [Read the command reference for further details.](/docs/commands/plugins) {% rendercontent "docs/note", title: "Turn Your Plugins into Gems", extra_margin: true %} If you'd like to maintain plugin separation from your site source code, share functionality across multiple projects, and manage dependencies, -you can create a Ruby plugin for private or public distribution. This is also +you can create a Ruby gem for private or public distribution. This is also how you'd create a [Bridgetown theme](/docs/themes). -[Read further instructions below on how to create and publish a plugin.](#creating-a-plugin){:data-no-swup="true"} +[Read further instructions below on how to create and publish a gem.](#creating-a-gem){:data-no-swup="true"} {% endrendercontent %} {% toc %} @@ -34,20 +34,20 @@ There are three methods of adding plugins to your site build. 1. In your site's root folder (aka where your config file lives), make a `plugins` folder. Write your custom plugins and save them here. Any file ending in `.rb` inside this folder will be loaded automatically before Bridgetown generates your site. -2. Add plugin-based plugins to the `bridgetown_plugins` Bundler group in your `Gemfile` by running a command such as: +2. Add gem-based plugins to the `bridgetown_plugins` Bundler group in your `Gemfile` by running a command such as: ```sh bundle add bridgetown-feed -g bridgetown_plugins ``` 3. Running an [automation](/docs/automations) which will install one or more -plugins along with other set up and configuration: +gems along with other set up and configuration: ```sh bin/bridgetown apply https://github.com/bridgetownrb/bridgetown-cloudinary ``` ## Introduction to the Builder API -The Builder API (also sometimes referred to as the Unified Plugins API) is our preferred method of writing plugins for both custom plugins as well as plugin-based plugins. Previous techniques of writing plugins (registering Liquid tags and filters, generators, etc.) are known as the Legacy API. This API isn't going away any time soon as it provides the underlying functionality for the Builder API. However, we recommend all new plugin development center around the Builder API going forward. +The Builder API (also sometimes referred to as the Unified Plugins API) is our preferred method of writing plugins for both custom plugins as well as gem-based plugins. Previous techniques of writing plugins (registering Liquid tags and filters, generators, etc.) are known as the Legacy API. This API isn't going away any time soon as it provides the underlying functionality for the Builder API. However, we recommend all new plugin development center around the Builder API going forward. ### Local Custom Plugins @@ -114,7 +114,7 @@ end ### Gem-based Plugins -For a plugin-based plugin, all you have to do is subclass directly from `Bridgetown::Builder` and then use the `register` class method to register the builder with Bridgetown when the plugin loads. Example: +For a gem-based plugin, all you have to do is subclass directly from `Bridgetown::Builder` and then use the `register` class method to register the builder with Bridgetown when the plugin loads. Example: ```ruby module Bridgetown @@ -137,7 +137,7 @@ end Bridgetown::MyNiftyPlugin::Builder.register ``` -[Read further instructions below on how to create and publish a plugin.](#creating-a-plugin){:data-no-swup="true"} +[Read further instructions below on how to create and publish a gem.](#creating-a-gem){:data-no-swup="true"} ## Internal Ruby API @@ -236,11 +236,11 @@ autoload_paths: ## Creating a Gem -The `bridgetown plugins new NAME` command will create an entire plugin scaffold +The `bridgetown plugins new NAME` command will create an entire gem scaffold for you to customize and publish to the [RubyGems.org](https://rubyplugins.org) and [NPM](https://www.npmjs.com) registries. This is a great way to provide [themes](/docs/themes), builders, and other sorts of add-on functionality to -Bridgetown websites. You'll want to make sure you update the `pluginspec`, +Bridgetown websites. You'll want to make sure you update the `gemspec`, `package.json`, `README.md`, and `CHANGELOG.md` files as you work on your plugin to ensure all the necessary metadata and user documentation is present and accounted for. @@ -256,7 +256,7 @@ You can also provide an automation via your plugin's GitHub repository by adding provide advanced and interactive setup for your plugin. [More information on automations here.](/docs/automations) -When you're ready, publish your plugin to the [RubyGems.org](https://rubyplugins.org) +When you're ready, publish your plugin gem to the [RubyGems.org](https://rubyplugins.org) and [NPM](https://www.npmjs.com) registries. There are instructions on how to do so in the sample README that is present in your new plugin folder under the heading **Releasing**. Of course you will also need to make sure you've uploaded @@ -269,12 +269,12 @@ As always, if you have any questions or need support in creating your plugin, [check out our community resources](/docs/community). {% rendercontent "docs/note", title: "Testing Your Plugin" %} -As you author your plugin, you'll need a way to _use_ the plugin within a live +As you author your plugin, you'll need a way to _use_ the gem within a live Bridgetown site. The easiest way to do that is to use a relative local path in the test site's `Gemfile`. ```ruby -plugin "my-plugin", :path => "../my-plugin", :group => :bridgetown_plugins +gem "my-plugin", :path => "../my-plugin", :group => :bridgetown_plugins ``` You would do something similar in your test site's `package.json` as well (be sure to run [yarn link](https://classic.yarnpkg.com/en/docs/cli/link) so Yarn knows not to install your local path into `node_modules`): @@ -287,10 +287,10 @@ You would do something similar in your test site's `package.json` as well (be su ``` You may need to restart your server at times to pick up changes you make -to your plugin (unfortunately hot-reload doesn't always work with plugin-based plugins). +to your plugin (unfortunately hot-reload doesn't always work with gem-based plugins). -Finally, you should try writing some [RSpec tests](https://relishapp.com/rspec) -in the `spec` folder of your plugin. These tests could ensure your tags, filters, +Finally, you should try writing some [tests](http://docs.seattlerb.org/minitest/) +in the `test` folder of your plugin. These tests could ensure your tags, filters, and other content are working as expected and won't break in the future as code gets updated. {% endrendercontent %} From b07b7ddcab09f5467f165185ff6ef6699febc133 Mon Sep 17 00:00:00 2001 From: Jared White Date: Thu, 4 Nov 2021 20:20:18 -0700 Subject: [PATCH 10/12] Add `loader_collapsed_paths` setting --- .../features/site_configuration.feature | 46 ++++++++++ bridgetown-core/features/support/helpers.rb | 1 + .../lib/bridgetown-core/configuration.rb | 89 ++++++++++--------- .../bridgetown-core/utils/loaders_manager.rb | 7 +- bridgetown-website/bridgetown.config.yml | 3 + .../plugins/{ => testing}/test_output.rb | 2 +- bridgetown-website/src/_docs/plugins.md | 27 +++++- 7 files changed, 129 insertions(+), 46 deletions(-) rename bridgetown-website/plugins/{ => testing}/test_output.rb (83%) diff --git a/bridgetown-core/features/site_configuration.feature b/bridgetown-core/features/site_configuration.feature index ae6ef7c7f..13e78a189 100644 --- a/bridgetown-core/features/site_configuration.feature +++ b/bridgetown-core/features/site_configuration.feature @@ -254,3 +254,49 @@ Feature: Site configuration And the output directory should exist And I should see "FOO" in "output/index.html" And I should not see " " in "output/index.html" + + Scenario: Allow collapsed Zeitwerk dirs with specific dir name + Given I have a "plugins/nested" directory + And I have a "plugins/nested/top_level.rb" file with content: + """ + module TopLevel + Bridgetown::Hooks.register :site, :after_reset do |site| + pg = Bridgetown::GeneratedPage.new(site, site.source, "/", "foo.html") + pg.content = "Zeitwerk specific dir" + + site.generated_pages << pg + end + end + """ + And I have a "bridgetown.config.yml" file with content: + """ + loader_collapsed_paths: + - plugins/nested + """ + When I run bridgetown build + Then I should get a zero exit status + And the output directory should exist + And I should see "Zeitwerk specific dir" in "output/foo/index.html" + + Scenario: Allow collapsed Zeitwerk dirs using globs + Given I have a "plugins/nested" directory + And I have a "plugins/nested/top_level.rb" file with content: + """ + module TopLevel + Bridgetown::Hooks.register :site, :after_reset do |site| + pg = Bridgetown::GeneratedPage.new(site, site.source, "/", "foo.html") + pg.content = "Zeitwerk glob dir" + + site.generated_pages << pg + end + end + """ + And I have a "bridgetown.config.yml" file with content: + """ + loader_collapsed_paths: + - plugins/* + """ + When I run bridgetown build + Then I should get a zero exit status + And the output directory should exist + And I should see "Zeitwerk glob dir" in "output/foo/index.html" diff --git a/bridgetown-core/features/support/helpers.rb b/bridgetown-core/features/support/helpers.rb index 166bdf430..3cf469151 100644 --- a/bridgetown-core/features/support/helpers.rb +++ b/bridgetown-core/features/support/helpers.rb @@ -37,6 +37,7 @@ def self.root_files ".bridgetown-webpack/manifest.json", "bridgetown.config.yml", "plugins", + "plugins/nested", "frontend", ] end diff --git a/bridgetown-core/lib/bridgetown-core/configuration.rb b/bridgetown-core/lib/bridgetown-core/configuration.rb index c478fab02..d941cfd90 100644 --- a/bridgetown-core/lib/bridgetown-core/configuration.rb +++ b/bridgetown-core/lib/bridgetown-core/configuration.rb @@ -11,67 +11,68 @@ class Configuration < HashWithDotAccess::Hash # Strings rather than symbols are used for compatibility with YAML. DEFAULTS = { # Where things are - "root_dir" => Dir.pwd, - "plugins_dir" => "plugins", - "source" => File.join(Dir.pwd, "src"), - "destination" => File.join(Dir.pwd, "output"), - "collections_dir" => "", - "cache_dir" => ".bridgetown-cache", - "layouts_dir" => "_layouts", - "data_dir" => "_data", - "components_dir" => "_components", - "partials_dir" => "_partials", - "collections" => {}, - "taxonomies" => { + "root_dir" => Dir.pwd, + "plugins_dir" => "plugins", + "source" => File.join(Dir.pwd, "src"), + "destination" => File.join(Dir.pwd, "output"), + "collections_dir" => "", + "cache_dir" => ".bridgetown-cache", + "layouts_dir" => "_layouts", + "data_dir" => "_data", + "components_dir" => "_components", + "partials_dir" => "_partials", + "collections" => {}, + "taxonomies" => { category: { key: "categories", title: "Category" }, tag: { key: "tags", title: "Tag" }, }, - "autoload_paths" => [], - "eager_load_paths" => [], - "plugins_use_zeitwerk" => true, + "autoload_paths" => [], + "eager_load_paths" => [], + "loader_collapsed_paths" => [], + "plugins_use_zeitwerk" => true, # Handling Reading - "include" => [".htaccess", "_redirects", ".well-known"], - "exclude" => [], - "keep_files" => [".git", ".svn", "_bridgetown"], - "encoding" => "utf-8", - "markdown_ext" => "markdown,mkdown,mkdn,mkd,md", - "strict_front_matter" => false, - "slugify_mode" => "pretty", + "include" => [".htaccess", "_redirects", ".well-known"], + "exclude" => [], + "keep_files" => [".git", ".svn", "_bridgetown"], + "encoding" => "utf-8", + "markdown_ext" => "markdown,mkdown,mkdn,mkd,md", + "strict_front_matter" => false, + "slugify_mode" => "pretty", # Filtering Content - "future" => false, - "unpublished" => false, - "ruby_in_front_matter" => true, + "future" => false, + "unpublished" => false, + "ruby_in_front_matter" => true, # Conversion - "content_engine" => "resource", - "markdown" => "kramdown", - "highlighter" => "rouge", - "excerpt_separator" => "\n\n", + "content_engine" => "resource", + "markdown" => "kramdown", + "highlighter" => "rouge", + "excerpt_separator" => "\n\n", # Serving - "port" => "4000", - "host" => "127.0.0.1", - "base_path" => "/", - "show_dir_listing" => false, + "port" => "4000", + "host" => "127.0.0.1", + "base_path" => "/", + "show_dir_listing" => false, # Output Configuration - "available_locales" => [:en], - "default_locale" => :en, - "permalink" => nil, # default is set according to content engine - "timezone" => nil, # use the local timezone + "available_locales" => [:en], + "default_locale" => :en, + "permalink" => nil, # default is set according to content engine + "timezone" => nil, # use the local timezone - "quiet" => false, - "verbose" => false, - "defaults" => [], + "quiet" => false, + "verbose" => false, + "defaults" => [], - "liquid" => { + "liquid" => { "error_mode" => "warn", "strict_filters" => false, "strict_variables" => false, }, - "kramdown" => { + "kramdown" => { "auto_ids" => true, "toc_levels" => (1..6).to_a, "entity_output" => "as_char", @@ -266,6 +267,10 @@ def setup_load_paths! File.expand_path(load_path, root_dir) end + loader_collapsed_paths.map! do |collapsed_path| + File.expand_path(collapsed_path, root_dir) + end + self end diff --git a/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb b/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb index 9e5b469f5..3f5848e5d 100644 --- a/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb +++ b/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb @@ -28,7 +28,7 @@ def reloading_enabled?(load_path) load_path.start_with?(root_dir) && ENV["BRIDGETOWN_ENV"] != "production" end - def setup_loaders(autoload_paths = []) # rubocop:todo Metrics/CyclomaticComplexity + def setup_loaders(autoload_paths = []) # rubocop:todo Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity (autoload_paths.presence || config.autoload_paths).each do |load_path| if @loaders.key?(load_path) raise "Zeitwerk loader already added for `#{load_path}'. Please check your config" @@ -44,6 +44,11 @@ def setup_loaders(autoload_paths = []) # rubocop:todo Metrics/CyclomaticComplexi end loader.enable_reloading if reloading_enabled?(load_path) loader.ignore(File.join(load_path, "**", "*.js.rb")) + config.loader_collapsed_paths.each do |collapsed_path| + next unless collapsed_path.starts_with?(load_path) + + loader.collapse(collapsed_path) + end Bridgetown::Hooks.trigger :loader, :pre_setup, loader, load_path loader.setup loader.eager_load if config.eager_load_paths.include?(load_path) diff --git a/bridgetown-website/bridgetown.config.yml b/bridgetown-website/bridgetown.config.yml index 91e0891dc..76ecdda42 100644 --- a/bridgetown-website/bridgetown.config.yml +++ b/bridgetown-website/bridgetown.config.yml @@ -2,6 +2,9 @@ url: "https://www.bridgetownrb.com" permalink: simple timezone: America/Los_Angeles +loader_collapsed_paths: + - plugins/testing + collections: docs: output: true diff --git a/bridgetown-website/plugins/test_output.rb b/bridgetown-website/plugins/testing/test_output.rb similarity index 83% rename from bridgetown-website/plugins/test_output.rb rename to bridgetown-website/plugins/testing/test_output.rb index 7f322966d..2e877301e 100644 --- a/bridgetown-website/plugins/test_output.rb +++ b/bridgetown-website/plugins/testing/test_output.rb @@ -5,7 +5,7 @@ module TestOutput Bridgetown::Hooks.register :site, :post_write do # Load test suite to run on exit require "nokogiri" - Dir["test/**/*.rb"].each { |file| require_relative("../#{file}") } + Dir["test/**/*.rb"].each { |file| require_relative("../../#{file}") } rescue LoadError Bridgetown.logger.warn "Testing:", "To run tests, you must first run `bundle install --with test`" diff --git a/bridgetown-website/src/_docs/plugins.md b/bridgetown-website/src/_docs/plugins.md index 70eb75146..2a6ef854e 100644 --- a/bridgetown-website/src/_docs/plugins.md +++ b/bridgetown-website/src/_docs/plugins.md @@ -216,10 +216,10 @@ plugins/my_plugin/woo/zoo.rb -> MyPlugin::Woo::Zoo You can read more about [Zeitwerk's file conventions here](https://github.com/fxn/zeitwerk#file-structure). {% rendercontent "docs/note", title: "Take Me Back" %} - If you run into any problems with Zeitwerk after upgrading your Bridgetown project from pre-1.0, you can switch to the previous plugin loading method by adding `plugins_use_zeitwerk: false` to your `bridgetown.config.yml`. + If you run into any problems with Zeitwerk after upgrading your Bridgetown project from pre-1.0, you can switch to the previous plugin loading method by adding `plugins_use_zeitwerk: false` to your `bridgetown.config.yml`. Or you can try using the `loader_collapsed_paths` setting as described below. {% endrendercontent %} -In addition to the `plugins` folder provided by default, **you can add your own folders** with autoloading support! Simply add to the `autoload_paths` key in your config YAML: +In addition to the `plugins` folder provided by default, **you can add your own folders** with autoloading support! Simply add to the `autoload_paths` setting in your config YAML: ```yaml autoload_paths: @@ -234,6 +234,29 @@ autoload_paths: eager: true ``` +There may be times when you want to bypass Zeitwerk's default folder-based namespacing. For example, if you wanted something like this: + +``` +plugins/builders/tags.rb -> Builders::Tags +plugins/helpers/hashify.rb -> Hashify +``` + +where the files in `builders` use a `Builders` namespace, but the files in `helpers` don't use a `Helpers` namespace, you can use the `loader_collapsed_paths` setting: + +```yaml +loader_collapsed_paths: + - plugins/helpers +``` + +And if you don't want namespacing for _any_ subfolders, you can use a glob pattern: + +```yaml +loader_collapsed_paths: + - top_level/* +``` + +Thus no files directly in `top_level` as well as any of its subfolders will be namespaced (that is, no `TopLevel` module will be implied). + ## Creating a Gem The `bridgetown plugins new NAME` command will create an entire gem scaffold From 369b6be096acc7b572e3c4fe8ade9219595d3e2b Mon Sep 17 00:00:00 2001 From: Jared White Date: Fri, 5 Nov 2021 08:20:50 -0700 Subject: [PATCH 11/12] Test and document subnested folders --- .../features/site_configuration.feature | 14 ++++++++++++++ bridgetown-core/features/support/helpers.rb | 1 + bridgetown-website/src/_docs/plugins.md | 2 +- 3 files changed, 16 insertions(+), 1 deletion(-) diff --git a/bridgetown-core/features/site_configuration.feature b/bridgetown-core/features/site_configuration.feature index 13e78a189..123aca025 100644 --- a/bridgetown-core/features/site_configuration.feature +++ b/bridgetown-core/features/site_configuration.feature @@ -280,6 +280,7 @@ Feature: Site configuration Scenario: Allow collapsed Zeitwerk dirs using globs Given I have a "plugins/nested" directory + And I have a "plugins/nested/subnested" directory And I have a "plugins/nested/top_level.rb" file with content: """ module TopLevel @@ -291,6 +292,17 @@ Feature: Site configuration end end """ + And I have a "plugins/nested/subnested/lower_level.rb" file with content: + """ + module Subnested::LowerLevel + Bridgetown::Hooks.register :site, :after_reset do |site| + pg = Bridgetown::GeneratedPage.new(site, site.source, "/", "bar.html") + pg.content = "Zeitwerk glob subnested dir" + + site.generated_pages << pg + end + end + """ And I have a "bridgetown.config.yml" file with content: """ loader_collapsed_paths: @@ -300,3 +312,5 @@ Feature: Site configuration Then I should get a zero exit status And the output directory should exist And I should see "Zeitwerk glob dir" in "output/foo/index.html" + And I should see "Zeitwerk glob subnested dir" in "output/bar/index.html" + diff --git a/bridgetown-core/features/support/helpers.rb b/bridgetown-core/features/support/helpers.rb index 3cf469151..8ff212505 100644 --- a/bridgetown-core/features/support/helpers.rb +++ b/bridgetown-core/features/support/helpers.rb @@ -38,6 +38,7 @@ def self.root_files "bridgetown.config.yml", "plugins", "plugins/nested", + "plugins/nested/subnested", "frontend", ] end diff --git a/bridgetown-website/src/_docs/plugins.md b/bridgetown-website/src/_docs/plugins.md index 2a6ef854e..f56c44698 100644 --- a/bridgetown-website/src/_docs/plugins.md +++ b/bridgetown-website/src/_docs/plugins.md @@ -255,7 +255,7 @@ loader_collapsed_paths: - top_level/* ``` -Thus no files directly in `top_level` as well as any of its subfolders will be namespaced (that is, no `TopLevel` module will be implied). +Thus no files directly in `top_level` as well as any of its immediate subfolders will be namespaced (that is, no `TopLevel` module will be implied). ## Creating a Gem From b92747a8949f8e39c56031c9db3ae3cdb63534dd Mon Sep 17 00:00:00 2001 From: Jared White Date: Fri, 5 Nov 2021 08:23:55 -0700 Subject: [PATCH 12/12] Switch from `loaders_collapsed_paths` to `autoloader_collapsed_paths` --- .../features/site_configuration.feature | 4 +- .../lib/bridgetown-core/configuration.rb | 88 +++++++++---------- .../bridgetown-core/utils/loaders_manager.rb | 2 +- bridgetown-website/bridgetown.config.yml | 2 +- bridgetown-website/src/_docs/plugins.md | 8 +- 5 files changed, 52 insertions(+), 52 deletions(-) diff --git a/bridgetown-core/features/site_configuration.feature b/bridgetown-core/features/site_configuration.feature index 123aca025..47a787b77 100644 --- a/bridgetown-core/features/site_configuration.feature +++ b/bridgetown-core/features/site_configuration.feature @@ -270,7 +270,7 @@ Feature: Site configuration """ And I have a "bridgetown.config.yml" file with content: """ - loader_collapsed_paths: + autoloader_collapsed_paths: - plugins/nested """ When I run bridgetown build @@ -305,7 +305,7 @@ Feature: Site configuration """ And I have a "bridgetown.config.yml" file with content: """ - loader_collapsed_paths: + autoloader_collapsed_paths: - plugins/* """ When I run bridgetown build diff --git a/bridgetown-core/lib/bridgetown-core/configuration.rb b/bridgetown-core/lib/bridgetown-core/configuration.rb index d941cfd90..89a6eb726 100644 --- a/bridgetown-core/lib/bridgetown-core/configuration.rb +++ b/bridgetown-core/lib/bridgetown-core/configuration.rb @@ -11,68 +11,68 @@ class Configuration < HashWithDotAccess::Hash # Strings rather than symbols are used for compatibility with YAML. DEFAULTS = { # Where things are - "root_dir" => Dir.pwd, - "plugins_dir" => "plugins", - "source" => File.join(Dir.pwd, "src"), - "destination" => File.join(Dir.pwd, "output"), - "collections_dir" => "", - "cache_dir" => ".bridgetown-cache", - "layouts_dir" => "_layouts", - "data_dir" => "_data", - "components_dir" => "_components", - "partials_dir" => "_partials", - "collections" => {}, - "taxonomies" => { + "root_dir" => Dir.pwd, + "plugins_dir" => "plugins", + "source" => File.join(Dir.pwd, "src"), + "destination" => File.join(Dir.pwd, "output"), + "collections_dir" => "", + "cache_dir" => ".bridgetown-cache", + "layouts_dir" => "_layouts", + "data_dir" => "_data", + "components_dir" => "_components", + "partials_dir" => "_partials", + "collections" => {}, + "taxonomies" => { category: { key: "categories", title: "Category" }, tag: { key: "tags", title: "Tag" }, }, - "autoload_paths" => [], - "eager_load_paths" => [], - "loader_collapsed_paths" => [], - "plugins_use_zeitwerk" => true, + "autoload_paths" => [], + "eager_load_paths" => [], + "autoloader_collapsed_paths" => [], + "plugins_use_zeitwerk" => true, # Handling Reading - "include" => [".htaccess", "_redirects", ".well-known"], - "exclude" => [], - "keep_files" => [".git", ".svn", "_bridgetown"], - "encoding" => "utf-8", - "markdown_ext" => "markdown,mkdown,mkdn,mkd,md", - "strict_front_matter" => false, - "slugify_mode" => "pretty", + "include" => [".htaccess", "_redirects", ".well-known"], + "exclude" => [], + "keep_files" => [".git", ".svn", "_bridgetown"], + "encoding" => "utf-8", + "markdown_ext" => "markdown,mkdown,mkdn,mkd,md", + "strict_front_matter" => false, + "slugify_mode" => "pretty", # Filtering Content - "future" => false, - "unpublished" => false, - "ruby_in_front_matter" => true, + "future" => false, + "unpublished" => false, + "ruby_in_front_matter" => true, # Conversion - "content_engine" => "resource", - "markdown" => "kramdown", - "highlighter" => "rouge", - "excerpt_separator" => "\n\n", + "content_engine" => "resource", + "markdown" => "kramdown", + "highlighter" => "rouge", + "excerpt_separator" => "\n\n", # Serving - "port" => "4000", - "host" => "127.0.0.1", - "base_path" => "/", - "show_dir_listing" => false, + "port" => "4000", + "host" => "127.0.0.1", + "base_path" => "/", + "show_dir_listing" => false, # Output Configuration - "available_locales" => [:en], - "default_locale" => :en, - "permalink" => nil, # default is set according to content engine - "timezone" => nil, # use the local timezone + "available_locales" => [:en], + "default_locale" => :en, + "permalink" => nil, # default is set according to content engine + "timezone" => nil, # use the local timezone - "quiet" => false, - "verbose" => false, - "defaults" => [], + "quiet" => false, + "verbose" => false, + "defaults" => [], - "liquid" => { + "liquid" => { "error_mode" => "warn", "strict_filters" => false, "strict_variables" => false, }, - "kramdown" => { + "kramdown" => { "auto_ids" => true, "toc_levels" => (1..6).to_a, "entity_output" => "as_char", @@ -267,7 +267,7 @@ def setup_load_paths! File.expand_path(load_path, root_dir) end - loader_collapsed_paths.map! do |collapsed_path| + autoloader_collapsed_paths.map! do |collapsed_path| File.expand_path(collapsed_path, root_dir) end diff --git a/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb b/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb index 3f5848e5d..f37342d48 100644 --- a/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb +++ b/bridgetown-core/lib/bridgetown-core/utils/loaders_manager.rb @@ -44,7 +44,7 @@ def setup_loaders(autoload_paths = []) # rubocop:todo Metrics/AbcSize, Metrics/C end loader.enable_reloading if reloading_enabled?(load_path) loader.ignore(File.join(load_path, "**", "*.js.rb")) - config.loader_collapsed_paths.each do |collapsed_path| + config.autoloader_collapsed_paths.each do |collapsed_path| next unless collapsed_path.starts_with?(load_path) loader.collapse(collapsed_path) diff --git a/bridgetown-website/bridgetown.config.yml b/bridgetown-website/bridgetown.config.yml index 76ecdda42..1e249bf5e 100644 --- a/bridgetown-website/bridgetown.config.yml +++ b/bridgetown-website/bridgetown.config.yml @@ -2,7 +2,7 @@ url: "https://www.bridgetownrb.com" permalink: simple timezone: America/Los_Angeles -loader_collapsed_paths: +autoloader_collapsed_paths: - plugins/testing collections: diff --git a/bridgetown-website/src/_docs/plugins.md b/bridgetown-website/src/_docs/plugins.md index f56c44698..d8b752af8 100644 --- a/bridgetown-website/src/_docs/plugins.md +++ b/bridgetown-website/src/_docs/plugins.md @@ -216,7 +216,7 @@ plugins/my_plugin/woo/zoo.rb -> MyPlugin::Woo::Zoo You can read more about [Zeitwerk's file conventions here](https://github.com/fxn/zeitwerk#file-structure). {% rendercontent "docs/note", title: "Take Me Back" %} - If you run into any problems with Zeitwerk after upgrading your Bridgetown project from pre-1.0, you can switch to the previous plugin loading method by adding `plugins_use_zeitwerk: false` to your `bridgetown.config.yml`. Or you can try using the `loader_collapsed_paths` setting as described below. + If you run into any problems with Zeitwerk after upgrading your Bridgetown project from pre-1.0, you can switch to the previous plugin loading method by adding `plugins_use_zeitwerk: false` to your `bridgetown.config.yml`. Or you can try using the `autoloader_collapsed_paths` setting as described below. {% endrendercontent %} In addition to the `plugins` folder provided by default, **you can add your own folders** with autoloading support! Simply add to the `autoload_paths` setting in your config YAML: @@ -241,17 +241,17 @@ plugins/builders/tags.rb -> Builders::Tags plugins/helpers/hashify.rb -> Hashify ``` -where the files in `builders` use a `Builders` namespace, but the files in `helpers` don't use a `Helpers` namespace, you can use the `loader_collapsed_paths` setting: +where the files in `builders` use a `Builders` namespace, but the files in `helpers` don't use a `Helpers` namespace, you can use the `autoloader_collapsed_paths` setting: ```yaml -loader_collapsed_paths: +autoloader_collapsed_paths: - plugins/helpers ``` And if you don't want namespacing for _any_ subfolders, you can use a glob pattern: ```yaml -loader_collapsed_paths: +autoloader_collapsed_paths: - top_level/* ```