contentfs 0.3.0 → 0.6.1

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: 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.