contentfs 0.1.1 → 0.5.0

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: 82a9b603b9239b87457c9783f5908964a92e42d2962ad28cbcec82f67520f9a2
4
- data.tar.gz: 97852ac7a01d5d050427a4b0e0e3a0687d794365dccee532bd3737a66f823517
3
+ metadata.gz: 82208eb59ae04f8483de860c59b496e0bfe47488c6693ee80b2517bb69c37619
4
+ data.tar.gz: '0685aaaf67a70ee5f713015ffc1312b8554598e7153d8427d957b8785007b312'
5
5
  SHA512:
6
- metadata.gz: 4622e5fe7621c3a5b13d9a54c2f7af22d5129a04e182a2da57cbd73e678a1aa1613c7abc45559182ed74b4d8afd497b8200395bbc28f0eeb5907856f77d415a3
7
- data.tar.gz: 999012506f5131e5448b4d8e4c665bd93a7d9dfb2b09043095b5c7b8e2cc98d4c6c7bc87d4c34559151f87bddd8600d357a09512ca1ca546feaf8627f5fec0c2
6
+ metadata.gz: 89f9d947f2bff284a260b10da2207d0305f85ee8df746078dd8aa57c23c70314580b04472235818e9fb9acd24e745d0f0995e03a47a3e1dd12282eb2be3765fd
7
+ data.tar.gz: 890b635054dbe37744334035bf495205bf97a4aebe4acdea70ac0e4c9572d5103ff5b98994cfae633f5d62ae5c52351293aea311707edcdc1b3b568af9e74026
data/CHANGELOG.md CHANGED
@@ -1,12 +1,42 @@
1
- ## v0.1.1
1
+ ## [v0.5.0](https://github.com/metabahn/contentfs/releases/tag/v0.5.0)
2
+
3
+ *released on 2021-04-01*
4
+
5
+ * `chg` [#10](https://github.com/metabahn/contentfs/pull/10) Replace redcarpet with cmark ([bryanp](https://github.com/bryanp))
6
+
7
+ ## [v0.4.0](https://github.com/metabahn/contentfs/releases/tag/v0.4.0)
8
+
9
+ *released on 2021-04-01*
10
+
11
+ * `add` [#9](https://github.com/metabahn/contentfs/pull/9) Includes ([bryanp](https://github.com/bryanp))
12
+
13
+ ## [v0.3.0](https://github.com/metabahn/contentfs/releases/tag/v0.3.0)
14
+
15
+ *released on 2020-11-18*
16
+
17
+ * `chg` [#8](https://github.com/metabahn/contentfs/pull/8) Load database content from _content to avoid collisions ([bryanp](https://github.com/bryanp))
18
+
19
+ ## v0.2.1
2
20
 
3
21
  *unreleased*
4
22
 
23
+ * `fix` [#7](https://github.com/metabahn/contentfs/pull/7) Order content and databases by prefix or slug ([bryanp](https://github.com/bryanp))
24
+
25
+ ## [v0.2.0](https://github.com/metabahn/contentfs/releases/tag/v0.2.0)
26
+
27
+ *released on 2020-11-14*
28
+
29
+ * `add` [#6](https://github.com/metabahn/contentfs/pull/6) Expose database metadata ([bryanp](https://github.com/bryanp))
30
+
31
+ ## [v0.1.1](https://github.com/metabahn/contentfs/releases/tag/v0.1.1)
32
+
33
+ *released on 2020-11-13*
34
+
5
35
  * `fix` [#5](https://github.com/metabahn/contentfs/pull/5) Remove front-matter from content ([bryanp](https://github.com/bryanp))
6
36
 
7
- ## v0.1.0
37
+ ## [v0.1.0](https://github.com/metabahn/contentfs/releases/tag/v0.1.0)
8
38
 
9
- *unreleased*
39
+ *released on 2020-11-12*
10
40
 
11
41
  * `add` [#4](https://github.com/metabahn/contentfs/pull/4) Expose namespaces for databases and content ([bryanp](https://github.com/bryanp))
12
42
  * `add` [#3](https://github.com/metabahn/contentfs/pull/3) Introduce `Database#nested` for iterating over databases ([bryanp](https://github.com/bryanp))
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
@@ -12,24 +12,26 @@ 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: [])
16
+ new(path: path, database: database, metadata: metadata, namespace: namespace)
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: [])
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
35
37
  @metadata = metadata.merge(parse_metadata(content))
@@ -41,8 +43,16 @@ module ContentFS
41
43
  end
42
44
 
43
45
  def render
46
+ working_content = @content.dup
47
+
48
+ @content.scan(INCLUDE_REGEXP) do |match|
49
+ if (include = @database.find_include(match[0]))
50
+ working_content.gsub!($~.to_s, include.render)
51
+ end
52
+ end
53
+
44
54
  if @format && (renderer = Renderers.resolve(@format))
45
- renderer.render(@content)
55
+ renderer.render(working_content)
46
56
  else
47
57
  to_s
48
58
  end
@@ -11,23 +11,22 @@ 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)
15
+ new(path: path, parent: parent, namespace: namespace, root: root)
16
16
  end
17
17
  end
18
18
 
19
19
  METADATA_FILE = "_metadata.yml"
20
20
 
21
- attr_reader :prefix, :slug, :namespace
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)
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
- @children = {}
29
- @nested = {}
30
28
  @namespace = namespace.dup
29
+ @parent = parent
31
30
 
32
31
  unless root
33
32
  @slug = Slug.build(remainder)
@@ -36,28 +35,54 @@ module ContentFS
36
35
 
37
36
  metadata_path = path.join(METADATA_FILE)
38
37
 
39
- metadata = if metadata_path.exist?
38
+ @metadata = if metadata_path.exist?
40
39
  YAML.safe_load(metadata_path.read).to_h
41
40
  else
42
41
  {}
43
42
  end
44
43
 
44
+ content_path = path.join.glob("_content.*")[0]
45
+
46
+ @content = if content_path&.exist?
47
+ Content.load(content_path, database: self, metadata: @metadata, namespace: @namespace)
48
+ end
49
+
50
+ children, nested, includes = {}, {}, {}
45
51
  Pathname.new(path).glob("*") do |path|
46
- next if path.basename.to_s.start_with?("_")
52
+ underscored = path.basename.to_s.start_with?("_")
53
+ next if underscored && path.directory?
47
54
 
48
55
  if path.directory?
49
- database = Database.load(path, namespace: @namespace, root: false)
50
- @nested[database.slug] = database
56
+ database = Database.load(path, parent: self, namespace: @namespace, root: false)
57
+ nested[database.slug] = database
58
+ elsif underscored
59
+ content = Content.load(path, database: self, metadata: @metadata, namespace: @namespace)
60
+
61
+ includes[content.slug.to_s[1..].to_sym] = content
51
62
  else
52
- content = Content.load(path, metadata: metadata, namespace: @namespace)
63
+ content = Content.load(path, database: self, metadata: @metadata, namespace: @namespace)
53
64
 
54
- if content.slug == :content
55
- @content = content
56
- else
57
- @children[content.slug] = content
58
- end
65
+ children[content.slug] = content
59
66
  end
60
67
  end
68
+
69
+ @children = Hash[
70
+ children.sort_by { |key, content|
71
+ (content.prefix || content.slug).to_s
72
+ }
73
+ ]
74
+
75
+ @nested = Hash[
76
+ nested.sort_by { |key, database|
77
+ (database.prefix || database.slug).to_s
78
+ }
79
+ ]
80
+
81
+ @includes = Hash[
82
+ includes.sort_by { |key, content|
83
+ (content.prefix || content.slug).to_s
84
+ }
85
+ ]
61
86
  end
62
87
 
63
88
  def content
@@ -100,6 +125,35 @@ module ContentFS
100
125
  end
101
126
  end
102
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
+
103
157
  def to_s
104
158
  @content&.to_s.to_s
105
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,27 @@ 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
+ Rouge::Formatters::HTML.new.format(Rouge::Lexer.find(language).lex(source))
34
+ end
25
35
  end
26
36
  end
27
37
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ContentFS
4
- VERSION = "0.1.1"
4
+ VERSION = "0.5.0"
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.1.1
4
+ version: 0.5.0
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-13 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.