mdphlex 0.1.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 +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +123 -0
- data/Rakefile +9 -0
- data/config/quickdraw.rb +5 -0
- data/lib/mdphlex/md.rb +303 -0
- data/lib/mdphlex/version.rb +5 -0
- data/lib/mdphlex.rb +11 -0
- data/quickdraw/interoperability.test.rb +330 -0
- data/quickdraw/mdphlex.test.rb +423 -0
- metadata +82 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 30fde45581cd7d2edeada97dd0c281cbf4bf6447e6938f5002ad99231c4ac2f0
|
4
|
+
data.tar.gz: 1168f4d22457f7965160e4248f468d7bce4e0a45eb56a44b6285cb3aebe94d83
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 8aab63e64b863d0fdba508d8e9d2a570eb7b8bc8d9c11081cf4b8718a9c06365a6069df65024a2c868efd6bc60e1163f4adf20251f220f7c63d0d6d4b4ffd05b
|
7
|
+
data.tar.gz: 66efd4524781b9fcc52568879f352285db7477f078039c40f1c209c09e13d63c3dc0c24f95c20876839203a68982d79166bf125d54abe4017dc6c9c3c2008e69
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2025 Martin Emde
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# MDPhlex
|
2
|
+
|
3
|
+
MDPhlex is a Phlex component for rendering Markdown. Write your Markdown in Ruby with Phlex's familiar syntax and render it as a Markdown string.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'mdphlex'
|
11
|
+
```
|
12
|
+
|
13
|
+
## Basic Usage
|
14
|
+
|
15
|
+
Create an MDPhlex::MD component just like you would create a Phlex component:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
class HelloWorld < MDPhlex::MD
|
19
|
+
def view_template
|
20
|
+
h1 "Hello, World!"
|
21
|
+
|
22
|
+
p "Welcome to MDPhlex - writing Markdown with Phlex syntax!"
|
23
|
+
|
24
|
+
h2 "Features"
|
25
|
+
|
26
|
+
ul do
|
27
|
+
li "Write Markdown using Ruby"
|
28
|
+
li do
|
29
|
+
plain "Support for "
|
30
|
+
strong "bold"
|
31
|
+
plain ", "
|
32
|
+
em "italic"
|
33
|
+
plain ", and "
|
34
|
+
code "inline code"
|
35
|
+
end
|
36
|
+
li "Full Phlex component composition"
|
37
|
+
end
|
38
|
+
|
39
|
+
p do
|
40
|
+
plain "Check out the "
|
41
|
+
a(href: "https://github.com/martinemde/mdphlex") { "MDPhlex repository" }
|
42
|
+
plain " for more information."
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Render the component
|
48
|
+
puts HelloWorld.new.call
|
49
|
+
```
|
50
|
+
|
51
|
+
This outputs the following Markdown:
|
52
|
+
|
53
|
+
```markdown
|
54
|
+
# Hello, World!
|
55
|
+
|
56
|
+
Welcome to MDPhlex - writing Markdown with Phlex syntax!
|
57
|
+
|
58
|
+
## Features
|
59
|
+
|
60
|
+
- Write Markdown using Ruby
|
61
|
+
- Support for **bold**, *italic*, and `inline code`
|
62
|
+
- Full Phlex component composition
|
63
|
+
|
64
|
+
Check out the [MDPhlex repository](https://github.com/martinemde/mdphlex) for more information.
|
65
|
+
```
|
66
|
+
|
67
|
+
## Rendering MDPhlex inside Phlex::HTML
|
68
|
+
|
69
|
+
MDPhlex components can be seamlessly integrated into your Phlex::HTML views:
|
70
|
+
|
71
|
+
```ruby
|
72
|
+
class ArticlePage < Phlex::HTML
|
73
|
+
def initialize(article)
|
74
|
+
@article = article
|
75
|
+
end
|
76
|
+
|
77
|
+
def view_template
|
78
|
+
html do
|
79
|
+
head do
|
80
|
+
title { @article.title }
|
81
|
+
end
|
82
|
+
body do
|
83
|
+
article do
|
84
|
+
# Render MDPhlex component inside HTML
|
85
|
+
div(class: "markdown-content") do
|
86
|
+
plain render(ArticleContent.new(@article))
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class ArticleContent < MDPhlex::MD
|
95
|
+
def initialize(article)
|
96
|
+
@article = article
|
97
|
+
end
|
98
|
+
|
99
|
+
def view_template
|
100
|
+
h1 @article.title
|
101
|
+
|
102
|
+
p @article.summary
|
103
|
+
|
104
|
+
h2 "Contents"
|
105
|
+
|
106
|
+
@article.sections.each do |section|
|
107
|
+
h3 section.title
|
108
|
+
p section.content
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
```
|
113
|
+
|
114
|
+
## Why MDPhlex?
|
115
|
+
|
116
|
+
- **Component-based**: Build reusable Markdown components
|
117
|
+
- **Type-safe**: Get Ruby's type checking and IDE support
|
118
|
+
- **Composable**: Mix Phlex::HTML and MDPhlex components freely
|
119
|
+
- **Familiar**: Uses the same syntax as Phlex
|
120
|
+
|
121
|
+
## License
|
122
|
+
|
123
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
data/config/quickdraw.rb
ADDED
data/lib/mdphlex/md.rb
ADDED
@@ -0,0 +1,303 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "phlex"
|
4
|
+
|
5
|
+
module MDPhlex
|
6
|
+
class MD < Phlex::SGML
|
7
|
+
extend Phlex::SGML::Elements
|
8
|
+
|
9
|
+
# Register a block-level custom element that ensures proper newlines
|
10
|
+
def self.register_block_element(method_name, tag: method_name.to_s.tr("_", "-"))
|
11
|
+
define_method(method_name) do |**attributes, &block|
|
12
|
+
state = @_state
|
13
|
+
buffer = state.buffer
|
14
|
+
|
15
|
+
unless state.should_render?
|
16
|
+
__yield_content__(&block) if block
|
17
|
+
return nil
|
18
|
+
end
|
19
|
+
|
20
|
+
# Render the opening tag - always followed by newline
|
21
|
+
buffer << "<#{tag}"
|
22
|
+
|
23
|
+
if attributes.length > 0
|
24
|
+
attributes.each do |key, value|
|
25
|
+
buffer << " #{key}=\"#{Phlex::Escape.html_escape(value.to_s)}\""
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
buffer << ">\n"
|
30
|
+
|
31
|
+
# Render content if block given
|
32
|
+
if block
|
33
|
+
__yield_content__(&block)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Render closing tag - always followed by newline for block elements
|
37
|
+
buffer << "</#{tag}>\n"
|
38
|
+
|
39
|
+
nil
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def h1(content = nil, &)
|
44
|
+
heading(1, content, &)
|
45
|
+
end
|
46
|
+
|
47
|
+
def h2(content = nil, &)
|
48
|
+
heading(2, content, &)
|
49
|
+
end
|
50
|
+
|
51
|
+
def h3(content = nil, &)
|
52
|
+
heading(3, content, &)
|
53
|
+
end
|
54
|
+
|
55
|
+
def h4(content = nil, &)
|
56
|
+
heading(4, content, &)
|
57
|
+
end
|
58
|
+
|
59
|
+
def h5(content = nil, &)
|
60
|
+
heading(5, content, &)
|
61
|
+
end
|
62
|
+
|
63
|
+
def h6(content = nil, &)
|
64
|
+
heading(6, content, &)
|
65
|
+
end
|
66
|
+
|
67
|
+
def p(content = nil, &)
|
68
|
+
state = @_state
|
69
|
+
return unless state.should_render?
|
70
|
+
|
71
|
+
buffer = state.buffer
|
72
|
+
|
73
|
+
if block_given?
|
74
|
+
__yield_content__(&)
|
75
|
+
elsif content
|
76
|
+
buffer << content.to_s
|
77
|
+
end
|
78
|
+
|
79
|
+
buffer << "\n\n"
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
|
83
|
+
def strong(content = nil, &)
|
84
|
+
wrap_inline("**", content, &)
|
85
|
+
end
|
86
|
+
|
87
|
+
def em(content = nil, &)
|
88
|
+
wrap_inline("*", content, &)
|
89
|
+
end
|
90
|
+
|
91
|
+
def code(content = nil, &)
|
92
|
+
wrap_inline("`", content, &)
|
93
|
+
end
|
94
|
+
|
95
|
+
def a(href: nil, **attributes, &)
|
96
|
+
state = @_state
|
97
|
+
return unless state.should_render?
|
98
|
+
|
99
|
+
buffer = state.buffer
|
100
|
+
buffer << "["
|
101
|
+
|
102
|
+
__yield_content__(&) if block_given?
|
103
|
+
|
104
|
+
buffer << "]("
|
105
|
+
buffer << href.to_s if href
|
106
|
+
buffer << ")"
|
107
|
+
nil
|
108
|
+
end
|
109
|
+
|
110
|
+
def plain(content)
|
111
|
+
state = @_state
|
112
|
+
return unless state.should_render?
|
113
|
+
|
114
|
+
state.buffer << content.to_s
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
|
118
|
+
def blockquote(content = nil, &)
|
119
|
+
state = @_state
|
120
|
+
return unless state.should_render?
|
121
|
+
|
122
|
+
buffer = state.buffer
|
123
|
+
|
124
|
+
if block_given?
|
125
|
+
# Save current position to capture content
|
126
|
+
start_pos = buffer.length
|
127
|
+
__yield_content__(&)
|
128
|
+
# Extract the content that was added
|
129
|
+
captured = buffer[start_pos..]
|
130
|
+
# Remove it from buffer to re-add with > prefix
|
131
|
+
buffer.slice!(start_pos..)
|
132
|
+
|
133
|
+
# Add > prefix to each line
|
134
|
+
captured.lines.each do |line|
|
135
|
+
buffer << "> " << line
|
136
|
+
end
|
137
|
+
else
|
138
|
+
content.to_s.lines.each do |line|
|
139
|
+
buffer << "> " << line
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
buffer << "\n\n"
|
144
|
+
nil
|
145
|
+
end
|
146
|
+
|
147
|
+
def pre(language: nil, &)
|
148
|
+
state = @_state
|
149
|
+
return unless state.should_render?
|
150
|
+
|
151
|
+
buffer = state.buffer
|
152
|
+
buffer << "```"
|
153
|
+
buffer << language.to_s if language
|
154
|
+
buffer << "\n"
|
155
|
+
|
156
|
+
__yield_content__(&) if block_given?
|
157
|
+
|
158
|
+
buffer << "\n```\n\n"
|
159
|
+
nil
|
160
|
+
end
|
161
|
+
|
162
|
+
def ul(&)
|
163
|
+
state = @_state
|
164
|
+
return unless state.should_render?
|
165
|
+
|
166
|
+
buffer = state.buffer
|
167
|
+
@_list_type ||= []
|
168
|
+
|
169
|
+
# If we're in a list item and there's already content, add a newline
|
170
|
+
buffer << "\n" if @_list_type.length > 0 && buffer.length > 0 && !buffer.end_with?("\n")
|
171
|
+
|
172
|
+
@_list_type.push(:ul)
|
173
|
+
|
174
|
+
__yield_content__(&) if block_given?
|
175
|
+
|
176
|
+
@_list_type.pop
|
177
|
+
# Only add extra newline at the end of top-level lists
|
178
|
+
buffer << "\n" if @_list_type.empty?
|
179
|
+
nil
|
180
|
+
end
|
181
|
+
|
182
|
+
def ol(&)
|
183
|
+
state = @_state
|
184
|
+
return unless state.should_render?
|
185
|
+
|
186
|
+
buffer = state.buffer
|
187
|
+
@_list_type ||= []
|
188
|
+
|
189
|
+
# If we're in a list item and there's already content, add a newline
|
190
|
+
buffer << "\n" if @_list_type.length > 0 && buffer.length > 0 && !buffer.end_with?("\n")
|
191
|
+
|
192
|
+
@_list_type.push(:ol)
|
193
|
+
@_ol_counter ||= []
|
194
|
+
@_ol_counter.push(0)
|
195
|
+
|
196
|
+
__yield_content__(&) if block_given?
|
197
|
+
|
198
|
+
@_list_type.pop
|
199
|
+
@_ol_counter.pop
|
200
|
+
# Only add extra newline at the end of top-level lists
|
201
|
+
buffer << "\n" if @_list_type.empty?
|
202
|
+
nil
|
203
|
+
end
|
204
|
+
|
205
|
+
def li(content = nil, &)
|
206
|
+
state = @_state
|
207
|
+
return unless state.should_render?
|
208
|
+
|
209
|
+
buffer = state.buffer
|
210
|
+
@_list_type ||= []
|
211
|
+
list_depth = @_list_type.length
|
212
|
+
|
213
|
+
# Add indentation for nested lists
|
214
|
+
buffer << (" " * [list_depth - 1, 0].max)
|
215
|
+
|
216
|
+
# Add list marker
|
217
|
+
if @_list_type.last == :ol
|
218
|
+
@_ol_counter ||= []
|
219
|
+
@_ol_counter[-1] += 1
|
220
|
+
buffer << "#{@_ol_counter.last}. "
|
221
|
+
else
|
222
|
+
buffer << "- "
|
223
|
+
end
|
224
|
+
|
225
|
+
if block_given?
|
226
|
+
# Mark position before yielding content
|
227
|
+
start_pos = buffer.length
|
228
|
+
__yield_content__(&)
|
229
|
+
# Only add newline if we haven't already ended with one
|
230
|
+
# (nested lists already add their trailing newline)
|
231
|
+
buffer << "\n" unless buffer.end_with?("\n")
|
232
|
+
else
|
233
|
+
buffer << content.to_s if content
|
234
|
+
buffer << "\n"
|
235
|
+
end
|
236
|
+
|
237
|
+
nil
|
238
|
+
end
|
239
|
+
|
240
|
+
def img(src: nil, alt: nil, **attributes)
|
241
|
+
state = @_state
|
242
|
+
return unless state.should_render?
|
243
|
+
|
244
|
+
buffer = state.buffer
|
245
|
+
buffer << ""
|
250
|
+
nil
|
251
|
+
end
|
252
|
+
|
253
|
+
def hr(**attributes)
|
254
|
+
state = @_state
|
255
|
+
return unless state.should_render?
|
256
|
+
|
257
|
+
state.buffer << "---\n\n"
|
258
|
+
nil
|
259
|
+
end
|
260
|
+
|
261
|
+
def br
|
262
|
+
state = @_state
|
263
|
+
return unless state.should_render?
|
264
|
+
|
265
|
+
state.buffer << " \n"
|
266
|
+
nil
|
267
|
+
end
|
268
|
+
|
269
|
+
private def heading(level, content = nil, &)
|
270
|
+
state = @_state
|
271
|
+
return unless state.should_render?
|
272
|
+
|
273
|
+
buffer = state.buffer
|
274
|
+
buffer << ("#" * level) << " "
|
275
|
+
|
276
|
+
if block_given?
|
277
|
+
__yield_content__(&)
|
278
|
+
elsif content
|
279
|
+
buffer << content.to_s
|
280
|
+
end
|
281
|
+
|
282
|
+
buffer << "\n"
|
283
|
+
nil
|
284
|
+
end
|
285
|
+
|
286
|
+
private def wrap_inline(marker, content = nil, &)
|
287
|
+
state = @_state
|
288
|
+
return unless state.should_render?
|
289
|
+
|
290
|
+
buffer = state.buffer
|
291
|
+
buffer << marker
|
292
|
+
|
293
|
+
if block_given?
|
294
|
+
__yield_content__(&)
|
295
|
+
elsif content
|
296
|
+
buffer << content.to_s
|
297
|
+
end
|
298
|
+
|
299
|
+
buffer << marker
|
300
|
+
nil
|
301
|
+
end
|
302
|
+
end
|
303
|
+
end
|
data/lib/mdphlex.rb
ADDED
@@ -0,0 +1,330 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "phlex"
|
4
|
+
|
5
|
+
test "MDPhlex::MD component rendered inside Phlex::HTML component" do
|
6
|
+
# Define a simple Markdown component
|
7
|
+
markdown_component = Class.new(MDPhlex::MD) do
|
8
|
+
def view_template
|
9
|
+
h2 "Markdown Section"
|
10
|
+
p "This is a paragraph with **bold** text."
|
11
|
+
ul do
|
12
|
+
li "First item"
|
13
|
+
li "Second item"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Define an HTML component that renders the Markdown component
|
19
|
+
html_component = Class.new(Phlex::HTML) do
|
20
|
+
def initialize(markdown_component)
|
21
|
+
@markdown_component = markdown_component
|
22
|
+
end
|
23
|
+
|
24
|
+
def view_template
|
25
|
+
article do
|
26
|
+
h1 { "HTML Article" }
|
27
|
+
div(class: "markdown-content") do
|
28
|
+
plain render(@markdown_component.new)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Render and test
|
35
|
+
output = html_component.new(markdown_component).call
|
36
|
+
|
37
|
+
assert output.include?("<article>")
|
38
|
+
assert output.include?("<h1>HTML Article</h1>")
|
39
|
+
assert output.include?('<div class="markdown-content">')
|
40
|
+
assert output.include?("## Markdown Section")
|
41
|
+
assert output.include?("This is a paragraph with **bold** text.")
|
42
|
+
assert output.include?("- First item")
|
43
|
+
assert output.include?("- Second item")
|
44
|
+
assert output.include?("</article>")
|
45
|
+
end
|
46
|
+
|
47
|
+
test "MDPhlex::MD component rendered inside Phlex::HTML without .new" do
|
48
|
+
# Define a Markdown component that doesn't need initialization
|
49
|
+
simple_markdown = Class.new(MDPhlex::MD) do
|
50
|
+
def view_template
|
51
|
+
h1 "About"
|
52
|
+
p "This component can be rendered without calling .new"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# HTML component that renders the class directly
|
57
|
+
html_wrapper = Class.new(Phlex::HTML) do
|
58
|
+
def initialize(markdown_class)
|
59
|
+
@markdown_class = markdown_class
|
60
|
+
end
|
61
|
+
|
62
|
+
def view_template
|
63
|
+
div(class: "wrapper") do
|
64
|
+
# Render without .new - Phlex should automatically initialize
|
65
|
+
plain render(@markdown_class)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
output = html_wrapper.new(simple_markdown).call
|
71
|
+
|
72
|
+
assert output.include?('<div class="wrapper">')
|
73
|
+
assert output.include?("# About")
|
74
|
+
assert output.include?("This component can be rendered without calling .new")
|
75
|
+
end
|
76
|
+
|
77
|
+
test "Phlex::HTML component rendered inside MDPhlex::MD component" do
|
78
|
+
# Define an HTML component
|
79
|
+
button_component = Class.new(Phlex::HTML) do
|
80
|
+
def initialize(text:, variant: "primary")
|
81
|
+
@text = text
|
82
|
+
@variant = variant
|
83
|
+
end
|
84
|
+
|
85
|
+
def view_template
|
86
|
+
button(type: "button", class: "btn btn-#{@variant}") { @text }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Define a Markdown component that renders HTML components
|
91
|
+
markdown_component = Class.new(MDPhlex::MD) do
|
92
|
+
def initialize(button_component)
|
93
|
+
@button_component = button_component
|
94
|
+
end
|
95
|
+
|
96
|
+
def view_template
|
97
|
+
h1 "Interactive Documentation"
|
98
|
+
|
99
|
+
p "Here's an example of a button component:"
|
100
|
+
|
101
|
+
# Render the HTML component directly
|
102
|
+
render @button_component.new(text: "Click me!", variant: "primary")
|
103
|
+
|
104
|
+
p "You can also use different variants:"
|
105
|
+
|
106
|
+
render @button_component.new(text: "Warning", variant: "warning")
|
107
|
+
render @button_component.new(text: "Danger", variant: "danger")
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Render and test
|
112
|
+
output = markdown_component.new(button_component).call
|
113
|
+
|
114
|
+
assert output.include?("# Interactive Documentation")
|
115
|
+
assert output.include?("Here's an example of a button component:")
|
116
|
+
assert output.include?('<button type="button" class="btn btn-primary">Click me!</button>')
|
117
|
+
assert output.include?("You can also use different variants:")
|
118
|
+
assert output.include?('<button type="button" class="btn btn-warning">Warning</button>')
|
119
|
+
assert output.include?('<button type="button" class="btn btn-danger">Danger</button>')
|
120
|
+
end
|
121
|
+
|
122
|
+
test "MDPhlex::MD component rendered inside another MDPhlex::MD component" do
|
123
|
+
# Define a reusable Markdown component for code examples
|
124
|
+
code_example = Class.new(MDPhlex::MD) do
|
125
|
+
def initialize(title:, code:, language: "ruby")
|
126
|
+
@title = title
|
127
|
+
@code = code
|
128
|
+
@language = language
|
129
|
+
end
|
130
|
+
|
131
|
+
def view_template
|
132
|
+
h3 @title
|
133
|
+
pre(language: @language) { plain @code }
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# Define a parent Markdown component that uses the code example component
|
138
|
+
tutorial_component = Class.new(MDPhlex::MD) do
|
139
|
+
def initialize(code_example_component)
|
140
|
+
@code_example_component = code_example_component
|
141
|
+
end
|
142
|
+
|
143
|
+
def view_template
|
144
|
+
h1 "MDPhlex Tutorial"
|
145
|
+
|
146
|
+
p "Learn how to use MDPhlex for creating beautiful documentation."
|
147
|
+
|
148
|
+
h2 "Examples"
|
149
|
+
|
150
|
+
# Render child MDPhlex::MD components
|
151
|
+
render @code_example_component.new(
|
152
|
+
title: "Basic Usage",
|
153
|
+
code: <<~RUBY
|
154
|
+
class MyDoc < MDPhlex
|
155
|
+
def view_template
|
156
|
+
h1 "Hello World"
|
157
|
+
p "This is MDPhlex!"
|
158
|
+
end
|
159
|
+
end
|
160
|
+
RUBY
|
161
|
+
)
|
162
|
+
|
163
|
+
render @code_example_component.new(
|
164
|
+
title: "Lists and Formatting",
|
165
|
+
code: <<~RUBY
|
166
|
+
ul do
|
167
|
+
li "Item with **bold** text"
|
168
|
+
li "Item with `code`"
|
169
|
+
end
|
170
|
+
RUBY
|
171
|
+
)
|
172
|
+
|
173
|
+
p "These examples show the flexibility of component composition."
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Render and test
|
178
|
+
output = tutorial_component.new(code_example).call
|
179
|
+
|
180
|
+
assert output.include?("# MDPhlex Tutorial")
|
181
|
+
assert output.include?("Learn how to use MDPhlex for creating beautiful documentation.")
|
182
|
+
assert output.include?("## Examples")
|
183
|
+
assert output.include?("### Basic Usage")
|
184
|
+
assert output.include?("```ruby")
|
185
|
+
assert output.include?('h1 "Hello World"')
|
186
|
+
assert output.include?("### Lists and Formatting")
|
187
|
+
assert output.include?('li "Item with **bold** text"')
|
188
|
+
assert output.include?("These examples show the flexibility of component composition.")
|
189
|
+
end
|
190
|
+
|
191
|
+
test "Components yielding content blocks across Phlex::HTML and MDPhlex::MD" do
|
192
|
+
# Define a card component that yields content
|
193
|
+
card_component = Class.new(Phlex::HTML) do
|
194
|
+
def initialize(title:)
|
195
|
+
@title = title
|
196
|
+
end
|
197
|
+
|
198
|
+
def view_template
|
199
|
+
div(class: "card") do
|
200
|
+
h2(class: "card-title") { @title }
|
201
|
+
div(class: "card-content") do
|
202
|
+
yield
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
# MDPhlex::MD component that uses the card and passes content
|
209
|
+
markdown_component = Class.new(MDPhlex::MD) do
|
210
|
+
def initialize(card_class)
|
211
|
+
@card_class = card_class
|
212
|
+
end
|
213
|
+
|
214
|
+
def view_template
|
215
|
+
h1 "Documentation with Cards"
|
216
|
+
|
217
|
+
# Render HTML component with a content block
|
218
|
+
render @card_class.new(title: "Important Note") do
|
219
|
+
p "This is **important** information inside a card."
|
220
|
+
ul do
|
221
|
+
li "First point"
|
222
|
+
li "Second point"
|
223
|
+
end
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
output = markdown_component.new(card_component).call
|
229
|
+
|
230
|
+
assert output.include?("# Documentation with Cards")
|
231
|
+
assert output.include?('<div class="card">')
|
232
|
+
assert output.include?('<h2 class="card-title">Important Note</h2>')
|
233
|
+
assert output.include?('<div class="card-content">')
|
234
|
+
assert output.include?("This is **important** information inside a card.")
|
235
|
+
assert output.include?("- First point")
|
236
|
+
end
|
237
|
+
|
238
|
+
test "Interface yielding pattern between MDPhlex::MD and Phlex::HTML" do
|
239
|
+
# Define a nav component with interface yielding
|
240
|
+
nav_component = Class.new(Phlex::HTML) do
|
241
|
+
def view_template(&)
|
242
|
+
nav(class: "special-nav", &)
|
243
|
+
end
|
244
|
+
|
245
|
+
def item(href, &)
|
246
|
+
a(class: "nav-item", href:, &)
|
247
|
+
end
|
248
|
+
|
249
|
+
def divider
|
250
|
+
span(class: "nav-divider") { "|" }
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
# MDPhlex::MD component that uses the nav interface
|
255
|
+
docs_layout = Class.new(MDPhlex::MD) do
|
256
|
+
def initialize(nav_class)
|
257
|
+
@nav_class = nav_class
|
258
|
+
end
|
259
|
+
|
260
|
+
def view_template
|
261
|
+
h1 "Site Navigation Example"
|
262
|
+
|
263
|
+
p "Here's how to use the nav component:"
|
264
|
+
|
265
|
+
# Use the yielded interface
|
266
|
+
render @nav_class.new do |nav|
|
267
|
+
nav.item("/") { "Home" }
|
268
|
+
nav.item("/docs") { "Documentation" }
|
269
|
+
nav.divider
|
270
|
+
nav.item("/about") { "About" }
|
271
|
+
end
|
272
|
+
|
273
|
+
p "The nav component yields an interface for building navigation."
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
output = docs_layout.new(nav_component).call
|
278
|
+
|
279
|
+
assert output.include?("# Site Navigation Example")
|
280
|
+
assert output.include?('<nav class="special-nav">')
|
281
|
+
assert output.include?('<a class="nav-item" href="/">Home</a>')
|
282
|
+
assert output.include?('<a class="nav-item" href="/docs">Documentation</a>')
|
283
|
+
assert output.include?('<span class="nav-divider">|</span>')
|
284
|
+
assert output.include?('<a class="nav-item" href="/about">About</a>')
|
285
|
+
end
|
286
|
+
|
287
|
+
test "Conditional rendering with render? method" do
|
288
|
+
# HTML component with conditional rendering
|
289
|
+
notification_badge = Class.new(Phlex::HTML) do
|
290
|
+
def initialize(count:)
|
291
|
+
@count = count
|
292
|
+
end
|
293
|
+
|
294
|
+
def view_template
|
295
|
+
span(class: "badge") { @count }
|
296
|
+
end
|
297
|
+
|
298
|
+
def render?
|
299
|
+
@count > 0
|
300
|
+
end
|
301
|
+
end
|
302
|
+
|
303
|
+
# MDPhlex::MD component that conditionally renders the badge
|
304
|
+
user_profile = Class.new(MDPhlex::MD) do
|
305
|
+
def initialize(badge_class, notifications_count)
|
306
|
+
@badge_class = badge_class
|
307
|
+
@notifications_count = notifications_count
|
308
|
+
end
|
309
|
+
|
310
|
+
def view_template
|
311
|
+
h1 "User Profile"
|
312
|
+
|
313
|
+
p do
|
314
|
+
plain "Notifications "
|
315
|
+
render @badge_class.new(count: @notifications_count)
|
316
|
+
end
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
320
|
+
# Test with notifications
|
321
|
+
output_with = user_profile.new(notification_badge, 5).call
|
322
|
+
assert output_with.include?("# User Profile")
|
323
|
+
assert output_with.include?("Notifications <span class=\"badge\">5</span>")
|
324
|
+
|
325
|
+
# Test without notifications
|
326
|
+
output_without = user_profile.new(notification_badge, 0).call
|
327
|
+
assert output_without.include?("# User Profile")
|
328
|
+
assert output_without.include?("Notifications ")
|
329
|
+
assert !output_without.include?("badge")
|
330
|
+
end
|
@@ -0,0 +1,423 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
test "renders h1 heading" do
|
4
|
+
example = Class.new(MDPhlex::MD) do
|
5
|
+
def view_template
|
6
|
+
h1 "Hello World"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
assert_equal example.new.call, "# Hello World\n"
|
11
|
+
end
|
12
|
+
|
13
|
+
test "renders h2 heading" do
|
14
|
+
example = Class.new(MDPhlex::MD) do
|
15
|
+
def view_template
|
16
|
+
h2 "Subheading"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
assert_equal example.new.call, "## Subheading\n"
|
21
|
+
end
|
22
|
+
|
23
|
+
test "renders h3 heading" do
|
24
|
+
example = Class.new(MDPhlex::MD) do
|
25
|
+
def view_template
|
26
|
+
h3 "Section"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
assert_equal example.new.call, "### Section\n"
|
31
|
+
end
|
32
|
+
|
33
|
+
test "renders h4-h6 headings" do
|
34
|
+
example = Class.new(MDPhlex::MD) do
|
35
|
+
def view_template
|
36
|
+
h4 "Level 4"
|
37
|
+
h5 "Level 5"
|
38
|
+
h6 "Level 6"
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
assert_equal example.new.call, "#### Level 4\n##### Level 5\n###### Level 6\n"
|
43
|
+
end
|
44
|
+
|
45
|
+
test "renders paragraph" do
|
46
|
+
example = Class.new(MDPhlex::MD) do
|
47
|
+
def view_template
|
48
|
+
p "This is a paragraph."
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
assert_equal example.new.call, "This is a paragraph.\n\n"
|
53
|
+
end
|
54
|
+
|
55
|
+
test "renders strong text" do
|
56
|
+
example = Class.new(MDPhlex::MD) do
|
57
|
+
def view_template
|
58
|
+
p do
|
59
|
+
plain "This is "
|
60
|
+
strong "bold"
|
61
|
+
plain " text."
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
assert_equal example.new.call, "This is **bold** text.\n\n"
|
67
|
+
end
|
68
|
+
|
69
|
+
test "renders emphasized text" do
|
70
|
+
example = Class.new(MDPhlex::MD) do
|
71
|
+
def view_template
|
72
|
+
p do
|
73
|
+
plain "This is "
|
74
|
+
em "italic"
|
75
|
+
plain " text."
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
assert_equal example.new.call, "This is *italic* text.\n\n"
|
81
|
+
end
|
82
|
+
|
83
|
+
test "renders inline code" do
|
84
|
+
example = Class.new(MDPhlex::MD) do
|
85
|
+
def view_template
|
86
|
+
p do
|
87
|
+
plain "Use "
|
88
|
+
code "puts 'hello'"
|
89
|
+
plain " to print."
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
assert_equal example.new.call, "Use `puts 'hello'` to print.\n\n"
|
95
|
+
end
|
96
|
+
|
97
|
+
test "renders links" do
|
98
|
+
example = Class.new(MDPhlex::MD) do
|
99
|
+
def view_template
|
100
|
+
p do
|
101
|
+
a(href: "https://example.com") { "Click here" }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
assert_equal example.new.call, "[Click here](https://example.com)\n\n"
|
107
|
+
end
|
108
|
+
|
109
|
+
test "separates multiple paragraphs with blank line" do
|
110
|
+
example = Class.new(MDPhlex::MD) do
|
111
|
+
def view_template
|
112
|
+
p "First paragraph."
|
113
|
+
p "Second paragraph."
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
assert_equal example.new.call, "First paragraph.\n\nSecond paragraph.\n\n"
|
118
|
+
end
|
119
|
+
|
120
|
+
test "renders blockquotes" do
|
121
|
+
example = Class.new(MDPhlex::MD) do
|
122
|
+
def view_template
|
123
|
+
blockquote "This is a quote."
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
assert_equal example.new.call, "> This is a quote.\n\n"
|
128
|
+
end
|
129
|
+
|
130
|
+
test "renders blockquotes with block content" do
|
131
|
+
example = Class.new(MDPhlex::MD) do
|
132
|
+
def view_template
|
133
|
+
blockquote do
|
134
|
+
plain "This is a "
|
135
|
+
strong "quoted"
|
136
|
+
plain " text."
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
assert_equal example.new.call, "> This is a **quoted** text.\n\n"
|
142
|
+
end
|
143
|
+
|
144
|
+
test "renders multiline blockquotes" do
|
145
|
+
example = Class.new(MDPhlex::MD) do
|
146
|
+
def view_template
|
147
|
+
blockquote "Line 1\nLine 2\nLine 3"
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
assert_equal example.new.call, "> Line 1\n> Line 2\n> Line 3\n\n"
|
152
|
+
end
|
153
|
+
|
154
|
+
test "renders code blocks" do
|
155
|
+
example = Class.new(MDPhlex::MD) do
|
156
|
+
def view_template
|
157
|
+
pre do
|
158
|
+
plain "def hello\n puts 'world'\nend"
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
assert_equal example.new.call, "```\ndef hello\n puts 'world'\nend\n```\n\n"
|
164
|
+
end
|
165
|
+
|
166
|
+
test "renders code blocks with language" do
|
167
|
+
example = Class.new(MDPhlex::MD) do
|
168
|
+
def view_template
|
169
|
+
pre(language: "ruby") do
|
170
|
+
plain "def hello\n puts 'world'\nend"
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
assert_equal example.new.call, "```ruby\ndef hello\n puts 'world'\nend\n```\n\n"
|
176
|
+
end
|
177
|
+
|
178
|
+
test "renders unordered lists" do
|
179
|
+
example = Class.new(MDPhlex::MD) do
|
180
|
+
def view_template
|
181
|
+
ul do
|
182
|
+
li "First item"
|
183
|
+
li "Second item"
|
184
|
+
li "Third item"
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
assert_equal example.new.call, "- First item\n- Second item\n- Third item\n\n"
|
190
|
+
end
|
191
|
+
|
192
|
+
test "renders ordered lists" do
|
193
|
+
example = Class.new(MDPhlex::MD) do
|
194
|
+
def view_template
|
195
|
+
ol do
|
196
|
+
li "First item"
|
197
|
+
li "Second item"
|
198
|
+
li "Third item"
|
199
|
+
end
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
assert_equal example.new.call, "1. First item\n2. Second item\n3. Third item\n\n"
|
204
|
+
end
|
205
|
+
|
206
|
+
test "renders nested lists" do
|
207
|
+
example = Class.new(MDPhlex::MD) do
|
208
|
+
def view_template
|
209
|
+
ul do
|
210
|
+
li "Item 1"
|
211
|
+
li do
|
212
|
+
plain "Item 2"
|
213
|
+
ul do
|
214
|
+
li "Nested item 2.1"
|
215
|
+
li "Nested item 2.2"
|
216
|
+
end
|
217
|
+
end
|
218
|
+
li "Item 3"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
assert_equal example.new.call, "- Item 1\n- Item 2\n - Nested item 2.1\n - Nested item 2.2\n- Item 3\n\n"
|
224
|
+
end
|
225
|
+
|
226
|
+
test "renders images" do
|
227
|
+
example = Class.new(MDPhlex::MD) do
|
228
|
+
def view_template
|
229
|
+
img(src: "https://example.com/image.jpg", alt: "Example image")
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
assert_equal example.new.call, ""
|
234
|
+
end
|
235
|
+
|
236
|
+
test "renders images without alt text" do
|
237
|
+
example = Class.new(MDPhlex::MD) do
|
238
|
+
def view_template
|
239
|
+
img(src: "https://example.com/image.jpg")
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
assert_equal example.new.call, ""
|
244
|
+
end
|
245
|
+
|
246
|
+
test "renders horizontal rules" do
|
247
|
+
example = Class.new(MDPhlex::MD) do
|
248
|
+
def view_template
|
249
|
+
p "Above the line"
|
250
|
+
hr
|
251
|
+
p "Below the line"
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
assert_equal example.new.call, "Above the line\n\n---\n\nBelow the line\n\n"
|
256
|
+
end
|
257
|
+
|
258
|
+
test "renders line breaks" do
|
259
|
+
example = Class.new(MDPhlex::MD) do
|
260
|
+
def view_template
|
261
|
+
p do
|
262
|
+
plain "Line 1"
|
263
|
+
br
|
264
|
+
plain "Line 2"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
assert_equal example.new.call, "Line 1 \nLine 2\n\n"
|
270
|
+
end
|
271
|
+
|
272
|
+
test "renders custom element registered with register_element" do
|
273
|
+
example = Class.new(MDPhlex::MD) do
|
274
|
+
register_element :system
|
275
|
+
|
276
|
+
def view_template
|
277
|
+
system { "System message" }
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
assert_equal example.new.call, "<system>System message</system>"
|
282
|
+
end
|
283
|
+
|
284
|
+
test "renders custom element with attributes" do
|
285
|
+
example = Class.new(MDPhlex::MD) do
|
286
|
+
register_element :system
|
287
|
+
|
288
|
+
def view_template
|
289
|
+
system(type: "warning", level: "high") { "Alert!" }
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
assert_equal example.new.call, '<system type="warning" level="high">Alert!</system>'
|
294
|
+
end
|
295
|
+
|
296
|
+
test "renders custom element within markdown context" do
|
297
|
+
example = Class.new(MDPhlex::MD) do
|
298
|
+
register_element :system
|
299
|
+
|
300
|
+
def view_template
|
301
|
+
p do
|
302
|
+
plain "This is a "
|
303
|
+
system { "system message" }
|
304
|
+
plain " in a paragraph."
|
305
|
+
end
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
assert_equal example.new.call, "This is a <system>system message</system> in a paragraph.\n\n"
|
310
|
+
end
|
311
|
+
|
312
|
+
test "renders nested custom block elements" do
|
313
|
+
example = Class.new(MDPhlex::MD) do
|
314
|
+
register_block_element :system
|
315
|
+
register_block_element :tools
|
316
|
+
|
317
|
+
def view_template
|
318
|
+
system do
|
319
|
+
h1 "Main:"
|
320
|
+
tools do
|
321
|
+
ul do
|
322
|
+
li "nested content 1"
|
323
|
+
li "nested content 2"
|
324
|
+
end
|
325
|
+
end
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
329
|
+
|
330
|
+
assert_equal example.new.call, <<~XML
|
331
|
+
<system>
|
332
|
+
# Main:
|
333
|
+
<tools>
|
334
|
+
- nested content 1
|
335
|
+
- nested content 2
|
336
|
+
|
337
|
+
</tools>
|
338
|
+
</system>
|
339
|
+
XML
|
340
|
+
end
|
341
|
+
|
342
|
+
test "register_block_element vs register_element spacing" do
|
343
|
+
example = Class.new(MDPhlex::MD) do
|
344
|
+
register_element :inline_tag
|
345
|
+
register_block_element :block_tag
|
346
|
+
|
347
|
+
def view_template
|
348
|
+
p do
|
349
|
+
plain "Text with "
|
350
|
+
inline_tag { "inline" }
|
351
|
+
plain " content."
|
352
|
+
end
|
353
|
+
|
354
|
+
block_tag { p "Block content" }
|
355
|
+
|
356
|
+
p "After block"
|
357
|
+
end
|
358
|
+
end
|
359
|
+
|
360
|
+
assert_equal example.new.call, <<~MD
|
361
|
+
Text with <inline-tag>inline</inline-tag> content.
|
362
|
+
|
363
|
+
<block-tag>
|
364
|
+
Block content
|
365
|
+
|
366
|
+
</block-tag>
|
367
|
+
After block
|
368
|
+
|
369
|
+
MD
|
370
|
+
end
|
371
|
+
|
372
|
+
test "register_block_element with attributes" do
|
373
|
+
example = Class.new(MDPhlex::MD) do
|
374
|
+
register_block_element :section
|
375
|
+
|
376
|
+
def view_template
|
377
|
+
section(id: "main", class: "container") do
|
378
|
+
h2 "Section Title"
|
379
|
+
p "Section content"
|
380
|
+
end
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
assert_equal example.new.call, <<~MD
|
385
|
+
<section id="main" class="container">
|
386
|
+
## Section Title
|
387
|
+
Section content
|
388
|
+
|
389
|
+
</section>
|
390
|
+
MD
|
391
|
+
end
|
392
|
+
|
393
|
+
test "multiple register_block_elements" do
|
394
|
+
example = Class.new(MDPhlex::MD) do
|
395
|
+
register_block_element :article
|
396
|
+
register_block_element :aside
|
397
|
+
|
398
|
+
def view_template
|
399
|
+
article do
|
400
|
+
h1 "Main Article"
|
401
|
+
p "Article content"
|
402
|
+
end
|
403
|
+
|
404
|
+
aside do
|
405
|
+
h2 "Related"
|
406
|
+
p "Sidebar content"
|
407
|
+
end
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
assert_equal example.new.call, <<~MD
|
412
|
+
<article>
|
413
|
+
# Main Article
|
414
|
+
Article content
|
415
|
+
|
416
|
+
</article>
|
417
|
+
<aside>
|
418
|
+
## Related
|
419
|
+
Sidebar content
|
420
|
+
|
421
|
+
</aside>
|
422
|
+
MD
|
423
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mdphlex
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martin Emde
|
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: phlex
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
15
|
+
requirements:
|
16
|
+
- - "~>"
|
17
|
+
- !ruby/object:Gem::Version
|
18
|
+
version: '2.0'
|
19
|
+
type: :runtime
|
20
|
+
prerelease: false
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
22
|
+
requirements:
|
23
|
+
- - "~>"
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: '2.0'
|
26
|
+
- !ruby/object:Gem::Dependency
|
27
|
+
name: zeitwerk
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
29
|
+
requirements:
|
30
|
+
- - "~>"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '2.7'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - "~>"
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '2.7'
|
40
|
+
description: A Phlex-based Markdown renderer that generates Markdown output
|
41
|
+
email:
|
42
|
+
- me@martinemde.com
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
extra_rdoc_files: []
|
46
|
+
files:
|
47
|
+
- CHANGELOG.md
|
48
|
+
- LICENSE.txt
|
49
|
+
- README.md
|
50
|
+
- Rakefile
|
51
|
+
- config/quickdraw.rb
|
52
|
+
- lib/mdphlex.rb
|
53
|
+
- lib/mdphlex/md.rb
|
54
|
+
- lib/mdphlex/version.rb
|
55
|
+
- quickdraw/interoperability.test.rb
|
56
|
+
- quickdraw/mdphlex.test.rb
|
57
|
+
homepage: https://github.com/martinemde/mdphlex
|
58
|
+
licenses:
|
59
|
+
- MIT
|
60
|
+
metadata:
|
61
|
+
allowed_push_host: https://rubygems.org
|
62
|
+
homepage_uri: https://github.com/martinemde/mdphlex
|
63
|
+
source_code_uri: https://github.com/martinemde/mdphlex
|
64
|
+
changelog_uri: https://github.com/martinemde/mdphlex/blob/main/CHANGELOG.md
|
65
|
+
rdoc_options: []
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: 3.2.0
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
requirements: []
|
79
|
+
rubygems_version: 3.6.9
|
80
|
+
specification_version: 4
|
81
|
+
summary: Markdown renderer for Phlex
|
82
|
+
test_files: []
|