class Jekyll::Site

def categories

def categories
  post_attr_hash("categories")
end

def cleanup

Returns nothing.

Remove orphaned files and empty directories in destination.
def cleanup
  site_cleaner.cleanup!
  nil
end

def collection_names

or an empty array if the `collections` key is not set.
Returns an array of collection names from the configuration,

The list of collection names.
def collection_names
  case config["collections"]
  when Hash
    config["collections"].keys
  when Array
    config["collections"]
  when nil
    []
  else
    raise ArgumentError, "Your `collections` key must be a hash or an array."
  end
end

def collections

Returns a Hash containing collection name-to-instance pairs.

for each item in the collection, a new hash is returned otherwise.
If config['collections'] is set, a new instance is created
The list of collections and their corresponding Jekyll::Collection instances.
def collections
  @collections ||= collection_names.each_with_object({}) do |name, hsh|
    hsh[name] = Jekyll::Collection.new(self, name)
  end
end

def collections_path

Returns the source directory or the absolute path to the custom collections_dir

with the current site.
Public: The full path to the directory that houses all the collections registered
def collections_path
  dir_str = config["collections_dir"]
  @collections_path ||= dir_str.empty? ? source : in_source_dir(dir_str)
end

def config=(config)

Returns the new configuration.

config - a Jekyll::Configuration, containing the new configuration.

changing values in the configuration.
Public: Set the site's configuration. This handles side-effects caused by
def config=(config)
  @config = config.clone
  %w(safe lsi highlighter baseurl exclude include future unpublished
     show_drafts limit_posts keep_files).each do |opt|
    send("#{opt}=", config[opt])
  end
  # keep using `gems` to avoid breaking change
  self.gems = config["plugins"]
  configure_cache
  configure_plugins
  configure_theme
  configure_include_paths
  configure_file_read_opts
  self.permalink_style = config["permalink"].to_sym
  # Read in a _config.yml from the current theme-gem at the very end.
  @config = load_theme_configuration(config) if theme
  @config
end

def configure_cache

Disable Marshaling cache to disk in Safe Mode
def configure_cache
  Jekyll::Cache.cache_dir = in_source_dir(config["cache_dir"], "Jekyll/Cache")
  if safe || config["disable_disk_cache"]
    Jekyll::Cache.disable_disk_cache!
  else
    hide_cache_dir_from_git
  end
end

def configure_file_read_opts

def configure_file_read_opts
  self.file_read_opts = {}
  file_read_opts[:encoding] = config["encoding"] if config["encoding"]
  self.file_read_opts = Jekyll::Utils.merged_file_read_opts(self, {})
end

def configure_include_paths

def configure_include_paths
  @includes_load_paths = Array(in_source_dir(config["includes_dir"].to_s))
  @includes_load_paths << theme.includes_path if theme&.includes_path
end

def configure_plugins

def configure_plugins
  self.plugin_manager = Jekyll::PluginManager.new(self)
  self.plugins        = plugin_manager.plugins_path
end

def configure_theme

def configure_theme
  self.theme = nil
  return if config["theme"].nil?
  self.theme =
    if config["theme"].is_a?(String)
      Jekyll::Theme.new(config["theme"])
    else
      Jekyll.logger.warn "Theme:", "value of 'theme' in config should be String to use " \
                                   "gem-based themes, but got #{config["theme"].class}"
      nil
    end
end

def docs_to_write

Returns an Array of Documents which should be written

Get the to be written documents
def docs_to_write
  documents.select(&:write?)
end

def documents

Returns an Array of all Documents

Get all the documents
def documents
  collections.each_with_object(Set.new) do |(_, collection), set|
    set.merge(collection.docs).merge(collection.files)
  end.to_a
end

def each_site_file

def each_site_file
  pages.each { |page| yield page }
  static_files.each { |file| yield(file) if file.write? }
  collections.each_value { |coll| coll.docs.each { |doc| yield(doc) if doc.write? } }
end

def ensure_not_in_dest

parent to the source dir.
Check that the destination dir isn't the source dir or a directory
def ensure_not_in_dest
  dest_pathname = Pathname.new(dest)
  Pathname.new(source).ascend do |path|
    if path == dest_pathname
      raise Errors::FatalException,
            "Destination directory cannot be or contain the Source directory."
    end
  end
end

def find_converter_instance(klass)

klass - The Class of the Converter to fetch.
Returns the Converter instance implementing the given Converter.
Get the implementation class for the given Converter.
def find_converter_instance(klass)
  @find_converter_instance ||= {}
  @find_converter_instance[klass] ||= converters.find do |converter|
    converter.instance_of?(klass)
  end || \
    raise("No Converters found for #{klass}")
end

def frontmatter_defaults

Returns The FrontmatterDefaults

if it doesn't already exist.
Returns the FrontmatterDefaults or creates a new FrontmatterDefaults
def frontmatter_defaults
  @frontmatter_defaults ||= FrontmatterDefaults.new(self)
end

def generate

Returns nothing.

Run each of the Generators.
def generate
  generators.each do |generator|
    start = Time.now
    generator.generate(self)
    Jekyll.logger.debug "Generating:",
                        "#{generator.class} finished in #{Time.now - start} seconds."
  end
  nil
end

def hide_cache_dir_from_git

def hide_cache_dir_from_git
  @cache_gitignore_path ||= in_source_dir(config["cache_dir"], ".gitignore")
  return if File.exist?(@cache_gitignore_path)
  cache_dir_path = in_source_dir(config["cache_dir"])
  FileUtils.mkdir_p(cache_dir_path) unless File.directory?(cache_dir_path)
  File.open(@cache_gitignore_path, "wb") do |file|
    file.puts("# ignore everything in this directory\n*")
  end
end

def in_cache_dir(*paths)

Returns a path which is prefixed with the cache directory.

cache directory
paths - (optional) path elements to a file or directory within the

Public: Prefix a given path with the cache directory.
def in_cache_dir(*paths)
  paths.reduce(cache_dir) do |base, path|
    Jekyll.sanitized_path(base, path)
  end
end

def in_dest_dir(*paths)

Returns a path which is prefixed with the destination directory.

destination directory
paths - (optional) path elements to a file or directory within the

Public: Prefix a given path with the destination directory.
def in_dest_dir(*paths)
  paths.reduce(dest) do |base, path|
    Jekyll.sanitized_path(base, path)
  end
end

def in_source_dir(*paths)

Returns a path which is prefixed with the source directory.

source directory
paths - (optional) path elements to a file or directory within the

Public: Prefix a given path with the source directory.
def in_source_dir(*paths)
  paths.reduce(source) do |base, path|
    Jekyll.sanitized_path(base, path)
  end
end

def in_theme_dir(*paths)

Returns a path which is prefixed with the theme root directory.

theme directory
paths - (optional) path elements to a file or directory within the

Public: Prefix a given path with the theme directory.
def in_theme_dir(*paths)
  return nil unless theme
  paths.reduce(theme.root) do |base, path|
    Jekyll.sanitized_path(base, path)
  end
end

def incremental?(override = {})

Returns a Boolean: true for a full rebuild, false for normal build

Whether to perform a full rebuild without incremental regeneration
def incremental?(override = {})
  override["incremental"] || config["incremental"]
end

def initialize(config)

config - A Hash containing site configuration details.

Public: Initialize a new Site.
def initialize(config)
  # Source and destination may not be changed after the site has been created.
  @source          = File.expand_path(config["source"]).freeze
  @dest            = File.expand_path(config["destination"]).freeze
  self.config = config
  @cache_dir       = in_source_dir(config["cache_dir"])
  @filter_cache    = {}
  @reader          = Reader.new(self)
  @profiler        = Profiler.new(self)
  @regenerator     = Regenerator.new(self)
  @liquid_renderer = LiquidRenderer.new(self)
  Jekyll.sites << self
  reset
  setup
  Jekyll::Hooks.trigger :site, :after_init, self
end

def inspect

Returns the object as a debug String.

Public
def inspect
  "#<#{self.class} @source=#{@source}>"
end

def instantiate_subclasses(klass)

def instantiate_subclasses(klass)
  klass.descendants.select { |c| !safe || c.safe }.tap do |result|
    result.sort!
    result.map! { |c| c.new(config) }
  end
end

def limit_posts!

Returns nothing

Limits the current posts; removes the posts which exceed the limit_posts
def limit_posts!
  if limit_posts.positive?
    limit = posts.docs.length < limit_posts ? posts.docs.length : limit_posts
    posts.docs = posts.docs[-limit, limit]
  end
end

def load_theme_configuration(config)

def load_theme_configuration(config)
  return config if config["ignore_theme_config"] == true
  theme_config_file = in_theme_dir("_config.yml")
  return config unless File.exist?(theme_config_file)
  # Bail out if the theme_config_file is a symlink file irrespective of safe mode
  return config if File.symlink?(theme_config_file)
  theme_config = SafeYAML.load_file(theme_config_file)
  return config unless theme_config.is_a?(Hash)
  Jekyll.logger.info "Theme Config file:", theme_config_file
  # theme_config should not be overriding Jekyll's defaults
  theme_config.delete_if { |key, _| Configuration::DEFAULTS.key?(key) }
  # Override theme_config with existing config and return the result.
  # Additionally ensure we return a `Jekyll::Configuration` instance instead of a Hash.
  Utils.deep_merge_hashes(theme_config, config)
    .each_with_object(Jekyll::Configuration.new) do |(key, value), conf|
      conf[key] = value
    end
end

def post_attr_hash(post_attr)

posts - The Array of Posts with the given attr value.
attr - One of the values for the requested attribute.
Returns the Hash: { attr => posts } where

# 'ruby' => [] }
# => { 'tech' => [, ],
post_attr_hash('categories')

Examples

post_attr - The String name of the Post attribute.

Construct a Hash of Posts indexed by the specified Post attribute.
def post_attr_hash(post_attr)
  # Build a hash map based on the specified post attribute ( post attr =>
  # array of posts ) then sort each array in reverse order.
  @post_attr_hash[post_attr] ||= begin
    hash = Hash.new { |h, key| h[key] = [] }
    posts.docs.each do |p|
      p.data[post_attr]&.each { |t| hash[t] << p }
    end
    hash.each_value { |posts| posts.sort!.reverse! }
    hash
  end
end

def posts

def posts
  collections["posts"] ||= Collection.new(self, "posts")
end

def print_stats

def print_stats
  Jekyll.logger.info @liquid_renderer.stats_table
end

def process

Returns nothing.

Public: Read, process, and write this Site to output.
def process
  return profiler.profile_process if config["profile"]
  reset
  read
  generate
  render
  cleanup
  write
end

def publisher

Returns The Publisher

already exist.
Returns the publisher or creates a new publisher if it doesn't
def publisher
  @publisher ||= Publisher.new(self)
end

def read

Returns nothing.

Read Site data from disk and load it into internal data structures.
def read
  reader.read
  limit_posts!
  Jekyll::Hooks.trigger :site, :post_read, self
  nil
end

def relative_permalinks_are_deprecated

Returns

directory. As this is a deprecated function of Jekyll.
Warns the user if permanent links are relative to the parent
def relative_permalinks_are_deprecated
  if config["relative_permalinks"]
    Jekyll.logger.abort_with "Since v3.0, permalinks for pages " \
                             "in subfolders must be relative to the " \
                             "site source directory, not the parent " \
                             "directory. Check https://jekyllrb.com/docs/upgrading/ " \
                             "for more info."
  end
end

def render

Returns nothing.

Render the site to the destination.
def render
  relative_permalinks_are_deprecated
  payload = site_payload
  Jekyll::Hooks.trigger :site, :pre_render, self, payload
  render_docs(payload)
  render_pages(payload)
  Jekyll::Hooks.trigger :site, :post_render, self, payload
  nil
end

def render_docs(payload)

def render_docs(payload)
  collections.each_value do |collection|
    collection.docs.each do |document|
      render_regenerated(document, payload)
    end
  end
end

def render_pages(payload)

def render_pages(payload)
  pages.each do |page|
    render_regenerated(page, payload)
  end
end

def render_regenerated(document, payload)

def render_regenerated(document, payload)
  return unless regenerator.regenerate?(document)
  document.renderer.payload = payload
  document.output = document.renderer.run
  document.trigger_hooks(:post_render)
end

def reset

Returns nothing

Reset Site details.

rubocop:disable Metrics/MethodLength
rubocop:disable Metrics/AbcSize
def reset
  self.time = if config["time"]
                Utils.parse_date(config["time"].to_s, "Invalid time in _config.yml.")
              else
                Time.now
              end
  self.layouts = {}
  self.inclusions = {}
  self.pages = []
  self.static_files = []
  self.data = {}
  @post_attr_hash = {}
  @site_data = nil
  @collections = nil
  @documents = nil
  @docs_to_write = nil
  @regenerator.clear_cache
  @liquid_renderer.reset
  @site_cleaner = nil
  frontmatter_defaults.reset
  raise ArgumentError, "limit_posts must be a non-negative number" if limit_posts.negative?
  Jekyll::Cache.clear_if_config_changed config
  Jekyll::Hooks.trigger :site, :after_reset, self
  nil
end

def setup

Returns nothing.

Load necessary libraries, plugins, converters, and generators.
def setup
  ensure_not_in_dest
  plugin_manager.conscientious_require
  self.converters = instantiate_subclasses(Jekyll::Converter)
  self.generators = instantiate_subclasses(Jekyll::Generator)
end

def site_cleaner

Returns The Cleaner

already exist.
Returns the Cleaner or creates a new Cleaner if it doesn't
def site_cleaner
  @site_cleaner ||= Cleaner.new(self)
end

def site_data

Returns the Hash to be hooked to site.data.

if the key 'data' is already used in _config.yml.
Prepare site data for site payload. The method maintains backward compatibility
def site_data
  @site_data ||= (config["data"] || data)
end

def site_payload

See Site#post_attr_hash for type info.
"tags" - The Hash of tag values and Posts.
See Site#post_attr_hash for type info.
"categories" - The Hash of category values and Posts.
"html_pages" - The Array of HTML Pages.
"pages" - The Array of all Pages.
and then title.
"posts" - The Array of Posts, sorted chronologically by post date
current time if none was specified.
"time" - The Time as specified in the configuration or the
Returns the Hash: { "site" => data } where data is a Hash with keys:

The Hash payload containing site-wide data.
def site_payload
  Drops::UnifiedPayloadDrop.new self
end

def static_files_to_write

Returns an Array of StaticFiles which should be written

Get the to be written static files
def static_files_to_write
  static_files.select(&:write?)
end

def tags

def tags
  post_attr_hash("tags")
end

def write

Returns nothing.

Write static files, pages, and posts.
def write
  Jekyll::Commands::Doctor.conflicting_urls(self)
  each_site_file do |item|
    item.write(dest) if regenerator.regenerate?(item)
  end
  regenerator.write_metadata
  Jekyll::Hooks.trigger :site, :post_write, self
  nil
end