contentfs 0.3.0 → 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5386e4d1a1ef23527ec457ecb544d4d36d02e44811fe81f87db93f4188d82145
4
- data.tar.gz: f44614daf6d97aabd4d83a0e2339dd8d20e19d742b8b8403c37fc2967dd40925
3
+ metadata.gz: 77679e7cfe4c3077753ec0b425edec31318c17272070713a4831f80b6878de3e
4
+ data.tar.gz: b07a87ccbfa7e5adcfbd193076af48ed7109b0240224c68e640c2189058c41c0
5
5
  SHA512:
6
- metadata.gz: 39ee593b9c158d2093d5a8bc59a8d8510adb8a206d4b786a0465347e79f12323806765be999be8ad7fd14bccb4e32134696c1f851fa466a1a515d3b44d7ecc63
7
- data.tar.gz: f1ab2b7a9cc994b5d91cef392fa8d4a4c1bedaac69428ceb9552143b7f969391ec32a16ae1240c66da616a80440945bfae9f8a30e010bcd47f2f36f7aa89e3ef
6
+ metadata.gz: 97667d012f6b9b94dfdf74281ce53bf47966b7bf2da4e375c6d433ef7917f6f906ea5e7497424aaac2bb95fd65ab8149c8d429749a14cc5389b8fd1e2b0d7deb
7
+ data.tar.gz: 65baae29aa627cb17ec18e657955dca9caad84eacff8a3ff2ce0c85696eeeb235820f3975a76dc66610c706605c2a892c64420ad775a977f1b0eef2a8c238fe6
data/CHANGELOG.md CHANGED
@@ -1,6 +1,36 @@
1
- ## v0.3.0
1
+ ## [v0.6.1](https://github.com/metabahn/contentfs/releases/tag/v0.6.1)
2
2
 
3
- *unreleased*
3
+ *released on 2021-04-02*
4
+
5
+ * `fix` [#13](https://github.com/metabahn/contentfs/pull/13) Resolve includes after rendering ([bryanp](https://github.com/bryanp))
6
+
7
+ ## [v0.6.0](https://github.com/metabahn/contentfs/releases/tag/v0.6.0)
8
+
9
+ *released on 2021-04-02*
10
+
11
+ * `add` [#12](https://github.com/metabahn/contentfs/pull/12) Provide a pattern for preprocessing content ([bryanp](https://github.com/bryanp))
12
+
13
+ ## [v0.5.1](https://github.com/metabahn/contentfs/releases/tag/v0.5.1)
14
+
15
+ *released on 2021-04-02*
16
+
17
+ * `fix` [#11](https://github.com/metabahn/contentfs/pull/11) Handle code blocks with no available lexer ([bryanp](https://github.com/bryanp))
18
+
19
+ ## [v0.5.0](https://github.com/metabahn/contentfs/releases/tag/v0.5.0)
20
+
21
+ *released on 2021-04-01*
22
+
23
+ * `chg` [#10](https://github.com/metabahn/contentfs/pull/10) Replace redcarpet with cmark ([bryanp](https://github.com/bryanp))
24
+
25
+ ## [v0.4.0](https://github.com/metabahn/contentfs/releases/tag/v0.4.0)
26
+
27
+ *released on 2021-04-01*
28
+
29
+ * `add` [#9](https://github.com/metabahn/contentfs/pull/9) Includes ([bryanp](https://github.com/bryanp))
30
+
31
+ ## [v0.3.0](https://github.com/metabahn/contentfs/releases/tag/v0.3.0)
32
+
33
+ *released on 2020-11-18*
4
34
 
5
35
  * `chg` [#8](https://github.com/metabahn/contentfs/pull/8) Load database content from _content to avoid collisions ([bryanp](https://github.com/bryanp))
6
36
 
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  This software is licensed under the MIT License.
2
2
 
3
- Copyright 2020 Metabahn.
3
+ Copyright 2020-2021 Metabahn.
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a
6
6
  copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -40,7 +40,7 @@ The `content` name is special in that it defines content for the containing fold
40
40
 
41
41
  ### Formats
42
42
 
43
- Markdown is supported by default. Simply add the `redcarpet` gem to your project's `Gemfile`. For automatic syntax highlighting, add the `rouge` to your `Gemfile` as well.
43
+ Markdown is supported by default. Simply add the `commonmarker` gem to your project's `Gemfile`. For automatic syntax highlighting, add the `rouge` to your `Gemfile` as well.
44
44
 
45
45
  Unknown formats default to plain text.
46
46
 
@@ -12,26 +12,29 @@ module ContentFS
12
12
  #
13
13
  class Content
14
14
  class << self
15
- def load(path, metadata: {}, namespace: [])
16
- new(path: path, metadata: metadata, namespace: namespace)
15
+ def load(path, database:, metadata: {}, namespace: [], &block)
16
+ new(path: path, database: database, metadata: metadata, namespace: namespace, &block)
17
17
  end
18
18
  end
19
19
 
20
20
  FRONT_MATTER_REGEXP = /\A---\s*\n(.*?\n?)^---\s*$\n?/m
21
+ INCLUDE_REGEXP = /<!-- @include\s*([a-zA-Z0-9\-_\/.]*) -->/
21
22
 
22
23
  attr_reader :format, :prefix, :slug, :metadata, :namespace
23
24
 
24
- def initialize(path:, metadata: {}, namespace: [])
25
+ def initialize(path:, database:, metadata: {}, namespace: [], &block)
25
26
  path = Pathname.new(path)
26
27
  extname = path.extname
27
28
  name = path.basename(extname)
28
29
  prefix, remainder = Prefix.build(name)
29
30
  @prefix = prefix
30
- @format = extname.to_s[1..-1]&.to_sym
31
+ @format = extname.to_s[1..]&.to_sym
31
32
  @slug = Slug.build(remainder)
32
33
  @namespace = namespace.dup << @slug
34
+ @database = database
33
35
 
34
36
  content = path.read
37
+ content = block.call(content) if block
35
38
  @metadata = metadata.merge(parse_metadata(content))
36
39
  @content = content.gsub(FRONT_MATTER_REGEXP, "")
37
40
  end
@@ -42,12 +45,24 @@ module ContentFS
42
45
 
43
46
  def render
44
47
  if @format && (renderer = Renderers.resolve(@format))
45
- renderer.render(@content)
48
+ resolve_includes(renderer.render(@content))
46
49
  else
47
- to_s
50
+ resolve_includes(to_s)
48
51
  end
49
52
  end
50
53
 
54
+ private def resolve_includes(content)
55
+ working_content = content.dup
56
+
57
+ content.scan(INCLUDE_REGEXP) do |match|
58
+ if (include = @database.find_include(match[0]))
59
+ working_content.gsub!($~.to_s, include.render)
60
+ end
61
+ end
62
+
63
+ working_content
64
+ end
65
+
51
66
  private def parse_metadata(content)
52
67
  if (match = content.match(FRONT_MATTER_REGEXP))
53
68
  YAML.safe_load(match.captures[0]).to_h
@@ -11,8 +11,8 @@ module ContentFS
11
11
  #
12
12
  class Database
13
13
  class << self
14
- def load(path, namespace: [], root: true)
15
- new(path: path, namespace: namespace, root: root)
14
+ def load(path, parent: nil, namespace: [], root: true, &block)
15
+ new(path: path, parent: parent, namespace: namespace, root: root, &block)
16
16
  end
17
17
  end
18
18
 
@@ -20,12 +20,13 @@ module ContentFS
20
20
 
21
21
  attr_reader :prefix, :slug, :namespace, :metadata
22
22
 
23
- def initialize(path:, namespace: [], root: false)
23
+ def initialize(path:, parent: nil, namespace: [], root: false, &block)
24
24
  path = Pathname.new(path)
25
25
  name = path.basename(path.extname)
26
26
  prefix, remainder = Prefix.build(name)
27
27
  @prefix = prefix
28
28
  @namespace = namespace.dup
29
+ @parent = parent
29
30
 
30
31
  unless root
31
32
  @slug = Slug.build(remainder)
@@ -43,18 +44,23 @@ module ContentFS
43
44
  content_path = path.join.glob("_content.*")[0]
44
45
 
45
46
  @content = if content_path&.exist?
46
- Content.load(content_path, metadata: @metadata, namespace: @namespace)
47
+ Content.load(content_path, database: self, metadata: @metadata, namespace: @namespace, &block)
47
48
  end
48
49
 
49
- children, nested = {}, {}
50
+ children, nested, includes = {}, {}, {}
50
51
  Pathname.new(path).glob("*") do |path|
51
- next if path.basename.to_s.start_with?("_")
52
+ underscored = path.basename.to_s.start_with?("_")
53
+ next if underscored && path.directory?
52
54
 
53
55
  if path.directory?
54
- database = Database.load(path, namespace: @namespace, root: false)
56
+ database = Database.load(path, parent: self, namespace: @namespace, root: false, &block)
55
57
  nested[database.slug] = database
58
+ elsif underscored
59
+ content = Content.load(path, database: self, metadata: @metadata, namespace: @namespace, &block)
60
+
61
+ includes[content.slug.to_s[1..].to_sym] = content
56
62
  else
57
- content = Content.load(path, metadata: @metadata, namespace: @namespace)
63
+ content = Content.load(path, database: self, metadata: @metadata, namespace: @namespace, &block)
58
64
 
59
65
  children[content.slug] = content
60
66
  end
@@ -71,6 +77,12 @@ module ContentFS
71
77
  (database.prefix || database.slug).to_s
72
78
  }
73
79
  ]
80
+
81
+ @includes = Hash[
82
+ includes.sort_by { |key, content|
83
+ (content.prefix || content.slug).to_s
84
+ }
85
+ ]
74
86
  end
75
87
 
76
88
  def content
@@ -113,6 +125,35 @@ module ContentFS
113
125
  end
114
126
  end
115
127
 
128
+ def find_include(path)
129
+ @includes[path.to_sym] || find_child_include(path) || find_parent_include(path) || find_include_from_toplevel(path)
130
+ end
131
+
132
+ def toplevel
133
+ @parent ? @parent.toplevel : self
134
+ end
135
+
136
+ private def find_child_include(path)
137
+ return unless path.include?("/")
138
+
139
+ path_parts = path.split("/", 2)
140
+ @nested[path_parts[0].to_sym]&.find_include(path_parts[1])
141
+ end
142
+
143
+ private def find_parent_include(path)
144
+ return if @parent.nil?
145
+ return unless path.start_with?("../")
146
+
147
+ path_parts = path.split("../", 2)
148
+ @parent.find_include(path_parts[1])
149
+ end
150
+
151
+ private def find_include_from_toplevel(path)
152
+ return if @parent.nil?
153
+
154
+ toplevel.find_include(path)
155
+ end
156
+
116
157
  def to_s
117
158
  @content&.to_s.to_s
118
159
  end
@@ -1,39 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "redcarpet"
3
+ require "commonmarker"
4
4
 
5
5
  module ContentFS
6
6
  module Renderers
7
7
  # @api private
8
8
  class Markdown
9
9
  class << self
10
- OPTIONS = {
11
- autolink: true,
12
- footnotes: true,
13
- fenced_code_blocks: true,
14
- tables: true
15
- }.freeze
16
-
17
10
  def render(content)
18
- renderer.render(content)
19
- end
20
-
21
- def options
22
- OPTIONS
23
- end
24
-
25
- private def renderer
26
- @_renderer ||= Redcarpet::Markdown.new(Renderer, options)
27
- end
28
- end
29
-
30
- class Renderer < Redcarpet::Render::HTML
31
- def block_quote(quote)
32
- if (match = quote.match(/<p>\[(.*)\]/))
33
- %(<blockquote class="#{match[1]}">#{quote.gsub("[#{match[1]}]", "")}</blockquote>)
34
- else
35
- super
36
- end
11
+ CommonMarker.render_html(content, [:DEFAULT, :UNSAFE])
37
12
  end
38
13
  end
39
14
  end
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rouge"
4
- require "rouge/plugins/redcarpet"
5
4
 
6
5
  require_relative "../markdown"
7
6
 
@@ -12,16 +11,31 @@ module ContentFS
12
11
  class Code
13
12
  class << self
14
13
  def render(content)
15
- renderer.render(content)
14
+ renderer.render(CommonMarker.render_doc(content))
16
15
  end
17
16
 
18
17
  private def renderer
19
- @_renderer ||= Redcarpet::Markdown.new(Renderer, Markdown.options)
18
+ SyntaxRenderer.new(options: [:DEFAULT, :UNSAFE])
20
19
  end
21
20
  end
22
21
 
23
- class Renderer < Markdown::Renderer
24
- include Rouge::Plugins::Redcarpet
22
+ class SyntaxRenderer < CommonMarker::HtmlRenderer
23
+ def code_block(node)
24
+ block do
25
+ language = node.fence_info.split(/\s+/)[0]
26
+ out("<div class=\"highlight\"><pre class=\"highlight #{language}\"><code>")
27
+ out(syntax_highlight(node.string_content, language))
28
+ out('</code></pre></div>')
29
+ end
30
+ end
31
+
32
+ private def syntax_highlight(source, language)
33
+ if (lexer = Rouge::Lexer.find(language))
34
+ Rouge::Formatters::HTML.new.format(lexer.lex(source))
35
+ else
36
+ source
37
+ end
38
+ end
25
39
  end
26
40
  end
27
41
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ContentFS
4
- VERSION = "0.3.0"
4
+ VERSION = "0.6.1"
5
5
 
6
6
  def self.version
7
7
  VERSION
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contentfs
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bryan Powell
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-11-18 00:00:00.000000000 Z
11
+ date: 2021-04-02 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A structured content file system.
14
14
  email: bryan@metabahn.com
@@ -47,7 +47,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
47
47
  - !ruby/object:Gem::Version
48
48
  version: '0'
49
49
  requirements: []
50
- rubygems_version: 3.1.2
50
+ rubygems_version: 3.2.4
51
51
  signing_key:
52
52
  specification_version: 4
53
53
  summary: A structured content file system.