Skip to content

Commit

Permalink
Unify Note and Index classes into Page.
Browse files Browse the repository at this point in the history
Once integrated with the parser, this will be a breaking API change.

- Any `Page` may contain child pages and belong to any other page.
- Use `Page#is_index?` to check if it is an "index" page - i.e. a page
that has children.
- Page entities are considered equal if their slug is equal
- Added `Page.create_root` factory method
  • Loading branch information
MatMoore committed Jul 28, 2023
1 parent db93c32 commit 3e780e3
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 0 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
## [Unreleased]

- Rename `Obsidian::MarkdownConverter` to `Obsidian::ParsedMarkdownDocument`
- Unify `Note` and `Index` classes into `Page`. This is a breaking API change.

## [0.3.0] - 2023-07-27

Expand Down
1 change: 1 addition & 0 deletions lib/obsidian/parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require_relative "parser/version"
require_relative "parser/parsed_markdown_document"
require_relative "parser/obsidian_flavored_markdown"
require_relative "parser/page"

module Obsidian
class Error < StandardError; end
Expand Down
87 changes: 87 additions & 0 deletions lib/obsidian/parser/page.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# frozen_string_literal: true

module Obsidian
# A page in the vault corresponding to either a markdown document,
# or a directory containing other documents.
#
# If a directory contains an index.md, that is used as the content of
# the directory page; otherwise content will be nil.
class Page
def self.create_root
Page.new(title: "", slug: "")
end

def initialize(title:, slug:, last_modified: nil, content: nil, parent: nil)
# TODO: check frontmatter for titles as well
@title = title
@slug = slug
@last_modified = last_modified
@content = content
@parent = parent
@children = {}
end

def is_index?
!children.empty?
end

def inspect
"Page(title: #{title.inspect}, slug: #{slug.inspect})"
end

def ==(other)
!slug.nil? && slug == other.slug
end

alias_method :eql?, :==

def hash
slug.hash
end

# Add a note to the tree based on its slug.
# Call this method on the root page.
# When calling this method, you must ensure that anscestor pages
# are added before their descendents.
def add_page(slug, last_modified: nil, content: nil)
path_components = slug.split("/")
raise ArgumentError, "Expecting non-empty slug" if path_components.empty?

title = path_components.pop

parent = path_components.reduce(self) do |index, anscestor_title|
anscestor_slug = Obsidian.build_slug(anscestor_title, index.slug)
index.get_or_create_child(slug: anscestor_slug, title: anscestor_title)
end

parent.get_or_create_child(
title: title,
slug: slug,
last_modified: last_modified,
content: content
)
end

def get_or_create_child(title:, slug:, last_modified: nil, content: nil)
# TODO: validate slug matches the current page slug

@children[title] ||= Page.new(
slug: slug,
title: title,
last_modified: last_modified,
content: content,
parent: self
)
end

def children
@children.values.sort_by { |c| [c.is_index? ? 1 : 0, c.title] }
end

attr_reader :title
attr_reader :slug
attr_reader :last_modified
attr_reader :content
attr_reader :parent
end
end
40 changes: 40 additions & 0 deletions spec/obsidian/parser/page_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# frozen_string_literal: true

RSpec.describe Obsidian::Page do
subject(:root) { described_class.create_root }

describe("#add_page") do
it "relates too pages" do
page = root.add_page("foo")

expect(page.parent).to eq(root)
expect(root.children).to eq([page])
end

it "assigns titles and slugs to a top level page" do
page = root.add_page("foo")

expect(page.slug).to eq("foo")
expect(page.title).to eq("foo")
end

it "assigns titles and slugs to a nested page" do
page = root.add_page("foo/bar/baz")

expect(page.slug).to eq("foo/bar/baz")
expect(page.title).to eq("baz")
end

it "infers missing directory pages" do
page = root.add_page("foo/bar/baz")
parent = page.parent
grandparent = parent.parent

expect(parent.slug).to eq("foo/bar")
expect(grandparent.slug).to eq("foo")
expect(parent.children).to eq([page])
expect(grandparent.children).to eq([parent])
expect(root.children).to eq([grandparent])
end
end
end

0 comments on commit 3e780e3

Please sign in to comment.