class ContentFS::Database

Structured content database, loaded from the filesystem.

Constants

METADATA_FILE

Attributes

metadata[R]
namespace[R]
prefix[R]
slug[R]

Public Class Methods

load(path, parent: nil, namespace: [], root: true, &block) click to toggle source
# File lib/contentfs/database.rb, line 14
def load(path, parent: nil, namespace: [], root: true, &block)
  new(path: path, parent: parent, namespace: namespace, root: root, &block)
end
new(path:, parent: nil, namespace: [], root: false, &block) click to toggle source
# File lib/contentfs/database.rb, line 23
def initialize(path:, parent: nil, namespace: [], root: false, &block)
  path = Pathname.new(path)
  name = path.basename(path.extname)
  prefix, remainder = Prefix.build(name)
  @prefix = prefix
  @namespace = namespace.dup
  @parent = parent

  unless root
    @slug = Slug.build(remainder)
    @namespace << @slug
  end

  metadata_path = path.join(METADATA_FILE)

  @metadata = if metadata_path.exist?
    YAML.safe_load(metadata_path.read).to_h
  else
    {}
  end

  content_path = path.join.glob("_content.*")[0]

  if content_path&.exist?
    @content = Content.load(content_path, database: self, metadata: @metadata, namespace: @namespace, &block)
    @metadata = @content.metadata.dup
  end

  children, nested, includes = {}, {}, {}
  Pathname.new(path).glob("*") do |path|
    underscored = path.basename.to_s.start_with?("_")
    next if underscored && path.directory?

    if path.directory?
      database = Database.load(path, parent: self, namespace: @namespace, root: false, &block)
      nested[database.slug] = database
    elsif underscored
      content = Content.load(path, database: self, metadata: @metadata, namespace: @namespace, &block)

      includes[content.slug.to_s[1..].to_sym] = content
    else
      content = Content.load(path, database: self, metadata: @metadata, namespace: @namespace, &block)

      children[content.slug] = content
    end
  end

  @children = Hash[
    children.sort_by { |key, content|
      (content.prefix || content.slug).to_s
    }
  ]

  @nested = Hash[
    nested.sort_by { |key, database|
      (database.prefix || database.slug).to_s
    }
  ]

  @includes = Hash[
    includes.sort_by { |key, content|
      (content.prefix || content.slug).to_s
    }
  ]
end

Public Instance Methods

content() { |value| ... } click to toggle source
# File lib/contentfs/database.rb, line 89
def content
  return to_enum(:content) unless block_given?

  @children.each_value do |value|
    yield value
  end
end
filter(**filters) { |content| ... } click to toggle source
# File lib/contentfs/database.rb, line 105
def filter(**filters)
  return to_enum(:filter, **filters) unless block_given?

  filters = filters.each_with_object({}) { |(key, value), hash|
    hash[key.to_s] = value
  }

  @children.each_value.select { |content|
    yield content if content.metadata.all? { |key, value|
      filters[key] == value
    }
  }
end
find(name, *nested) click to toggle source
# File lib/contentfs/database.rb, line 119
def find(name, *nested)
  if @children.key?(name)
    @children[name]
  elsif @nested.key?(name)
    nested.inject(@nested[name]) { |database, next_nested|
      database.find(next_nested.to_sym)
    }
  end
end
find_include(path) click to toggle source
# File lib/contentfs/database.rb, line 129
def find_include(path)
  @includes[path.to_sym] || find_child_include(path) || find_parent_include(path) || find_include_from_toplevel(path)
end
method_missing(name, *nested, **) click to toggle source
Calls superclass method
# File lib/contentfs/database.rb, line 166
def method_missing(name, *nested, **)
  find(name, *nested) || super
end
nested() { |value| ... } click to toggle source
# File lib/contentfs/database.rb, line 97
def nested
  return to_enum(:nested) unless block_given?

  @nested.each_value do |value|
    yield value
  end
end
render() click to toggle source
# File lib/contentfs/database.rb, line 162
def render
  @content&.render
end
respond_to_missing?(name, *) click to toggle source
Calls superclass method
# File lib/contentfs/database.rb, line 170
def respond_to_missing?(name, *)
  @children.key?(name) || super
end
to_s() click to toggle source
# File lib/contentfs/database.rb, line 158
def to_s
  @content&.to_s.to_s
end
toplevel() click to toggle source
# File lib/contentfs/database.rb, line 133
def toplevel
  @parent ? @parent.toplevel : self
end

Private Instance Methods

find_child_include(path) click to toggle source
# File lib/contentfs/database.rb, line 137
        def find_child_include(path)
  return unless path.include?("/")

  path_parts = path.split("/", 2)
  @nested[path_parts[0].to_sym]&.find_include(path_parts[1])
end
find_include_from_toplevel(path) click to toggle source
# File lib/contentfs/database.rb, line 152
        def find_include_from_toplevel(path)
  return if @parent.nil?

  toplevel.find_include(path)
end
find_parent_include(path) click to toggle source
# File lib/contentfs/database.rb, line 144
        def find_parent_include(path)
  return if @parent.nil?
  return unless path.start_with?("../")

  path_parts = path.split("../", 2)
  @parent.find_include(path_parts[1])
end