ace-support-markdown 0.3.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 +7 -0
- data/CHANGELOG.md +121 -0
- data/CONTRIBUTING.md +221 -0
- data/LICENSE +21 -0
- data/README.md +29 -0
- data/Rakefile +13 -0
- data/lib/ace/support/markdown/atoms/document_validator.rb +129 -0
- data/lib/ace/support/markdown/atoms/frontmatter_extractor.rb +117 -0
- data/lib/ace/support/markdown/atoms/frontmatter_serializer.rb +88 -0
- data/lib/ace/support/markdown/atoms/section_extractor.rb +174 -0
- data/lib/ace/support/markdown/models/markdown_document.rb +187 -0
- data/lib/ace/support/markdown/models/section.rb +114 -0
- data/lib/ace/support/markdown/molecules/document_builder.rb +151 -0
- data/lib/ace/support/markdown/molecules/frontmatter_editor.rb +108 -0
- data/lib/ace/support/markdown/molecules/kramdown_processor.rb +124 -0
- data/lib/ace/support/markdown/molecules/section_editor.rb +158 -0
- data/lib/ace/support/markdown/organisms/document_editor.rb +190 -0
- data/lib/ace/support/markdown/organisms/safe_file_writer.rb +203 -0
- data/lib/ace/support/markdown/version.rb +9 -0
- data/lib/ace/support/markdown.rb +34 -0
- metadata +148 -0
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Ace
|
|
4
|
+
module Support
|
|
5
|
+
module Markdown
|
|
6
|
+
module Organisms
|
|
7
|
+
# Main API for document editing with fluent interface
|
|
8
|
+
# Provides safe editing with state management and validation
|
|
9
|
+
class DocumentEditor
|
|
10
|
+
attr_reader :document, :file_path, :original_content
|
|
11
|
+
|
|
12
|
+
# Create a new DocumentEditor
|
|
13
|
+
# @param file_path [String] Path to the markdown file
|
|
14
|
+
def initialize(file_path)
|
|
15
|
+
raise ArgumentError, "File path cannot be nil" if file_path.nil?
|
|
16
|
+
raise FileOperationError, "File not found: #{file_path}" unless File.exist?(file_path)
|
|
17
|
+
|
|
18
|
+
@file_path = file_path
|
|
19
|
+
@original_content = File.read(file_path)
|
|
20
|
+
@document = Models::MarkdownDocument.parse(@original_content, file_path: file_path)
|
|
21
|
+
@backup_path = nil
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Update frontmatter fields
|
|
25
|
+
# @param updates [Hash] Fields to update
|
|
26
|
+
# @return [DocumentEditor] self for chaining
|
|
27
|
+
def update_frontmatter(updates)
|
|
28
|
+
@document = Molecules::FrontmatterEditor.update(@document, updates)
|
|
29
|
+
self
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# Update a single frontmatter field
|
|
33
|
+
# @param key [String] The field key
|
|
34
|
+
# @param value [Object] The field value
|
|
35
|
+
# @return [DocumentEditor] self for chaining
|
|
36
|
+
def set_field(key, value)
|
|
37
|
+
@document = Molecules::FrontmatterEditor.update_field(@document, key, value)
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Replace a section's content
|
|
42
|
+
# @param heading [String] The section heading
|
|
43
|
+
# @param new_content [String] The new content
|
|
44
|
+
# @return [DocumentEditor] self for chaining
|
|
45
|
+
def replace_section(heading, new_content)
|
|
46
|
+
@document = Molecules::SectionEditor.replace_section(@document, heading, new_content)
|
|
47
|
+
self
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Append to a section
|
|
51
|
+
# @param heading [String] The section heading
|
|
52
|
+
# @param content [String] Content to append
|
|
53
|
+
# @return [DocumentEditor] self for chaining
|
|
54
|
+
def append_to_section(heading, content)
|
|
55
|
+
@document = Molecules::SectionEditor.append_to_section(@document, heading, content)
|
|
56
|
+
self
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Delete a section
|
|
60
|
+
# @param heading [String] The section heading
|
|
61
|
+
# @return [DocumentEditor] self for chaining
|
|
62
|
+
def delete_section(heading)
|
|
63
|
+
@document = Molecules::SectionEditor.delete_section(@document, heading)
|
|
64
|
+
self
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Add a new section
|
|
68
|
+
# @param heading [String] The section heading
|
|
69
|
+
# @param content [String] The section content
|
|
70
|
+
# @param level [Integer] The heading level (default: 2)
|
|
71
|
+
# @return [DocumentEditor] self for chaining
|
|
72
|
+
def add_section(heading, content, level: 2)
|
|
73
|
+
section = Models::Section.new(heading: heading, content: content, level: level)
|
|
74
|
+
@document = Molecules::SectionEditor.add_section(@document, section)
|
|
75
|
+
self
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Validate the current document state
|
|
79
|
+
# @param rules [Hash] Optional validation rules
|
|
80
|
+
# @return [Hash] Result with :valid, :errors, :warnings
|
|
81
|
+
def validate(rules: {})
|
|
82
|
+
content = @document.to_markdown
|
|
83
|
+
Atoms::DocumentValidator.validate(content, rules: rules)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Check if document is valid
|
|
87
|
+
# @param rules [Hash] Optional validation rules
|
|
88
|
+
# @return [Boolean]
|
|
89
|
+
def valid?(rules: {})
|
|
90
|
+
validate(rules: rules)[:valid]
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Save the document to file
|
|
94
|
+
# @param backup [Boolean] Create backup before writing (default: true)
|
|
95
|
+
# @param validate_before [Boolean] Validate before writing (default: true)
|
|
96
|
+
# @param rules [Hash] Optional validation rules
|
|
97
|
+
# @return [Hash] Result with :success, :backup_path, :errors
|
|
98
|
+
def save!(backup: true, validate_before: true, rules: {})
|
|
99
|
+
# Validate before save
|
|
100
|
+
if validate_before
|
|
101
|
+
validation = validate(rules: rules)
|
|
102
|
+
unless validation[:valid]
|
|
103
|
+
return {
|
|
104
|
+
success: false,
|
|
105
|
+
backup_path: nil,
|
|
106
|
+
errors: validation[:errors]
|
|
107
|
+
}
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Generate new content
|
|
112
|
+
new_content = @document.to_markdown
|
|
113
|
+
|
|
114
|
+
# Use SafeFileWriter for atomic write with backup
|
|
115
|
+
result = SafeFileWriter.write(
|
|
116
|
+
@file_path,
|
|
117
|
+
new_content,
|
|
118
|
+
backup: backup
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if result[:success]
|
|
122
|
+
@backup_path = result[:backup_path]
|
|
123
|
+
@original_content = new_content
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
result
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Rollback to original content
|
|
130
|
+
# @return [Hash] Result with :success, :errors
|
|
131
|
+
def rollback
|
|
132
|
+
if @backup_path && File.exist?(@backup_path)
|
|
133
|
+
begin
|
|
134
|
+
File.write(@file_path, File.read(@backup_path))
|
|
135
|
+
File.delete(@backup_path)
|
|
136
|
+
@backup_path = nil
|
|
137
|
+
|
|
138
|
+
# Reload document
|
|
139
|
+
@document = Models::MarkdownDocument.parse(@original_content, file_path: @file_path)
|
|
140
|
+
|
|
141
|
+
{success: true, errors: []}
|
|
142
|
+
rescue => e
|
|
143
|
+
{success: false, errors: ["Rollback failed: #{e.message}"]}
|
|
144
|
+
end
|
|
145
|
+
else
|
|
146
|
+
{success: false, errors: ["No backup available for rollback"]}
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# Get current document content as string
|
|
151
|
+
# @return [String]
|
|
152
|
+
def to_markdown
|
|
153
|
+
@document.to_markdown
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Check if document has been modified
|
|
157
|
+
# @return [Boolean]
|
|
158
|
+
def modified?
|
|
159
|
+
@document.to_markdown != @original_content
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Get document statistics
|
|
163
|
+
# @return [Hash]
|
|
164
|
+
def stats
|
|
165
|
+
@document.stats
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Create a DocumentEditor from content string
|
|
169
|
+
# @param content [String] The markdown content
|
|
170
|
+
# @param file_path [String, nil] Optional file path for context
|
|
171
|
+
# @return [DocumentEditor]
|
|
172
|
+
def self.from_content(content, file_path: nil)
|
|
173
|
+
# Create a temporary file if needed
|
|
174
|
+
if file_path.nil?
|
|
175
|
+
require "tempfile"
|
|
176
|
+
temp = Tempfile.new(["markdown", ".md"])
|
|
177
|
+
temp.write(content)
|
|
178
|
+
temp.close
|
|
179
|
+
file_path = temp.path
|
|
180
|
+
else
|
|
181
|
+
File.write(file_path, content)
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
new(file_path)
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
end
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "fileutils"
|
|
4
|
+
require "tempfile"
|
|
5
|
+
|
|
6
|
+
module Ace
|
|
7
|
+
module Support
|
|
8
|
+
module Markdown
|
|
9
|
+
module Organisms
|
|
10
|
+
# Safe file writing with backup and atomic operations
|
|
11
|
+
# Prevents corruption through temp file + move pattern
|
|
12
|
+
class SafeFileWriter
|
|
13
|
+
# Write content to file safely with backup and rollback
|
|
14
|
+
# @param file_path [String] Target file path
|
|
15
|
+
# @param content [String] Content to write
|
|
16
|
+
# @param backup [Boolean] Create backup before writing (default: true)
|
|
17
|
+
# @param validate [Boolean] Validate content before writing (default: false)
|
|
18
|
+
# @param validator [Proc, nil] Optional validation proc
|
|
19
|
+
# @return [Hash] Result with :success, :backup_path, :errors
|
|
20
|
+
def self.write(file_path, content, backup: true, validate: false, validator: nil)
|
|
21
|
+
raise ArgumentError, "File path cannot be nil" if file_path.nil?
|
|
22
|
+
raise ArgumentError, "Content cannot be nil" if content.nil?
|
|
23
|
+
|
|
24
|
+
errors = []
|
|
25
|
+
|
|
26
|
+
# Validate content if requested
|
|
27
|
+
if validate || validator
|
|
28
|
+
validation_errors = perform_validation(content, validator)
|
|
29
|
+
unless validation_errors.empty?
|
|
30
|
+
return {
|
|
31
|
+
success: false,
|
|
32
|
+
backup_path: nil,
|
|
33
|
+
errors: validation_errors
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
backup_path = nil
|
|
39
|
+
|
|
40
|
+
begin
|
|
41
|
+
# Create backup if requested and file exists
|
|
42
|
+
if backup && File.exist?(file_path)
|
|
43
|
+
backup_path = create_backup(file_path)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Write atomically using temp file + move
|
|
47
|
+
write_atomic(file_path, content)
|
|
48
|
+
|
|
49
|
+
{
|
|
50
|
+
success: true,
|
|
51
|
+
backup_path: backup_path,
|
|
52
|
+
errors: []
|
|
53
|
+
}
|
|
54
|
+
rescue => e
|
|
55
|
+
# Rollback from backup if available
|
|
56
|
+
if backup_path && File.exist?(backup_path)
|
|
57
|
+
begin
|
|
58
|
+
FileUtils.cp(backup_path, file_path)
|
|
59
|
+
errors << "Write failed, restored from backup: #{e.message}"
|
|
60
|
+
rescue => rollback_error
|
|
61
|
+
errors << "Write failed and rollback failed: #{e.message} | #{rollback_error.message}"
|
|
62
|
+
end
|
|
63
|
+
else
|
|
64
|
+
errors << "Write failed: #{e.message}"
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
{
|
|
68
|
+
success: false,
|
|
69
|
+
backup_path: backup_path,
|
|
70
|
+
errors: errors
|
|
71
|
+
}
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Write content with automatic validation
|
|
76
|
+
# @param file_path [String] Target file path
|
|
77
|
+
# @param content [String] Content to write
|
|
78
|
+
# @param rules [Hash] Validation rules
|
|
79
|
+
# @return [Hash] Result with :success, :backup_path, :errors
|
|
80
|
+
def self.write_with_validation(file_path, content, rules: {})
|
|
81
|
+
validator = lambda do |c|
|
|
82
|
+
result = Atoms::DocumentValidator.validate(c, rules: rules)
|
|
83
|
+
result[:valid] ? [] : result[:errors]
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
write(file_path, content, backup: true, validate: true, validator: validator)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Create a backup of the file
|
|
90
|
+
# @param file_path [String] File to backup
|
|
91
|
+
# @return [String] Backup file path
|
|
92
|
+
def self.create_backup(file_path)
|
|
93
|
+
raise FileOperationError, "File not found: #{file_path}" unless File.exist?(file_path)
|
|
94
|
+
|
|
95
|
+
timestamp = Time.now.strftime("%Y%m%d_%H%M%S_%L")
|
|
96
|
+
backup_path = "#{file_path}.backup.#{timestamp}"
|
|
97
|
+
|
|
98
|
+
FileUtils.cp(file_path, backup_path)
|
|
99
|
+
backup_path
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Restore from backup
|
|
103
|
+
# @param file_path [String] Target file path
|
|
104
|
+
# @param backup_path [String] Backup file path
|
|
105
|
+
# @return [Hash] Result with :success, :errors
|
|
106
|
+
def self.restore_from_backup(file_path, backup_path)
|
|
107
|
+
unless File.exist?(backup_path)
|
|
108
|
+
return {
|
|
109
|
+
success: false,
|
|
110
|
+
errors: ["Backup file not found: #{backup_path}"]
|
|
111
|
+
}
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
begin
|
|
115
|
+
FileUtils.cp(backup_path, file_path)
|
|
116
|
+
{
|
|
117
|
+
success: true,
|
|
118
|
+
errors: []
|
|
119
|
+
}
|
|
120
|
+
rescue => e
|
|
121
|
+
{
|
|
122
|
+
success: false,
|
|
123
|
+
errors: ["Restore failed: #{e.message}"]
|
|
124
|
+
}
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# Cleanup old backup files
|
|
129
|
+
# @param file_path [String] Original file path
|
|
130
|
+
# @param keep [Integer] Number of recent backups to keep (default: 5)
|
|
131
|
+
# @return [Integer] Number of backups deleted
|
|
132
|
+
def self.cleanup_backups(file_path, keep: 5)
|
|
133
|
+
dir = File.dirname(file_path)
|
|
134
|
+
basename = File.basename(file_path)
|
|
135
|
+
|
|
136
|
+
# Find all backup files for this file
|
|
137
|
+
backup_pattern = File.join(dir, "#{basename}.backup.*")
|
|
138
|
+
backups = Dir.glob(backup_pattern).sort
|
|
139
|
+
|
|
140
|
+
# Keep only the most recent N backups
|
|
141
|
+
to_delete = backups[0...-keep] || []
|
|
142
|
+
|
|
143
|
+
deleted = 0
|
|
144
|
+
to_delete.each do |backup|
|
|
145
|
+
File.delete(backup)
|
|
146
|
+
deleted += 1
|
|
147
|
+
rescue
|
|
148
|
+
# Skip files that can't be deleted
|
|
149
|
+
next
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
deleted
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
private
|
|
156
|
+
|
|
157
|
+
# Write file atomically using temp file + move
|
|
158
|
+
def self.write_atomic(file_path, content)
|
|
159
|
+
# Get directory and ensure it exists
|
|
160
|
+
dir = File.dirname(file_path)
|
|
161
|
+
FileUtils.mkdir_p(dir) unless Dir.exist?(dir)
|
|
162
|
+
|
|
163
|
+
# Create temp file in same directory (same filesystem for atomic move)
|
|
164
|
+
temp = Tempfile.new([File.basename(file_path, ".*"), File.extname(file_path)], dir)
|
|
165
|
+
|
|
166
|
+
begin
|
|
167
|
+
# Write content to temp file
|
|
168
|
+
temp.write(content)
|
|
169
|
+
temp.close
|
|
170
|
+
|
|
171
|
+
# Atomic move (rename) - this is the critical operation
|
|
172
|
+
# On most filesystems, rename is atomic
|
|
173
|
+
FileUtils.mv(temp.path, file_path)
|
|
174
|
+
ensure
|
|
175
|
+
# Clean up temp file if it still exists
|
|
176
|
+
temp.close
|
|
177
|
+
temp.unlink if File.exist?(temp.path)
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Perform validation on content
|
|
182
|
+
def self.perform_validation(content, validator)
|
|
183
|
+
errors = []
|
|
184
|
+
|
|
185
|
+
# Basic validation - check content can be parsed
|
|
186
|
+
result = Atoms::FrontmatterExtractor.extract(content)
|
|
187
|
+
unless result[:valid]
|
|
188
|
+
errors.concat(result[:errors])
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
# Custom validator
|
|
192
|
+
if validator.is_a?(Proc)
|
|
193
|
+
custom_errors = validator.call(content)
|
|
194
|
+
errors.concat(custom_errors) if custom_errors.is_a?(Array)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
errors
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "markdown/version"
|
|
4
|
+
|
|
5
|
+
# Atoms
|
|
6
|
+
require_relative "markdown/atoms/frontmatter_extractor"
|
|
7
|
+
require_relative "markdown/atoms/frontmatter_serializer"
|
|
8
|
+
require_relative "markdown/atoms/section_extractor"
|
|
9
|
+
require_relative "markdown/atoms/document_validator"
|
|
10
|
+
|
|
11
|
+
# Molecules
|
|
12
|
+
require_relative "markdown/molecules/frontmatter_editor"
|
|
13
|
+
require_relative "markdown/molecules/section_editor"
|
|
14
|
+
require_relative "markdown/molecules/kramdown_processor"
|
|
15
|
+
require_relative "markdown/molecules/document_builder"
|
|
16
|
+
|
|
17
|
+
# Organisms
|
|
18
|
+
require_relative "markdown/organisms/document_editor"
|
|
19
|
+
require_relative "markdown/organisms/safe_file_writer"
|
|
20
|
+
|
|
21
|
+
# Models
|
|
22
|
+
require_relative "markdown/models/markdown_document"
|
|
23
|
+
require_relative "markdown/models/section"
|
|
24
|
+
|
|
25
|
+
module Ace
|
|
26
|
+
module Support
|
|
27
|
+
module Markdown
|
|
28
|
+
class Error < StandardError; end
|
|
29
|
+
class ValidationError < Error; end
|
|
30
|
+
class SectionNotFoundError < Error; end
|
|
31
|
+
class FileOperationError < Error; end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ace-support-markdown
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.3.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Michal Czyz
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: kramdown
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '2.4'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '2.4'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: kramdown-parser-gfm
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.1'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.1'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: bundler
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '2.0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '2.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: minitest
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '5.0'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '5.0'
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: rake
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - "~>"
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: '13.0'
|
|
75
|
+
type: :development
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - "~>"
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: '13.0'
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: ace-support-test-helpers
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - "~>"
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: '0.12'
|
|
89
|
+
type: :development
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - "~>"
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: '0.12'
|
|
96
|
+
description: Provides safe, atomic markdown file operations with frontmatter extraction,
|
|
97
|
+
section editing, and validation. Prevents file corruption through backup/rollback
|
|
98
|
+
mechanisms.
|
|
99
|
+
email:
|
|
100
|
+
- mc@cs3b.com
|
|
101
|
+
executables: []
|
|
102
|
+
extensions: []
|
|
103
|
+
extra_rdoc_files: []
|
|
104
|
+
files:
|
|
105
|
+
- CHANGELOG.md
|
|
106
|
+
- CONTRIBUTING.md
|
|
107
|
+
- LICENSE
|
|
108
|
+
- README.md
|
|
109
|
+
- Rakefile
|
|
110
|
+
- lib/ace/support/markdown.rb
|
|
111
|
+
- lib/ace/support/markdown/atoms/document_validator.rb
|
|
112
|
+
- lib/ace/support/markdown/atoms/frontmatter_extractor.rb
|
|
113
|
+
- lib/ace/support/markdown/atoms/frontmatter_serializer.rb
|
|
114
|
+
- lib/ace/support/markdown/atoms/section_extractor.rb
|
|
115
|
+
- lib/ace/support/markdown/models/markdown_document.rb
|
|
116
|
+
- lib/ace/support/markdown/models/section.rb
|
|
117
|
+
- lib/ace/support/markdown/molecules/document_builder.rb
|
|
118
|
+
- lib/ace/support/markdown/molecules/frontmatter_editor.rb
|
|
119
|
+
- lib/ace/support/markdown/molecules/kramdown_processor.rb
|
|
120
|
+
- lib/ace/support/markdown/molecules/section_editor.rb
|
|
121
|
+
- lib/ace/support/markdown/organisms/document_editor.rb
|
|
122
|
+
- lib/ace/support/markdown/organisms/safe_file_writer.rb
|
|
123
|
+
- lib/ace/support/markdown/version.rb
|
|
124
|
+
homepage: https://github.com/cs3b/ace
|
|
125
|
+
licenses:
|
|
126
|
+
- MIT
|
|
127
|
+
metadata:
|
|
128
|
+
homepage_uri: https://github.com/cs3b/ace
|
|
129
|
+
source_code_uri: https://github.com/cs3b/ace/tree/main/ace-support-markdown/
|
|
130
|
+
changelog_uri: https://github.com/cs3b/ace/blob/main/ace-support-markdown/CHANGELOG.md
|
|
131
|
+
rdoc_options: []
|
|
132
|
+
require_paths:
|
|
133
|
+
- lib
|
|
134
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
135
|
+
requirements:
|
|
136
|
+
- - ">="
|
|
137
|
+
- !ruby/object:Gem::Version
|
|
138
|
+
version: 3.2.0
|
|
139
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
140
|
+
requirements:
|
|
141
|
+
- - ">="
|
|
142
|
+
- !ruby/object:Gem::Version
|
|
143
|
+
version: '0'
|
|
144
|
+
requirements: []
|
|
145
|
+
rubygems_version: 3.6.9
|
|
146
|
+
specification_version: 4
|
|
147
|
+
summary: Safe markdown editing with frontmatter support for ACE gems
|
|
148
|
+
test_files: []
|