gloo-md 1.1 → 1.3

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/lib/gloo-md.rb +6 -4
  3. data/lib/md_doc.rb +200 -0
  4. metadata +3 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d4263fc76f70591ae470fa3bf72800df6bc2055fe6ea73610940d4bc398548b6
4
- data.tar.gz: f645e639d0759dab026b38d8b266a4e67004c977039402b6c4efac5a0ee12008
3
+ metadata.gz: 2bfa6b6b1b63e61071e271da7649cb0d13d0dd7a6ab8f0bcfaeb455e6325efcc
4
+ data.tar.gz: 1fcd9683e59f38daf40984bc0d711fa44eac8ca3d9f39a7e6ce21d2b98568de6
5
5
  SHA512:
6
- metadata.gz: c885359772e7b95bcf53baa9d2e62db12f9a03a86dce068a03c8c1eb9ea8aa9f10ea07252a929ebef0ad373b364f9b43fd6a6427aff134e6586468d9f2f7972c
7
- data.tar.gz: 5bce0c04c4cc2f76e54fa69b6aa2b86b1f5506953eb04de23274c1102e5afc7c40d74e81340443da7a706d59f603826ed1e5964567c61ef50e4f120d29abb672
6
+ metadata.gz: 0ef1a0fc0d81d9fc7c145a5385b70dd848fc1de52c74c2c833175c998b04f8eded160ef9241ee579c1a723229f26b8036af4dd2dde7048871358c8b58730513e
7
+ data.tar.gz: a3572eb4470c11e401fef0628b1e506f24b628a07cc8115819abc4f59db7dc59d059591a5f4ef6422c72f4dd7f2fa5e43a4c7dba05d813e218fba1813920040b
data/lib/gloo-md.rb CHANGED
@@ -4,18 +4,20 @@
4
4
  # This file is loaded when someone does `require 'gloo-md'`
5
5
  #
6
6
  require 'md'
7
+ require 'md_doc'
7
8
  require 'markdown_ext'
8
9
 
9
- #
10
+ #
10
11
  # Registers the extension.
11
- #
12
+ #
12
13
  class MdInit < Gloo::Plugin::Base
13
14
 
14
- #
15
+ #
15
16
  # Register verbs and objects.
16
- #
17
+ #
17
18
  def register( callback )
18
19
  callback.register_obj( Md )
20
+ callback.register_obj( MdDoc )
19
21
  end
20
22
 
21
23
  end
data/lib/md_doc.rb ADDED
@@ -0,0 +1,200 @@
1
+ # Author:: Eric Crane (mailto:eric.crane@mac.com)
2
+ # Copyright:: Copyright (c) 2026 Eric Crane. All rights reserved.
3
+ #
4
+ # A Markdown document with YAML frontmatter.
5
+ # Holds a file path and exposes the frontmatter fields as container
6
+ # children and the Markdown body as a text child.
7
+ #
8
+ require 'yaml'
9
+
10
+ class MdDoc < Gloo::Core::Obj
11
+
12
+ KEYWORD = 'md_doc'.freeze
13
+ KEYWORD_SHORT = 'md_doc'.freeze
14
+
15
+ PATH = 'path'.freeze
16
+ FRONTMATTER = 'frontmatter'.freeze
17
+ BODY = 'body'.freeze
18
+
19
+
20
+ # ---------------------------------------------------------------------
21
+ # Type identity
22
+ # ---------------------------------------------------------------------
23
+
24
+ #
25
+ # The name of the object type.
26
+ #
27
+ def self.typename
28
+ return KEYWORD
29
+ end
30
+
31
+ #
32
+ # The short name of the object type.
33
+ #
34
+ def self.short_typename
35
+ return KEYWORD_SHORT
36
+ end
37
+
38
+ #
39
+ # Set the value with any necessary type conversions.
40
+ #
41
+ def set_value( new_value )
42
+ self.value = new_value.to_s
43
+ end
44
+
45
+
46
+ # ---------------------------------------------------------------------
47
+ # Children
48
+ # ---------------------------------------------------------------------
49
+
50
+ #
51
+ # Does this object have children to add when an object
52
+ # is created in interactive mode?
53
+ #
54
+ def add_children_on_create?
55
+ return true
56
+ end
57
+
58
+ #
59
+ # Add the default children: path, frontmatter, body.
60
+ #
61
+ def add_default_children
62
+ fac = @engine.factory
63
+ fac.create_file PATH, nil, self
64
+ fac.create_can FRONTMATTER, self
65
+ fac.create_text BODY, nil, self
66
+ end
67
+
68
+
69
+ # ---------------------------------------------------------------------
70
+ # Messages
71
+ # ---------------------------------------------------------------------
72
+
73
+ #
74
+ # Get a list of message names that this object receives.
75
+ #
76
+ def self.messages
77
+ return super + %w[read write]
78
+ end
79
+
80
+ #
81
+ # Read the file at path, parse frontmatter and body, populate children.
82
+ # Only scalar frontmatter values become gloo string children; complex values
83
+ # (arrays, nested hashes) are skipped — they are preserved on write by
84
+ # re-reading the file.
85
+ #
86
+ def msg_read
87
+ path = resolve_path
88
+ return unless path
89
+
90
+ unless File.exist?( path )
91
+ @engine.log.error "md_doc file not found: #{path}"
92
+ return
93
+ end
94
+
95
+ content = File.read( path )
96
+ fm_hash, body_text = parse_frontmatter( content )
97
+
98
+ fm_can = find_child FRONTMATTER
99
+ if fm_can && fm_hash
100
+ fm_hash.each do |key, val|
101
+ next unless scalar?( val )
102
+ child = fm_can.find_add_child( key.to_s, 'string' )
103
+ child.set_value val.to_s
104
+ end
105
+ end
106
+
107
+ body = find_child BODY
108
+ body.set_value( body_text ) if body
109
+ end
110
+
111
+ #
112
+ # Serialize frontmatter and body children back to the file at path.
113
+ # Re-reads the current file to get the base hash (preserving arrays and other
114
+ # complex values), then overlays the scalar children which may have been
115
+ # modified. Creates the file if it does not exist.
116
+ #
117
+ def msg_write
118
+ path = resolve_path
119
+ return unless path
120
+
121
+ expanded = File.expand_path( path )
122
+
123
+ # Re-read the file so arrays and nested hashes survive unchanged.
124
+ base = {}
125
+ if File.exist?( expanded )
126
+ base, _ = parse_frontmatter( File.read( expanded ) )
127
+ end
128
+
129
+ fm_can = find_child FRONTMATTER
130
+
131
+ # Overlay scalar children; updating an existing key preserves its position.
132
+ if fm_can
133
+ fm_can.children.each do |child|
134
+ base[ child.name ] = child.value
135
+ end
136
+ end
137
+
138
+ body = find_child BODY
139
+ body_text = body ? body.value.to_s : ''
140
+
141
+ File.write( expanded, build_content( base, body_text ) )
142
+ end
143
+
144
+
145
+ # ---------------------------------------------------------------------
146
+ # Private helpers
147
+ # ---------------------------------------------------------------------
148
+
149
+ private
150
+
151
+ #
152
+ # True for scalar YAML values that can be stored as gloo string children.
153
+ # Arrays, hashes, and other complex types are skipped on read.
154
+ #
155
+ def scalar?( val )
156
+ val.is_a?( String ) || val.is_a?( Integer ) || val.is_a?( Float ) ||
157
+ val.is_a?( TrueClass ) || val.is_a?( FalseClass ) || val.nil?
158
+ end
159
+
160
+ #
161
+ # Get the expanded path string from the path child.
162
+ #
163
+ def resolve_path
164
+ o = find_child PATH
165
+ return nil unless o
166
+ return nil if o.value.to_s.strip.empty?
167
+
168
+ File.expand_path( o.value.to_s )
169
+ end
170
+
171
+ #
172
+ # Parse YAML frontmatter from file content.
173
+ # Returns [fm_hash, body_text]. If there is no frontmatter block,
174
+ # fm_hash is empty and body_text is the full content.
175
+ #
176
+ def parse_frontmatter( content )
177
+ if content =~ /\A---\s*\n(.*?\n)---\s*\n?(.*)\z/m
178
+ fm_hash = YAML.safe_load( $1 ) || {}
179
+ body_text = $2
180
+ return fm_hash, body_text
181
+ end
182
+
183
+ return {}, content
184
+ end
185
+
186
+ #
187
+ # Build the file content string from a frontmatter hash and body text.
188
+ # Emits frontmatter as YAML between --- delimiters.
189
+ #
190
+ def build_content( fm_hash, body_text )
191
+ if fm_hash.empty?
192
+ return body_text
193
+ end
194
+
195
+ # to_yaml emits "---\nkey: val\n"; strip the leading "---\n" and rewrap.
196
+ fm_yaml = fm_hash.to_yaml.sub( /\A---\n/, '' )
197
+ "---\n#{fm_yaml}---\n#{body_text}"
198
+ end
199
+
200
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gloo-md
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.1'
4
+ version: '1.3'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Crane
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-11 00:00:00.000000000 Z
11
+ date: 2026-06-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: redcarpet
@@ -34,6 +34,7 @@ files:
34
34
  - lib/gloo-md.rb
35
35
  - lib/markdown_ext.rb
36
36
  - lib/md.rb
37
+ - lib/md_doc.rb
37
38
  homepage: https://gloo.ecrane.us/
38
39
  licenses:
39
40
  - MIT