obsidian-parser 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 347ae1e2c1152ccae4ae2ad6a19c4477ae07a8dca77dcfd73d1e31da823cf5f6
4
- data.tar.gz: 4d2257fd34be583429baf260e3136ca6cdf6cdb4d66fbd592abc396e81a32dfa
3
+ metadata.gz: 041aa2c11a3972e24477c8601a880276de98b0108ef7628d9c30b623a1b14cb9
4
+ data.tar.gz: 1843bb05271c17a14658c956b7418472f19a5c460d95708f9f3e3fbe7d115d09
5
5
  SHA512:
6
- metadata.gz: 99cfb58610e351574d4ca83faa28b90117ae63a2d219151f005d87e365ee80704d44759c2610cd6bf9b8301fec0ffeb4245d112231fa9ace90202283ad094758
7
- data.tar.gz: a032cb2063e781db38c5ecafbe13f047d36e16cb5d71cf4c6b8558e40a6867d20b510a25aabbfe2201f5ce45a80a0ca7c729f1c5bd3e78f8030f01be812f61ac
6
+ metadata.gz: 8c21ac0b8b64e487cfbdf7f35c15b486c4094a69dd4d068a90e5d7135159efd148f5df7aaa3ac2a52f327849cb30c5bd698938f0bca9ae4d45e558ab9cb142f2
7
+ data.tar.gz: e7f08dda0226319f82135e7bf7374c0f9b6448497f9e7e89a6b1e29a5d0fd0a281a0415d4fc246aae645a91b9cb8a2fb2ac024c941ec2ded63f5089a5ff51dec
data/CHANGELOG.md CHANGED
@@ -1,12 +1,18 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.4.0] - 2023-07-30
4
+ - Unify `Note` and `Index` classes into `Page`. This is a breaking API change. `Parser#notes is replaced by Parse#pages`. Call `Page#is_index?`to distinguish between directory derived pages and documents.
5
+ - Remove `Parser#table_of_contents` and `Parser#walk_tree`.
6
+ - Add `Page#find_in_tree` to recursively search for a page with a matching slug.
7
+ - Rename `Obsidian::MarkdownConverter` to `Obsidian::ParsedMarkdownDocument`
8
+
3
9
  ## [0.3.0] - 2023-07-27
4
10
 
5
- - Note objects have a `parent` attribute.
11
+ - `Note` objects have a `parent` attribute.
6
12
 
7
13
  ## [0.2.0] - 2023-07-24
8
14
 
9
- - Note objects have a `content` attribute. Call `content.generate_html` to generate HTML on demand.
15
+ - `Note` objects have a `content` attribute. Call `content.generate_html` to generate HTML on demand.
10
16
 
11
17
  ## [0.1.0] - 2023-07-10
12
18
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- obsidian-parser (0.3.0)
4
+ obsidian-parser (0.4.0)
5
5
  kramdown (~> 2.4)
6
6
  kramdown-parser-gfm (~> 1.1)
7
7
 
@@ -34,7 +34,7 @@ module Obsidian
34
34
  # Parse links from obsidian-flavored-markdown text
35
35
  def self.parse(markdown_text)
36
36
  document = Kramdown::Document.new(normalize(markdown_text), input: "GFM")
37
- Obsidian::MarkdownConverter.new(document)
37
+ Obsidian::ParsedMarkdownDocument.new(document)
38
38
  end
39
39
  end
40
40
  end
@@ -0,0 +1,124 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Obsidian
4
+ # A page in the vault corresponding to either a markdown document,
5
+ # or a directory containing other documents.
6
+ #
7
+ # If a directory contains an index.md, that is used as the content of
8
+ # the directory page; otherwise content will be nil.
9
+ class Page
10
+ def self.create_root
11
+ Page.new(title: "", slug: "")
12
+ end
13
+
14
+ def initialize(title:, slug:, last_modified: nil, content: nil, parent: nil)
15
+ # TODO: check frontmatter for titles as well
16
+ @title = title
17
+ @slug = slug
18
+ @last_modified = last_modified
19
+ @content = content
20
+ @parent = parent
21
+ @children = {}
22
+ end
23
+
24
+ def is_index?
25
+ !children.empty?
26
+ end
27
+
28
+ def inspect
29
+ "Page(title: #{title.inspect}, slug: #{slug.inspect})"
30
+ end
31
+
32
+ def ==(other)
33
+ self.class == other.class &&
34
+ !slug.nil? &&
35
+ slug == other&.slug
36
+ end
37
+
38
+ alias_method :eql?, :==
39
+
40
+ def hash
41
+ slug.hash
42
+ end
43
+
44
+ # Add a note to the tree based on its slug.
45
+ # Call this method on the root page.
46
+ # When calling this method, you must ensure that anscestor pages
47
+ # are added before their descendents.
48
+ def add_page(slug, last_modified: nil, content: nil)
49
+ path_components = slug.split("/")
50
+ raise ArgumentError, "Expecting non-empty slug" if path_components.empty?
51
+
52
+ title = path_components.pop
53
+
54
+ parent = path_components.reduce(self) do |index, anscestor_title|
55
+ anscestor_slug = Obsidian.build_slug(anscestor_title, index.slug)
56
+ index.get_or_create_child(slug: anscestor_slug, title: anscestor_title)
57
+ end
58
+
59
+ parent.get_or_create_child(
60
+ title: title,
61
+ slug: slug,
62
+ last_modified: last_modified,
63
+ content: content
64
+ )
65
+ end
66
+
67
+ def get_or_create_child(title:, slug:, last_modified: nil, content: nil)
68
+ # TODO: validate slug matches the current page slug
69
+
70
+ @children[title] ||= Page.new(
71
+ slug: slug,
72
+ title: title,
73
+ last_modified: last_modified,
74
+ content: content,
75
+ parent: self
76
+ )
77
+ end
78
+
79
+ def children
80
+ @children.values.sort_by { |c| [c.is_index? ? 1 : 0, c.title] }
81
+ end
82
+
83
+ def walk_tree(&block)
84
+ children.each do |page|
85
+ block.call(page)
86
+ page.walk_tree(&block)
87
+ end
88
+ end
89
+
90
+ # Return the page that matches a slug.
91
+ # If there is an exact match, we should always return that
92
+ # Otherwise, if we can skip over some anscestors and get a
93
+ # match, then return the first, shortest match.
94
+ def find_in_tree(query_slug)
95
+ # Exact match
96
+ return self if slug == query_slug
97
+
98
+ # Partial match
99
+ query_parts = query_slug.split("/")
100
+ length = query_parts.size
101
+ slug_parts = slug.split("/")
102
+
103
+ if slug_parts.length > length
104
+ if slug_parts.slice(-length, length) == query_parts
105
+ return self
106
+ end
107
+ end
108
+
109
+ # Recurse
110
+ children.each do |child|
111
+ result = child.find_in_tree(query_slug)
112
+ return result unless result.nil?
113
+ end
114
+
115
+ nil
116
+ end
117
+
118
+ attr_reader :title
119
+ attr_reader :slug
120
+ attr_reader :last_modified
121
+ attr_reader :content
122
+ attr_reader :parent
123
+ end
124
+ end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Obsidian
4
- class MarkdownConverter
4
+ class ParsedMarkdownDocument
5
5
  def initialize(document)
6
6
  @document = document
7
7
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Obsidian
4
4
  class Parser
5
- VERSION = "0.3.0"
5
+ VERSION = "0.4.0"
6
6
  end
7
7
  end
@@ -1,8 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "parser/version"
4
- require_relative "parser/markdown_converter"
4
+ require_relative "parser/parsed_markdown_document"
5
5
  require_relative "parser/obsidian_flavored_markdown"
6
+ require_relative "parser/page"
7
+
8
+ require "forwardable"
6
9
 
7
10
  module Obsidian
8
11
  class Error < StandardError; end
@@ -22,76 +25,11 @@ module Obsidian
22
25
  end
23
26
  end
24
27
 
25
- class Note
26
- def initialize(title, slug, last_modified, content: nil, parent: nil)
27
- # TODO: check frontmatter for titles as well
28
- @title = title
29
- @slug = slug
30
- @last_modified = last_modified
31
- @content = content
32
- @parent = parent
33
- end
34
-
35
- def inspect
36
- "Note(title: #{title.inspect}, slug: #{slug.inspect})"
37
- end
38
-
39
- attr_reader :title
40
- attr_reader :slug
41
- attr_reader :last_modified
42
- attr_reader :content
43
- attr_reader :parent
44
- end
45
-
46
- class Index
47
- def initialize(title = "", slug = "", content: nil)
48
- @title = title
49
- @slug = slug
50
- @notes = []
51
- @directories = {}
52
- @content = content
53
- end
54
-
55
- def add_directory(title)
56
- new_slug = Obsidian.build_slug(title, slug)
57
- @directories[title] ||= Index.new(title, new_slug)
58
- end
59
-
60
- def add_note(title, parent_slug, last_modified, content: nil)
61
- slug = Obsidian.build_slug(title, parent_slug)
62
- directory = nested_directory(parent_slug.split("/"))
63
- note = Note.new(title, slug, last_modified, content: content, parent: directory)
64
-
65
- directory.notes << note
66
-
67
- note
68
- end
69
-
70
- def inspect
71
- "Index(title: #{title.inspect}, slug: #{slug.inspect})"
72
- end
73
-
74
- def directories
75
- @directories.values
76
- end
77
-
78
- attr_reader :notes
79
- attr_reader :title
80
- attr_reader :slug
81
- attr_reader :content
82
-
83
- private
84
-
85
- def nested_directory(path_components)
86
- path_components.reduce(self) { |index, subdirectory| index.add_directory(subdirectory) }
87
- end
88
- end
89
-
90
28
  class Parser
91
29
  attr_reader :index
92
30
 
93
31
  def initialize(vault_directory)
94
- @index = Index.new("", "")
32
+ @index = Obsidian::Page.create_root
95
33
 
96
34
  vault_directory.glob("**/*.md").each do |path|
97
35
  dirname, basename = path.relative_path_from(vault_directory).split
@@ -102,32 +40,23 @@ module Obsidian
102
40
  if basename != "index.md"
103
41
  title = basename.to_s.gsub(/\.md\z/, "")
104
42
  parent_slug = dirname.to_s.gsub(/\A\.\/?/, "")
43
+ slug = Obsidian.build_slug(title, parent_slug)
105
44
  content = MarkdownContent.new(path)
106
- @index.add_note(title, parent_slug, path.mtime, content: content)
45
+
46
+ @index.add_page(
47
+ slug,
48
+ last_modified: path.mtime,
49
+ content: content
50
+ )
107
51
  end
108
52
  end
109
53
 
110
54
  # TODO: capture links between notes
111
55
  end
112
56
 
113
- def notes
114
- table_of_contents.map(&:first)
115
- end
116
-
117
- def walk_tree(index, level = 0, &block)
118
- index.directories.sort_by(&:title).each do |note|
119
- block.call(note, level)
120
- walk_tree(note, level + 1, &block)
121
- end
122
-
123
- index.notes.sort_by(&:title).each do |note|
124
- block.call(note, level)
125
- end
126
- end
127
-
128
- def table_of_contents
57
+ def pages
129
58
  result = []
130
- walk_tree(index) { |note, level| result << [note, level] }
59
+ index.walk_tree { |page| result << page }
131
60
  result
132
61
  end
133
62
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: obsidian-parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mat Moore
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2023-07-27 00:00:00.000000000 Z
11
+ date: 2023-07-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: kramdown
@@ -56,8 +56,9 @@ files:
56
56
  - README.md
57
57
  - Rakefile
58
58
  - lib/obsidian/parser.rb
59
- - lib/obsidian/parser/markdown_converter.rb
60
59
  - lib/obsidian/parser/obsidian_flavored_markdown.rb
60
+ - lib/obsidian/parser/page.rb
61
+ - lib/obsidian/parser/parsed_markdown_document.rb
61
62
  - lib/obsidian/parser/version.rb
62
63
  - sig/obsidian/parser.rbs
63
64
  homepage: