Class/Module Index [+]

Quicksearch

Nanoc::DataSources::Filesystem

Provides functionality common across all filesystem data sources.

Attributes

vcs[W]

Public Instance Methods

create_item(content, attributes, identifier, params={}) click to toggle source

See {Nanoc::DataSource#create_item}.

# File lib/nanoc/data_sources/filesystem.rb, line 45
def create_item(content, attributes, identifier, params={})
  create_object('content', content, attributes, identifier, params)
end
create_layout(content, attributes, identifier, params={}) click to toggle source

See {Nanoc::DataSource#create_layout}.

# File lib/nanoc/data_sources/filesystem.rb, line 50
def create_layout(content, attributes, identifier, params={})
  create_object('layouts', content, attributes, identifier, params)
end
down() click to toggle source

See {Nanoc::DataSource#down}.

# File lib/nanoc/data_sources/filesystem.rb, line 22
def down
end
items() click to toggle source

See {Nanoc::DataSource#items}.

# File lib/nanoc/data_sources/filesystem.rb, line 35
def items
  load_objects('content', 'item', Nanoc::Item)
end
layouts() click to toggle source

See {Nanoc::DataSource#layouts}.

# File lib/nanoc/data_sources/filesystem.rb, line 40
def layouts
  load_objects('layouts', 'layout', Nanoc::Layout)
end
setup() click to toggle source

See {Nanoc::DataSource#setup}.

# File lib/nanoc/data_sources/filesystem.rb, line 26
def setup
  # Create directories
  %( content layouts ).each do |dir|
    FileUtils.mkdir_p(dir)
    vcs.add(dir)
  end
end
up() click to toggle source

See {Nanoc::DataSource#up}.

# File lib/nanoc/data_sources/filesystem.rb, line 18
def up
end
vcs() click to toggle source

The VCS that will be called when adding, deleting and moving files. If no VCS has been set, or if the VCS has been set to `nil`, a dummy VCS will be returned.

@return [Nanoc::Extra::VCS, nil] The VCS that will be used.

# File lib/nanoc/data_sources/filesystem.rb, line 12
def vcs
  @vcs ||= Nanoc::Extra::VCSes::Dummy.new
end

Protected Instance Methods

all_files_in(dir_name) click to toggle source

Returns all files in the given directory and directories below it.

# File lib/nanoc/data_sources/filesystem.rb, line 176
def all_files_in(dir_name)
  Nanoc::Extra::FilesystemTools.all_files_in(dir_name)
end
all_split_files_in(dir_name) click to toggle source

Finds all items/layouts/... in the given base directory. Returns a hash in which the keys are the file's dirname + basenames, and the values a pair consisting of the metafile extension and the content file extension. The meta file extension or the content file extension can be nil, but not both. Backup files are ignored. For example:

{
  'content/foo' => [ 'yaml', 'html' ],
  'content/bar' => [ 'yaml', nil    ],
  'content/qux' => [ nil,    'html' ]
}
# File lib/nanoc/data_sources/filesystem.rb, line 144
def all_split_files_in(dir_name)
  # Get all good file names
  filenames = self.all_files_in(dir_name)
  filenames.reject! { |fn| fn =~ /(~|\.orig|\.rej|\.bak)$/ }

  # Group by identifier
  grouped_filenames = filenames.group_by { |fn| basename_of(fn) }

  # Convert values into metafile/content file extension tuple
  grouped_filenames.each_pair do |key, filenames|
    # Divide
    meta_filenames    = filenames.select { |fn| ext_of(fn) == '.yaml' }
    content_filenames = filenames.select { |fn| ext_of(fn) != '.yaml' }

    # Check number of files per type
    if ![ 0, 1 ].include?(meta_filenames.size)
      raise RuntimeError, "Found #{meta_filenames.size} meta files for #{key}; expected 0 or 1"
    end
    if ![ 0, 1 ].include?(content_filenames.size)
      raise RuntimeError, "Found #{content_filenames.size} content files for #{key}; expected 0 or 1"
    end

    # Reorder elements and convert to extnames
    filenames[0] = meta_filenames[0]    ? 'yaml'                                   : nil
    filenames[1] = content_filenames[0] ? ext_of(content_filenames[0])[1..-1] || '': nil
  end

  # Done
  grouped_filenames
end
basename_of(filename) click to toggle source

Returns the base name of filename, i.e. filename with the first or all extensions stripped off. By default, all extensions are stripped off, but when allow_periods_in_identifiers is set to true in the site configuration, only the last extension will be stripped .

# File lib/nanoc/data_sources/filesystem.rb, line 208
def basename_of(filename)
  filename.sub(extension_regex, '')
end
create_object(dir_name, content, attributes, identifier, params={}) click to toggle source

Creates a new object (item or layout) on disk in dir_name according to the given identifier. The file will have its attributes taken from the attributes hash argument and its content from the content argument.

# File lib/nanoc/data_sources/filesystem.rb, line 59
def create_object(dir_name, content, attributes, identifier, params={})
  raise NotImplementedError.new(
    "#{self.class} does not implement ##{name}"
  )
end
ext_of(filename) click to toggle source

Returns the extension(s) of filename. Supports multiple extensions. Includes the leading period.

# File lib/nanoc/data_sources/filesystem.rb, line 214
def ext_of(filename)
  filename =~ extension_regex ? $1 : ''
end
extension_regex() click to toggle source

Returns a regex that is used for determining the extension of a file name. The first match group will be the entire extension, including the leading period.

# File lib/nanoc/data_sources/filesystem.rb, line 221
def extension_regex
  if @config && @config[:allow_periods_in_identifiers]
    /(\.[^\/\.]+$)/
  else
    /(\.[^\/]+$)/
  end
end
filename_for(base_filename, ext) click to toggle source

Returns the filename for the given base filename and the extension.

If the extension is nil, this function should return nil as well.

A simple implementation would simply concatenate the base filename, a period and an extension (which is what the {Nanoc::DataSources::FilesystemCompact} data source does), but other data sources may prefer to implement this differently (for example, {Nanoc::DataSources::FilesystemVerbose} doubles the last part of the basename before concatenating it with a period and the extension).

# File lib/nanoc/data_sources/filesystem.rb, line 190
def filename_for(base_filename, ext)
  raise NotImplementedError.new(
    "#{self.class} does not implement #filename_for"
  )
end
identifier_for_filename(filename) click to toggle source

Returns the identifier that corresponds with the given filename, which can be the content filename or the meta filename.

# File lib/nanoc/data_sources/filesystem.rb, line 198
def identifier_for_filename(filename)
  raise NotImplementedError.new(
    "#{self.class} does not implement #identifier_for_filename"
  )
end
load_objects(dir_name, kind, klass) click to toggle source

Creates instances of klass corresponding to the files in dir_name. The kind attribute indicates the kind of object that is being loaded and is used solely for debugging purposes.

This particular implementation loads objects from a filesystem-based data source where content and attributes can be spread over two separate files. The content and meta-file are optional (but at least one of them needs to be present, obviously) and the content file can start with a metadata section.

@see Nanoc::DataSources::Filesystem#load_objects

# File lib/nanoc/data_sources/filesystem.rb, line 76
def load_objects(dir_name, kind, klass)
  all_split_files_in(dir_name).map do |base_filename, (meta_ext, content_ext)|
    # Get filenames
    meta_filename    = filename_for(base_filename, meta_ext)
    content_filename = filename_for(base_filename, content_ext)

    # Read content and metadata
    is_binary = !!(content_filename && !@site.config[:text_extensions].include?(File.extname(content_filename)[1..-1]))
    if is_binary && klass == Nanoc::Item
      meta                = (meta_filename && YAML.load_file(meta_filename)) || {}
      content_or_filename = content_filename
    else
      meta, content_or_filename = parse(content_filename, meta_filename, kind)
    end

    # Get attributes
    attributes = {
      :filename         => content_filename,
      :content_filename => content_filename,
      :meta_filename    => meta_filename,
      :extension        => content_filename ? ext_of(content_filename)[1..-1] : nil,
      # WARNING :file is deprecated; please create a File object manually
      # using the :content_filename or :meta_filename attributes.
      # TODO [in nanoc 4.0] remove me
      :file             => content_filename ? Nanoc::Extra::FileProxy.new(content_filename) : nil
    }.merge(meta)

    # Get identifier
    if meta_filename
      identifier = identifier_for_filename(meta_filename[(dir_name.length+1)..-1])
    elsif content_filename
      identifier = identifier_for_filename(content_filename[(dir_name.length+1)..-1])
    else
      raise RuntimeError, "meta_filename and content_filename are both nil"
    end

    # Get modification times
    meta_mtime    = meta_filename    ? File.stat(meta_filename).mtime    : nil
    content_mtime = content_filename ? File.stat(content_filename).mtime : nil
    if meta_mtime && content_mtime
      mtime = meta_mtime > content_mtime ? meta_mtime : content_mtime
    elsif meta_mtime
      mtime = meta_mtime
    elsif content_mtime
      mtime = content_mtime
    else
      raise RuntimeError, "meta_mtime and content_mtime are both nil"
    end

    # Create layout object
    klass.new(
      content_or_filename, attributes, identifier,
      :binary => is_binary, :mtime => mtime
    )
  end
end
parse(content_filename, meta_filename, kind) click to toggle source

Parses the file named `filename` and returns an array with its first element a hash with the file's metadata, and with its second element the file content itself.

# File lib/nanoc/data_sources/filesystem.rb, line 232
def parse(content_filename, meta_filename, kind)
  # Read content and metadata from separate files
  if meta_filename
    content = content_filename ? read(content_filename) : ''
    meta_raw = read(meta_filename)
    begin
      meta = YAML.load(meta_raw) || {}
    rescue Exception => e
      raise "Could not parse YAML for #{meta_filename}: #{e.message}"
    end
    return [ meta, content ]
  end

  # Read data
  data = read(content_filename)

  # Check presence of metadata section
  if data !~ /\A-{3,5}\s*$/
    return [ {}, data ]
  end

  # Split data
  pieces = data.split(/^(-{5}|-{3})\s*$/)
  if pieces.size < 4
    raise RuntimeError.new(
      "The file '#{content_filename}' appears to start with a metadata section (three or five dashes at the top) but it does not seem to be in the correct format."
    )
  end

  # Parse
  begin
    meta = YAML.load(pieces[2]) || {}
  rescue Exception => e
    raise "Could not parse YAML for #{content_filename}: #{e.message}"
  end
  content = pieces[4..-1].join.strip

  # Done
  [ meta, content ]
end
raise_encoding_error(filename, encoding) click to toggle source

Raises an invalid encoding error for the given filename and encoding.

# File lib/nanoc/data_sources/filesystem.rb, line 305
def raise_encoding_error(filename, encoding)
  raise RuntimeError.new("Could not read #{filename} because the file is not valid #{encoding}.")
end
read(filename) click to toggle source

Reads the content of the file with the given name and returns a string in UTF-8 encoding. The original encoding of the string is derived from the default external encoding, but this can be overridden by the “encoding” configuration attribute in the data source configuration.

# File lib/nanoc/data_sources/filesystem.rb, line 277
def read(filename)
  # Read
  begin
    data = File.read(filename)
  rescue => e
    raise RuntimeError.new("Could not read #{filename}: #{e.inspect}")
  end

  # Fix
  if data.respond_to?(:encode!)
    if @config && @config[:encoding]
      original_encoding = Encoding.find(@config[:encoding])
      data.force_encoding(@config[:encoding])
    else
      original_encoding = data.encoding
    end

    data.encode!('UTF-8') rescue raise_encoding_error(filename, original_encoding)
    raise_encoding_error(filename, original_encoding) if !data.valid_encoding?
  end

  # Remove UTF-8 BOM (ugly)
  data.gsub!("\xEF\xBB\xBF", '')

  data
end

[Validate]

Generated with the Darkfish Rdoc Generator 2.