class Jekyll::Document

Constants

DATELESS_FILENAME_MATCHER
DATE_FILENAME_MATCHER
YAML_FRONT_MATTER_REGEXP

Attributes

collection[R]
content[RW]
extname[R]
output[RW]
path[R]
site[R]

Public Class Methods

new(path, relations = {}) click to toggle source

Create a new Document.

path - the path to the file relations - a hash with keys :site and :collection, the values of which

are the Jekyll::Site and Jekyll::Collection to which this
Document belong.

Returns nothing.

# File lib/jekyll/document.rb, line 21
def initialize(path, relations = {})
  @site = relations[:site]
  @path = path
  @extname = File.extname(path)
  @collection = relations[:collection]
  @has_yaml_header = nil

  if draft?
    categories_from_path("_drafts")
  else
    categories_from_path(collection.relative_directory)
  end

  data.default_proc = proc do |_, key|
    site.frontmatter_defaults.find(relative_path, collection.label, key)
  end

  trigger_hooks(:post_init)
end

Public Instance Methods

<=>(other) click to toggle source

Compare this document against another document. Comparison is a comparison between the 2 paths of the documents.

Returns -1, 0, +1 or nil depending on whether this doc's path is less than,

equal or greater than the other doc's path. See String#<=> for more details.
# File lib/jekyll/document.rb, line 366
def <=>(other)
  return nil unless other.respond_to?(:data)
  cmp = data["date"] <=> other.data["date"]
  cmp = path <=> other.path if cmp.nil? || cmp.zero?
  cmp
end
[](key) click to toggle source
# File lib/jekyll/document.rb, line 213
def [](key)
  data[key]
end
asset_file?() click to toggle source

Determine whether the document is an asset file. Asset files include CoffeeScript files and Sass/SCSS files.

Returns true if the extname belongs to the set of extensions

that asset files use.
# File lib/jekyll/document.rb, line 145
def asset_file?
  sass_file? || coffeescript_file?
end
basename() click to toggle source

The base filename of the document.

Returns the base filename of the document.

# File lib/jekyll/document.rb, line 113
def basename
  @basename ||= File.basename(path)
end
basename_without_ext() click to toggle source

The base filename of the document, without the file extname.

Returns the basename without the file extname.

# File lib/jekyll/document.rb, line 106
def basename_without_ext
  @basename_without_ext ||= File.basename(path, ".*")
end
categories_from_path(special_dir) click to toggle source

Add superdirectories of the special_dir to categories. In the case of es/_posts, 'es' is added as a category. In the case of _posts/es, 'es' is NOT added as a category.

Returns nothing.

# File lib/jekyll/document.rb, line 315
def categories_from_path(special_dir)
  superdirs = relative_path.sub(%r#{special_dir}(.*)!, "")
    .split(File::SEPARATOR)
    .reject do |c|
    c.empty? || c.eql?(special_dir) || c.eql?(basename)
  end
  merge_data!({ "categories" => superdirs }, :source => "file path")
end
cleaned_relative_path() click to toggle source

Produces a “cleaned” relative path. The “cleaned” relative path is the relative path without the extname

and with the collection's directory removed as well.

This method is useful when building the URL of the document.

Examples:

When relative_path is "_methods/site/generate.md":
  cleaned_relative_path
  # => "/site/generate"

Returns the cleaned relative path of the document.

# File lib/jekyll/document.rb, line 128
def cleaned_relative_path
  @cleaned_relative_path ||=
    relative_path[0..-extname.length - 1].sub(collection.relative_directory, "")
end
coffeescript_file?() click to toggle source

Determine whether the document is a CoffeeScript file.

Returns true if extname == .coffee, false otherwise.

# File lib/jekyll/document.rb, line 159
def coffeescript_file?
  ".coffee" == extname
end
data() click to toggle source

Fetch the Document's data.

Returns a Hash containing the data. An empty hash is returned if

no data was read.
# File lib/jekyll/document.rb, line 45
def data
  @data ||= {}
end
date() click to toggle source
# File lib/jekyll/document.rb, line 69
def date
  data["date"] ||= (draft? ? source_file_mtime : site.time)
end
destination(base_directory) click to toggle source

The full path to the output file.

base_directory - the base path of the output directory

Returns the full path to the output file of this document.

# File lib/jekyll/document.rb, line 222
def destination(base_directory)
  dest = site.in_dest_dir(base_directory)
  path = site.in_dest_dir(dest, URL.unescape_path(url))
  if url.end_with? "/"
    path = File.join(path, "index.html")
  else
    path << output_ext unless path.end_with? output_ext
  end
  path
end
draft?() click to toggle source

Returns whether the document is a draft. This is only the case if the document is in the 'posts' collection but in a different directory than '_posts'.

Returns whether the document is a draft.

# File lib/jekyll/document.rb, line 82
def draft?
  data["draft"] ||= relative_path.index(collection.relative_directory).nil? &&
    collection.label == "posts"
end
excerpt_separator() click to toggle source

The Document #excerpt_separator, from the YAML Front-Matter or site default #excerpt_separator value

Returns the document #excerpt_separator

# File lib/jekyll/document.rb, line 386
def excerpt_separator
  (data["excerpt_separator"] || site.config["excerpt_separator"]).to_s
end
generate_excerpt?() click to toggle source

Whether to generate an excerpt

Returns true if the excerpt separator is configured.

# File lib/jekyll/document.rb, line 393
def generate_excerpt?
  !excerpt_separator.empty?
end
id() click to toggle source
# File lib/jekyll/document.rb, line 416
def id
  @id ||= File.join(File.dirname(url), (data["slug"] || basename_without_ext).to_s)
end
inspect() click to toggle source

The inspect string for this document. Includes the relative path and the collection label.

Returns the inspect string for this document.

# File lib/jekyll/document.rb, line 350
def inspect
  "#<Jekyll::Document #{relative_path} collection=#{collection.label}>"
end
merge_data!(other, source: "YAML front matter") click to toggle source

Merge some data in with this document's data.

Returns the merged data.

# File lib/jekyll/document.rb, line 52
def merge_data!(other, source: "YAML front matter")
  if other.key?("categories") && !other["categories"].nil?
    if other["categories"].is_a?(String)
      other["categories"] = other["categories"].split(" ").map(&:strip)
    end
    other["categories"] = (data["categories"] || []) | other["categories"]
  end
  Utils.deep_merge_hashes!(data, other)
  if data.key?("date") && !data["date"].is_a?(Time)
    data["date"] = Utils.parse_date(
      data["date"].to_s,
      "Document '#{relative_path}' does not have a valid date in the #{source}."
    )
  end
  data
end
method_missing(method, *args, &blck) click to toggle source

Override of #method_missing to check in @data for the key.

Calls superclass method
# File lib/jekyll/document.rb, line 434
def method_missing(method, *args, &blck)
  if data.key?(method.to_s)
    Jekyll::Deprecator.deprecation_message "Document##{method} is now a key "                            "in the #data hash."
    Jekyll::Deprecator.deprecation_message "Called by #{caller.first}."
    data[method.to_s]
  else
    super
  end
end
next_doc() click to toggle source
# File lib/jekyll/document.rb, line 397
def next_doc
  pos = collection.docs.index { |post| post.equal?(self) }
  if pos && pos < collection.docs.length - 1
    collection.docs[pos + 1]
  end
end
output_ext() click to toggle source

The output extension of the document.

Returns the output extension

# File lib/jekyll/document.rb, line 99
def output_ext
  Jekyll::Renderer.new(site, self).output_ext
end
place_in_layout?() click to toggle source

Determine whether the file should be placed into layouts.

Returns false if the document is either an asset file or a yaml file,

true otherwise.
# File lib/jekyll/document.rb, line 175
def place_in_layout?
  !(asset_file? || yaml_file?)
end
populate_categories() click to toggle source
# File lib/jekyll/document.rb, line 324
def populate_categories
  merge_data!({
    "categories" => (
      Array(data["categories"]) +
      Utils.pluralized_array_from_hash(data, "category", "categories")
    ).map(&:to_s).flatten.uniq
  })
end
populate_tags() click to toggle source
# File lib/jekyll/document.rb, line 333
def populate_tags
  merge_data!({
    "tags" => Utils.pluralized_array_from_hash(data, "tag", "tags").flatten
  })
end
post_read() click to toggle source
# File lib/jekyll/document.rb, line 289
def post_read
  if relative_path =~ DATE_FILENAME_MATCHER
    date, slug, ext = Regexp.last_match.captures
    if !data["date"] || data["date"].to_i == site.time.to_i
      merge_data!({ "date" => date }, :source => "filename")
    end
  elsif relative_path =~ DATELESS_FILENAME_MATCHER
    slug, ext = Regexp.last_match.captures
  end

  # Try to ensure the user gets a title.
  data["title"] ||= Utils.titleize_slug(slug)
  # Only overwrite slug & ext if they aren't specified.
  data["slug"] ||= slug
  data["ext"]  ||= ext

  populate_categories
  populate_tags
  generate_excerpt
end
previous_doc() click to toggle source
# File lib/jekyll/document.rb, line 404
def previous_doc
  pos = collection.docs.index { |post| post.equal?(self) }
  if pos && pos > 0
    collection.docs[pos - 1]
  end
end
published?() click to toggle source

Whether the file is published or not, as indicated in YAML front-matter

Returns 'false' if the 'published' key is specified in the YAML front-matter and is 'false'. Otherwise returns 'true'.

# File lib/jekyll/document.rb, line 250
def published?
  !(data.key?("published") && data["published"] == false)
end
read(opts = {}) click to toggle source

Read in the file and assign the content and data based on the file contents. Merge the frontmatter of the file with the frontmatter default values

Returns nothing.

# File lib/jekyll/document.rb, line 259
def read(opts = {})
  Jekyll.logger.debug "Reading:", relative_path

  if yaml_file?
    @data = SafeYAML.load_file(path)
  else
    begin
      defaults = @site.frontmatter_defaults.all(
        relative_path,
        collection.label.to_sym
      )
      merge_data!(defaults, :source => "front matter defaults") unless defaults.empty?

      self.content = File.read(path, Utils.merged_file_read_opts(site, opts))
      if content =~ YAML_FRONT_MATTER_REGEXP
        self.content = $POSTMATCH
        data_file = SafeYAML.load(Regexp.last_match(1))
        merge_data!(data_file, :source => "YAML front matter") if data_file
      end

      post_read
    rescue SyntaxError => e
      Jekyll.logger.error "Error:", "YAML Exception reading #{path}: #{e.message}"
    rescue => e
      raise e if e.is_a? Jekyll::Errors::FatalException
      Jekyll.logger.error "Error:", "could not read file #{path}: #{e.message}"
    end
  end
end
relative_path() click to toggle source

The path to the document, relative to the site source.

Returns a String path which represents the relative path

from the site source to this document
# File lib/jekyll/document.rb, line 91
def relative_path
  @relative_path ||= Pathname.new(path)
    .relative_path_from(Pathname.new(site.source)).to_s
end
render_with_liquid?() click to toggle source

Determine whether the file should be rendered with Liquid.

Returns false if the document is either an asset file or a yaml file,

true otherwise.
# File lib/jekyll/document.rb, line 167
def render_with_liquid?
  !(coffeescript_file? || yaml_file?)
end
respond_to?(method, include_private = false) click to toggle source

Override of normal respond_to? to match #method_missing's logic for looking in @data.

Calls superclass method
# File lib/jekyll/document.rb, line 429
def respond_to?(method, include_private = false)
  data.key?(method.to_s) || super
end
sass_file?() click to toggle source

Determine whether the document is a Sass file.

Returns true if extname == .sass or .scss, false otherwise.

# File lib/jekyll/document.rb, line 152
def sass_file?
  %w(.sass .scss).include?(extname)
end
source_file_mtime() click to toggle source
# File lib/jekyll/document.rb, line 73
def source_file_mtime
  @source_file_mtime ||= File.mtime(path)
end
to_liquid() click to toggle source

Create a Liquid-understandable version of this Document.

Returns a Hash representing this Document's data.

# File lib/jekyll/document.rb, line 342
def to_liquid
  @to_liquid ||= Drops::DocumentDrop.new(self)
end
to_s() click to toggle source

The string representation for this document.

Returns the content of the document

# File lib/jekyll/document.rb, line 357
def to_s
  output || content || "NO CONTENT"
end
trigger_hooks(hook_name, *args) click to toggle source
# File lib/jekyll/document.rb, line 411
def trigger_hooks(hook_name, *args)
  Jekyll::Hooks.trigger collection.label.to_sym, hook_name, self, *args if collection
  Jekyll::Hooks.trigger :documents, hook_name, self, *args
end
url() click to toggle source

The computed URL for the document. See `Jekyll::URL#to_s` for more details.

Returns the computed URL for the document.

# File lib/jekyll/document.rb, line 205
def url
  @url = URL.new({
    :template     => url_template,
    :placeholders => url_placeholders,
    :permalink    => permalink
  }).to_s
end
url_placeholders() click to toggle source

Construct a Hash of key-value pairs which contain a mapping between

a key in the URL template and the corresponding value for this document.

Returns the Hash of key-value pairs for replacement in the URL.

# File lib/jekyll/document.rb, line 190
def url_placeholders
  @url_placeholders ||= Drops::UrlDrop.new(self)
end
url_template() click to toggle source

The URL template where the document would be accessible.

Returns the URL template for the document.

# File lib/jekyll/document.rb, line 182
def url_template
  collection.url_template
end
write(dest) click to toggle source

Write the generated Document file to the destination directory.

dest - The String path to the destination dir.

Returns nothing.

# File lib/jekyll/document.rb, line 238
def write(dest)
  path = destination(dest)
  FileUtils.mkdir_p(File.dirname(path))
  File.write(path, output, :mode => "wb")

  trigger_hooks(:post_write)
end
write?() click to toggle source

Determine whether this document should be written. Based on the Collection to which it belongs.

True if the document has a collection and if that collection's write?

method returns true, otherwise false.
# File lib/jekyll/document.rb, line 378
def write?
  collection && collection.write?
end
yaml_file?() click to toggle source

Determine whether the document is a YAML file.

Returns true if the extname is either .yml or .yaml, false otherwise.

# File lib/jekyll/document.rb, line 136
def yaml_file?
  %w(.yaml .yml).include?(extname)
end

Private Instance Methods

generate_excerpt() click to toggle source
# File lib/jekyll/document.rb, line 446
def generate_excerpt
  if generate_excerpt?
    data["excerpt"] ||= Jekyll::Excerpt.new(self)
  end
end