class Bridgetown::Collection
Attributes
@return [Bridgetown::Site]
Public Class Methods
Create a new Collection
.
@param site [Bridgetown::Site] the site to which this collection belongs @param label [String] the name of the collection
# File lib/bridgetown-core/collection.rb, line 17 def initialize(site, label) @site = site @label = sanitize_label(label) @metadata = extract_metadata end
Public Instance Methods
The full path to the folder containing the collection.
@return [String] full path where the collection is stored on the filesystem
# File lib/bridgetown-core/collection.rb, line 149 def absolute_path @absolute_path ||= site.in_source_dir(relative_path) end
# File lib/bridgetown-core/collection.rb, line 23 def builtin? label.in? %w(posts pages data).freeze end
The full path to the folder containing the collection, with
optional subpaths.
@param *files [Array<String>] any other path pieces relative to the
folder to append to the path
@return [String]
# File lib/bridgetown-core/collection.rb, line 160 def collection_dir(*files) return absolute_path if files.empty? site.in_source_dir(relative_path, *files) end
# File lib/bridgetown-core/collection.rb, line 31 def data? label == "data" end
Used by Resource's permalink processor @return [String]
# File lib/bridgetown-core/collection.rb, line 223 def default_permalink metadata.fetch("permalink") do "/:collection/:path/" end end
Fetch the Documents in this collection. Defaults to an empty array if no documents have been read in.
@return [Array<Bridgetown::Document>]
# File lib/bridgetown-core/collection.rb, line 39 def docs @docs ||= [] end
Iterate over either Resources or Documents depending on how the site is configured
# File lib/bridgetown-core/collection.rb, line 53 def each(&block) site.uses_resource? ? resources.each(&block) : docs.each(&block) end
All the entries in this collection.
@return [Array<String>] file paths to the documents in this collection
relative to the collection's folder
# File lib/bridgetown-core/collection.rb, line 103 def entries return [] unless exists? @entries ||= begin collection_dir_slash = "#{collection_dir}/" Utils.safe_glob(collection_dir, ["**", "*"], File::FNM_DOTMATCH).map do |entry| entry.sub(collection_dir_slash, "") end end end
The entry filter for this collection. Creates an instance of Bridgetown::EntryFilter
if needed.
@return [Bridgetown::EntryFilter]
# File lib/bridgetown-core/collection.rb, line 177 def entry_filter @entry_filter ||= Bridgetown::EntryFilter.new( site, base_directory: folder_name, include_underscores: site.uses_resource? ) end
Checks whether the folder “exists” for this collection.
@return [Boolean]
# File lib/bridgetown-core/collection.rb, line 169 def exists? File.directory?(absolute_path) end
Extract options for this collection from the site configuration.
@return [HashWithDotAccess::Hash]
# File lib/bridgetown-core/collection.rb, line 241 def extract_metadata site.config.collections[label] || HashWithDotAccess::Hash.new end
# File lib/bridgetown-core/collection.rb, line 65 def files Bridgetown::Deprecator.deprecation_message "Collection#files is now Collection#static_files" static_files end
Filtered version of the entries in this collection. See `Bridgetown::EntryFilter#filter` for more information.
@return [Array<String>] list of filtered entry paths
# File lib/bridgetown-core/collection.rb, line 118 def filtered_entries return [] unless exists? @filtered_entries ||= Dir.chdir(absolute_path) do entry_filter.filter(entries).reject do |f| path = collection_dir(f) File.directory?(path) end end end
The folder name of this Collection
, e.g. `_posts` or `_events`
@return [String]
# File lib/bridgetown-core/collection.rb, line 133 def folder_name @folder_name ||= "_#{label}" end
An inspect string.
@return [String]
# File lib/bridgetown-core/collection.rb, line 188 def inspect "#<#{self.class} @label=#{label} docs=#{docs} resources=#{resources}>" end
# File lib/bridgetown-core/collection.rb, line 27 def legacy_reader? label.in? %w(posts data).freeze end
# File lib/bridgetown-core/collection.rb, line 245 def merge_data_resources # rubocop:todo Metrics/AbcSize, Metrics/MethodLength data_contents = {} sanitize_filename = ->(name) do name.gsub(%r![^\w\s-]+|(?<=^|\b\s)\s+(?=$|\s?\b)!, "") .gsub(%r!\s+!, "_") end resources.each do |data_resource| segments = data_resource.relative_path.each_filename.to_a[1..-1] nested = [] segments.each_with_index do |segment, index| sanitized_segment = sanitize_filename.(File.basename(segment, ".*")) hsh = nested.empty? ? data_contents : data_contents.dig(*nested) if !hsh.is_a?(Hash) Bridgetown.logger.error( "Error:", "#{nested.join("/")} is not a Hash structure, #{segment} cannot be read" ) elsif index == segments.length - 1 hsh[sanitized_segment] = data_resource.data.rows || data_resource.data elsif !hsh.key?(sanitized_segment) hsh[sanitized_segment] = {} end nested << sanitized_segment end end merge_environment_specific_metadata(data_contents).with_dot_access end
# File lib/bridgetown-core/collection.rb, line 276 def merge_environment_specific_metadata(data_contents) if data_contents["site_metadata"] data_contents["site_metadata"][Bridgetown.environment]&.each_key do |k| data_contents["site_metadata"][k] = data_contents["site_metadata"][Bridgetown.environment][k] end data_contents["site_metadata"].delete(Bridgetown.environment) end data_contents end
Read the allowed documents into the collection's array of docs.
@return [Bridgetown::Collection] self
# File lib/bridgetown-core/collection.rb, line 73 def read # rubocop:todo Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity filtered_entries.each do |file_path| full_path = collection_dir(file_path) next if File.directory?(full_path) if site.uses_resource? next if File.basename(file_path).starts_with?("_") if label == "data" || Utils.has_yaml_header?(full_path) || Utils.has_rbfm_header?(full_path) read_resource(full_path) else read_static_file(file_path, full_path) end elsif Utils.has_yaml_header? full_path read_document(full_path) else read_static_file(file_path, full_path) end end site.static_files.concat(static_files) sort_docs! self end
Read in resource from repo path @param full_path [String]
# File lib/bridgetown-core/collection.rb, line 290 def read_resource(full_path) id = "repo://#{label}.collection/" + Addressable::URI.escape( Pathname(full_path).relative_path_from(Pathname(site.source)).to_s ) resource = Bridgetown::Model::Base.find(id).to_resource.read! resources << resource if site.config.unpublished || resource.published? end
The relative path to the folder containing the collection.
@return [String] folder where the collection is stored relative to the
configured collections folder (usually the site source)
# File lib/bridgetown-core/collection.rb, line 142 def relative_path Pathname.new(container).join(folder_name).to_s end
Fetch the Resources in this collection. Defaults to an empty array if no resources have been read in.
@return [Array<Bridgetown::Resource::Base>]
# File lib/bridgetown-core/collection.rb, line 47 def resources @resources ||= [] end
Produce a sanitized label name Label names may not contain anything but alphanumeric characters,
underscores, and hyphens.
@param label [String] the possibly-unsafe label @return [String] sanitized version of the label.
# File lib/bridgetown-core/collection.rb, line 198 def sanitize_label(label) label.gsub(%r![^a-z0-9_\-\.]!i, "") end
Fetch the static files in this collection. Defaults to an empty array if no static files have been read in.
@return [Array<Bridgetown::StaticFile>]
# File lib/bridgetown-core/collection.rb, line 61 def static_files @static_files ||= [] end
Produce a representation of this Collection
for use in Liquid. Exposes two attributes:
- label - docs
@return [Bridgetown::Drops::CollectionDrop] representation of this
collection for use in Liquid
# File lib/bridgetown-core/collection.rb, line 209 def to_liquid Drops::CollectionDrop.new self end
LEGACY: The URL
template to render collection's documents at.
@return [String]
# File lib/bridgetown-core/collection.rb, line 232 def url_template @url_template ||= metadata.fetch("permalink") do Utils.add_permalink_suffix("/:collection/:path", site.permalink_style) end end
Whether the collection's documents ought to be written as individual
files in the output.
@return [Boolean] true if the 'write' metadata is true, false otherwise.
# File lib/bridgetown-core/collection.rb, line 217 def write? !!metadata.fetch("output", false) end
Private Instance Methods
# File lib/bridgetown-core/collection.rb, line 300 def container @container ||= site.config["collections_dir"] end
# File lib/bridgetown-core/collection.rb, line 359 def determine_sort_order(sort_key, apples, olives) apple_property, apple_document = apples olive_property, olive_document = olives if apple_property.nil? && !olive_property.nil? order_with_warning(sort_key, apple_document, 1) elsif !apple_property.nil? && olive_property.nil? order_with_warning(sort_key, olive_document, -1) else apple_property <=> olive_property end end
# File lib/bridgetown-core/collection.rb, line 372 def order_with_warning(sort_key, document, order) Bridgetown.logger.warn "Sort warning:", "'#{sort_key}' not defined in" \ " #{document.relative_path}" order end
# File lib/bridgetown-core/collection.rb, line 304 def read_document(full_path) doc = Document.new(full_path, site: site, collection: self).tap(&:read) docs << doc if site.config.unpublished || doc.published? end
# File lib/bridgetown-core/collection.rb, line 378 def read_static_file(file_path, full_path) relative_dir = Bridgetown.sanitized_path( relative_path, File.dirname(file_path) ).chomp("/.") static_files << StaticFile.new( site, site.source, relative_dir, File.basename(full_path), self ) end
# File lib/bridgetown-core/collection.rb, line 309 def sort_docs! if metadata["sort_by"].is_a?(String) sort_docs_by_key! sort_resources_by_key! else docs.sort! resources.sort! end docs.reverse! if metadata.sort_direction == "descending" resources.reverse! if metadata.sort_direction == "descending" end
A custom sort function based on Schwartzian transform Refer byparker.com/blog/2017/schwartzian-transform-faster-sorting/ for details
# File lib/bridgetown-core/collection.rb, line 323 def sort_docs_by_key! meta_key = metadata["sort_by"] # Modify `docs` array to cache document's property along with the Document instance docs.map! { |doc| [doc.data[meta_key], doc] }.sort! do |apples, olives| order = determine_sort_order(meta_key, apples, olives) # Fall back to `Document#<=>` if the properties were equal or were non-sortable # Otherwise continue with current sort-order if order.nil? || order.zero? apples[-1] <=> olives[-1] else order end # Finally restore the `docs` array with just the Document objects themselves end.map!(&:last) end
# File lib/bridgetown-core/collection.rb, line 341 def sort_resources_by_key! meta_key = metadata["sort_by"] # Modify `docs` array to cache document's property along with the Document instance resources.map! { |doc| [doc.data[meta_key], doc] }.sort! do |apples, olives| order = determine_sort_order(meta_key, apples, olives) # Fall back to `Document#<=>` if the properties were equal or were non-sortable # Otherwise continue with current sort-order if order.nil? || order.zero? apples[-1] <=> olives[-1] else order end # Finally restore the `docs` array with just the Document objects themselves end.map!(&:last) end