prosereflect 0.1.0 → 0.2.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 +4 -4
- data/.github/workflows/rake.yml +4 -0
- data/.github/workflows/release.yml +5 -0
- data/.rubocop.yml +19 -1
- data/.rubocop_todo.yml +143 -174
- data/CLAUDE.md +78 -0
- data/Gemfile +8 -4
- data/README.adoc +193 -12
- data/Rakefile +3 -3
- data/lib/prosereflect/attribute/base.rb +32 -0
- data/lib/prosereflect/attribute/bold.rb +18 -0
- data/lib/prosereflect/attribute/href.rb +22 -0
- data/lib/prosereflect/attribute/id.rb +24 -0
- data/lib/prosereflect/attribute.rb +10 -0
- data/lib/prosereflect/blockquote.rb +84 -0
- data/lib/prosereflect/bullet_list.rb +84 -0
- data/lib/prosereflect/code_block.rb +135 -0
- data/lib/prosereflect/code_block_wrapper.rb +65 -0
- data/lib/prosereflect/document.rb +93 -26
- data/lib/prosereflect/hard_break.rb +13 -11
- data/lib/prosereflect/heading.rb +63 -0
- data/lib/prosereflect/horizontal_rule.rb +70 -0
- data/lib/prosereflect/image.rb +126 -0
- data/lib/prosereflect/input/html.rb +484 -0
- data/lib/prosereflect/input.rb +7 -0
- data/lib/prosereflect/list_item.rb +64 -0
- data/lib/prosereflect/mark/base.rb +47 -0
- data/lib/prosereflect/mark/bold.rb +13 -0
- data/lib/prosereflect/mark/code.rb +12 -0
- data/lib/prosereflect/mark/italic.rb +13 -0
- data/lib/prosereflect/mark/link.rb +16 -0
- data/lib/prosereflect/mark/strike.rb +13 -0
- data/lib/prosereflect/mark/subscript.rb +13 -0
- data/lib/prosereflect/mark/superscript.rb +13 -0
- data/lib/prosereflect/mark/underline.rb +13 -0
- data/lib/prosereflect/mark.rb +15 -0
- data/lib/prosereflect/node.rb +181 -32
- data/lib/prosereflect/ordered_list.rb +86 -0
- data/lib/prosereflect/output/html.rb +376 -0
- data/lib/prosereflect/output.rb +7 -0
- data/lib/prosereflect/paragraph.rb +29 -20
- data/lib/prosereflect/parser.rb +101 -33
- data/lib/prosereflect/table.rb +42 -12
- data/lib/prosereflect/table_cell.rb +36 -11
- data/lib/prosereflect/table_header.rb +92 -0
- data/lib/prosereflect/table_row.rb +34 -11
- data/lib/prosereflect/text.rb +15 -19
- data/lib/prosereflect/user.rb +63 -0
- data/lib/prosereflect/version.rb +1 -1
- data/lib/prosereflect.rb +27 -11
- data/prosereflect.gemspec +17 -15
- data/spec/prosereflect/document_spec.rb +477 -75
- data/spec/prosereflect/hard_break_spec.rb +226 -30
- data/spec/prosereflect/input/html_spec.rb +797 -0
- data/spec/prosereflect/node_spec.rb +307 -137
- data/spec/prosereflect/output/html_spec.rb +369 -0
- data/spec/prosereflect/paragraph_spec.rb +458 -82
- data/spec/prosereflect/parser_spec.rb +311 -93
- data/spec/prosereflect/table_cell_spec.rb +282 -71
- data/spec/prosereflect/table_row_spec.rb +218 -48
- data/spec/prosereflect/table_spec.rb +415 -82
- data/spec/prosereflect/text_spec.rb +231 -72
- data/spec/prosereflect/user_spec.rb +76 -0
- data/spec/prosereflect_spec.rb +30 -23
- data/spec/spec_helper.rb +6 -6
- data/spec/support/matchers.rb +6 -6
- data/spec/support/shared_examples.rb +79 -50
- metadata +53 -6
- data/debug_loading.rb +0 -34
- data/spec/prosereflect/version_spec.rb +0 -11
|
@@ -1,114 +1,325 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "spec_helper"
|
|
4
4
|
|
|
5
5
|
RSpec.describe Prosereflect::TableCell do
|
|
6
|
-
describe
|
|
7
|
-
it
|
|
8
|
-
cell = described_class.new({
|
|
9
|
-
expect(cell.type).to eq(
|
|
6
|
+
describe "initialization" do
|
|
7
|
+
it "initializes as a table_cell node" do
|
|
8
|
+
cell = described_class.new({ "type" => "table_cell" })
|
|
9
|
+
expect(cell.type).to eq("table_cell")
|
|
10
10
|
end
|
|
11
11
|
end
|
|
12
12
|
|
|
13
|
-
describe
|
|
14
|
-
it
|
|
13
|
+
describe ".create" do
|
|
14
|
+
it "creates an empty table cell" do
|
|
15
15
|
cell = described_class.create
|
|
16
|
-
expect(cell).to be_a(described_class)
|
|
17
|
-
expect(cell.type).to eq('table_cell')
|
|
18
|
-
expect(cell.content).to be_empty
|
|
19
|
-
end
|
|
20
16
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
17
|
+
expected = {
|
|
18
|
+
"type" => "table_cell",
|
|
19
|
+
"content" => [],
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
expect(cell.to_h).to eq(expected)
|
|
25
23
|
end
|
|
26
|
-
end
|
|
27
24
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
it "creates a table cell with attributes" do
|
|
26
|
+
cell = described_class.create(attrs: {
|
|
27
|
+
"colspan" => 2,
|
|
28
|
+
"rowspan" => 1,
|
|
29
|
+
"background" => "#f5f5f5",
|
|
30
|
+
"align" => "center",
|
|
31
|
+
"valign" => "middle",
|
|
32
|
+
})
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
expected = {
|
|
35
|
+
"type" => "table_cell",
|
|
36
|
+
"attrs" => {
|
|
37
|
+
"colspan" => 2,
|
|
38
|
+
"rowspan" => 1,
|
|
39
|
+
"background" => "#f5f5f5",
|
|
40
|
+
"align" => "center",
|
|
41
|
+
"valign" => "middle",
|
|
42
|
+
},
|
|
43
|
+
"content" => [],
|
|
44
|
+
}
|
|
37
45
|
|
|
38
|
-
|
|
39
|
-
cell = described_class.create
|
|
40
|
-
expect(cell.paragraphs).to eq([])
|
|
46
|
+
expect(cell.to_h).to eq(expected)
|
|
41
47
|
end
|
|
42
48
|
end
|
|
43
49
|
|
|
44
|
-
describe
|
|
45
|
-
it
|
|
50
|
+
describe "cell structure" do
|
|
51
|
+
it "creates a cell with simple text content" do
|
|
46
52
|
cell = described_class.create
|
|
47
|
-
cell.add_paragraph(
|
|
48
|
-
cell.add_paragraph('Second paragraph')
|
|
53
|
+
cell.add_paragraph("Simple text")
|
|
49
54
|
|
|
50
|
-
|
|
55
|
+
expected = {
|
|
56
|
+
"type" => "table_cell",
|
|
57
|
+
"content" => [{
|
|
58
|
+
"type" => "paragraph",
|
|
59
|
+
"content" => [{
|
|
60
|
+
"type" => "text",
|
|
61
|
+
"text" => "Simple text",
|
|
62
|
+
}],
|
|
63
|
+
}],
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
expect(cell.to_h).to eq(expected)
|
|
51
67
|
end
|
|
52
68
|
|
|
53
|
-
it
|
|
69
|
+
it "creates a cell with complex content" do
|
|
54
70
|
cell = described_class.create
|
|
55
|
-
|
|
71
|
+
|
|
72
|
+
# First paragraph with mixed formatting
|
|
73
|
+
para = cell.add_paragraph("This is ")
|
|
74
|
+
para.add_text("bold", [Prosereflect::Mark::Bold.new])
|
|
75
|
+
para.add_text(" and ")
|
|
76
|
+
para.add_text("italic", [Prosereflect::Mark::Italic.new])
|
|
77
|
+
para.add_hard_break
|
|
78
|
+
para.add_text("with a line break")
|
|
79
|
+
|
|
80
|
+
# Second paragraph with more formatting
|
|
81
|
+
para = cell.add_paragraph
|
|
82
|
+
para.add_text("Status: ", [Prosereflect::Mark::Bold.new])
|
|
83
|
+
para.add_text("Done", [Prosereflect::Mark::Strike.new])
|
|
84
|
+
para.add_text(" → ")
|
|
85
|
+
para.add_text("In Progress", [Prosereflect::Mark::Italic.new])
|
|
86
|
+
|
|
87
|
+
expected = {
|
|
88
|
+
"type" => "table_cell",
|
|
89
|
+
"content" => [{
|
|
90
|
+
"type" => "paragraph",
|
|
91
|
+
"content" => [{
|
|
92
|
+
"type" => "text",
|
|
93
|
+
"text" => "This is ",
|
|
94
|
+
}, {
|
|
95
|
+
"type" => "text",
|
|
96
|
+
"text" => "bold",
|
|
97
|
+
"marks" => [{
|
|
98
|
+
"type" => "bold",
|
|
99
|
+
}],
|
|
100
|
+
}, {
|
|
101
|
+
"type" => "text",
|
|
102
|
+
"text" => " and ",
|
|
103
|
+
}, {
|
|
104
|
+
"type" => "text",
|
|
105
|
+
"text" => "italic",
|
|
106
|
+
"marks" => [{
|
|
107
|
+
"type" => "italic",
|
|
108
|
+
}],
|
|
109
|
+
}, {
|
|
110
|
+
"type" => "hard_break",
|
|
111
|
+
}, {
|
|
112
|
+
"type" => "text",
|
|
113
|
+
"text" => "with a line break",
|
|
114
|
+
}],
|
|
115
|
+
}, {
|
|
116
|
+
"type" => "paragraph",
|
|
117
|
+
"content" => [{
|
|
118
|
+
"type" => "text",
|
|
119
|
+
"text" => "Status: ",
|
|
120
|
+
"marks" => [{
|
|
121
|
+
"type" => "bold",
|
|
122
|
+
}],
|
|
123
|
+
}, {
|
|
124
|
+
"type" => "text",
|
|
125
|
+
"text" => "Done",
|
|
126
|
+
"marks" => [{
|
|
127
|
+
"type" => "strike",
|
|
128
|
+
}],
|
|
129
|
+
}, {
|
|
130
|
+
"type" => "text",
|
|
131
|
+
"text" => " → ",
|
|
132
|
+
}, {
|
|
133
|
+
"type" => "text",
|
|
134
|
+
"text" => "In Progress",
|
|
135
|
+
"marks" => [{
|
|
136
|
+
"type" => "italic",
|
|
137
|
+
}],
|
|
138
|
+
}],
|
|
139
|
+
}],
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
expect(cell.to_h).to eq(expected)
|
|
56
143
|
end
|
|
57
144
|
end
|
|
58
145
|
|
|
59
|
-
describe
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
146
|
+
describe "cell operations" do
|
|
147
|
+
let(:cell) do
|
|
148
|
+
c = described_class.create
|
|
149
|
+
c.add_paragraph("First paragraph")
|
|
150
|
+
c.add_paragraph("Second paragraph")
|
|
151
|
+
c.add_paragraph("Third paragraph")
|
|
152
|
+
c
|
|
66
153
|
end
|
|
67
154
|
|
|
68
|
-
|
|
69
|
-
cell
|
|
70
|
-
|
|
155
|
+
describe "#paragraphs" do
|
|
156
|
+
it "returns all paragraphs in the cell" do
|
|
157
|
+
expect(cell.paragraphs.size).to eq(3)
|
|
158
|
+
expect(cell.paragraphs).to all(be_a(Prosereflect::Paragraph))
|
|
159
|
+
expect(cell.paragraphs.map(&:text_content)).to eq([
|
|
160
|
+
"First paragraph",
|
|
161
|
+
"Second paragraph",
|
|
162
|
+
"Third paragraph",
|
|
163
|
+
])
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
it "returns empty array for cell with no paragraphs" do
|
|
167
|
+
empty_cell = described_class.create
|
|
168
|
+
expect(empty_cell.paragraphs).to eq([])
|
|
169
|
+
end
|
|
71
170
|
end
|
|
72
171
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
172
|
+
describe "#text_content" do
|
|
173
|
+
it "returns concatenated text from all paragraphs with newlines" do
|
|
174
|
+
expect(cell.text_content).to eq("First paragraph\nSecond paragraph\nThird paragraph")
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it "returns empty string for empty cell" do
|
|
178
|
+
empty_cell = described_class.create
|
|
179
|
+
expect(empty_cell.text_content).to eq("")
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
it "handles complex formatting and line breaks" do
|
|
183
|
+
cell = described_class.create
|
|
184
|
+
para = cell.add_paragraph("Line 1")
|
|
185
|
+
para.add_hard_break
|
|
186
|
+
para.add_text("Line 2", [Prosereflect::Mark::Bold.new])
|
|
187
|
+
para.add_hard_break
|
|
188
|
+
para.add_text("Line 3", [Prosereflect::Mark::Italic.new])
|
|
78
189
|
|
|
79
|
-
|
|
190
|
+
expect(cell.text_content).to eq("Line 1\nLine 2\nLine 3")
|
|
191
|
+
end
|
|
80
192
|
end
|
|
81
|
-
end
|
|
82
193
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
194
|
+
describe "#lines" do
|
|
195
|
+
it "splits text content into lines" do
|
|
196
|
+
expect(cell.lines).to eq([
|
|
197
|
+
"First paragraph",
|
|
198
|
+
"Second paragraph",
|
|
199
|
+
"Third paragraph",
|
|
200
|
+
])
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
it "returns empty array for empty cell" do
|
|
204
|
+
empty_cell = described_class.create
|
|
205
|
+
expect(empty_cell.lines).to eq([])
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
it "handles hard breaks within paragraphs" do
|
|
209
|
+
cell = described_class.create
|
|
210
|
+
para = cell.add_paragraph("First line")
|
|
211
|
+
para.add_hard_break
|
|
212
|
+
para.add_text("Second line")
|
|
213
|
+
cell.add_paragraph("Third line")
|
|
87
214
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
215
|
+
expect(cell.lines).to eq([
|
|
216
|
+
"First line",
|
|
217
|
+
"Second line",
|
|
218
|
+
"Third line",
|
|
219
|
+
])
|
|
220
|
+
end
|
|
91
221
|
end
|
|
92
222
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
223
|
+
describe "#add_paragraph" do
|
|
224
|
+
it "adds a paragraph with text and formatting" do
|
|
225
|
+
cell = described_class.create
|
|
226
|
+
para = cell.add_paragraph("Main text")
|
|
227
|
+
para.add_text(" (", [Prosereflect::Mark::Bold.new])
|
|
228
|
+
para.add_text("important", [Prosereflect::Mark::Bold.new, Prosereflect::Mark::Italic.new])
|
|
229
|
+
para.add_text(")", [Prosereflect::Mark::Bold.new])
|
|
230
|
+
|
|
231
|
+
expected = {
|
|
232
|
+
"type" => "table_cell",
|
|
233
|
+
"content" => [{
|
|
234
|
+
"type" => "paragraph",
|
|
235
|
+
"content" => [{
|
|
236
|
+
"type" => "text",
|
|
237
|
+
"text" => "Main text",
|
|
238
|
+
}, {
|
|
239
|
+
"type" => "text",
|
|
240
|
+
"text" => " (",
|
|
241
|
+
"marks" => [{
|
|
242
|
+
"type" => "bold",
|
|
243
|
+
}],
|
|
244
|
+
}, {
|
|
245
|
+
"type" => "text",
|
|
246
|
+
"text" => "important",
|
|
247
|
+
"marks" => [{
|
|
248
|
+
"type" => "bold",
|
|
249
|
+
}, {
|
|
250
|
+
"type" => "italic",
|
|
251
|
+
}],
|
|
252
|
+
}, {
|
|
253
|
+
"type" => "text",
|
|
254
|
+
"text" => ")",
|
|
255
|
+
"marks" => [{
|
|
256
|
+
"type" => "bold",
|
|
257
|
+
}],
|
|
258
|
+
}],
|
|
259
|
+
}],
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
expect(cell.to_h).to eq(expected)
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
it "adds multiple paragraphs with mixed content" do
|
|
266
|
+
cell = described_class.create
|
|
96
267
|
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
268
|
+
# First paragraph with hard break
|
|
269
|
+
para = cell.add_paragraph("Title")
|
|
270
|
+
para.add_hard_break
|
|
271
|
+
para.add_text("Subtitle", [Prosereflect::Mark::Italic.new])
|
|
272
|
+
|
|
273
|
+
# Second paragraph with mixed formatting
|
|
274
|
+
para = cell.add_paragraph
|
|
275
|
+
para.add_text("Note: ", [Prosereflect::Mark::Bold.new])
|
|
276
|
+
para.add_text("This is important")
|
|
277
|
+
|
|
278
|
+
expected = {
|
|
279
|
+
"type" => "table_cell",
|
|
280
|
+
"content" => [{
|
|
281
|
+
"type" => "paragraph",
|
|
282
|
+
"content" => [{
|
|
283
|
+
"type" => "text",
|
|
284
|
+
"text" => "Title",
|
|
285
|
+
}, {
|
|
286
|
+
"type" => "hard_break",
|
|
287
|
+
}, {
|
|
288
|
+
"type" => "text",
|
|
289
|
+
"text" => "Subtitle",
|
|
290
|
+
"marks" => [{
|
|
291
|
+
"type" => "italic",
|
|
292
|
+
}],
|
|
293
|
+
}],
|
|
294
|
+
}, {
|
|
295
|
+
"type" => "paragraph",
|
|
296
|
+
"content" => [{
|
|
297
|
+
"type" => "text",
|
|
298
|
+
"text" => "Note: ",
|
|
299
|
+
"marks" => [{
|
|
300
|
+
"type" => "bold",
|
|
301
|
+
}],
|
|
302
|
+
}, {
|
|
303
|
+
"type" => "text",
|
|
304
|
+
"text" => "This is important",
|
|
305
|
+
}],
|
|
306
|
+
}],
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
expect(cell.to_h).to eq(expected)
|
|
310
|
+
end
|
|
100
311
|
end
|
|
101
312
|
end
|
|
102
313
|
|
|
103
|
-
describe
|
|
104
|
-
it
|
|
105
|
-
cell = described_class.
|
|
106
|
-
cell.add_paragraph(
|
|
314
|
+
describe "serialization" do
|
|
315
|
+
it "converts to hash representation" do
|
|
316
|
+
cell = described_class.new
|
|
317
|
+
cell.add_paragraph("Test content")
|
|
107
318
|
|
|
108
319
|
hash = cell.to_h
|
|
109
|
-
expect(hash[
|
|
110
|
-
expect(hash[
|
|
111
|
-
expect(hash[
|
|
320
|
+
expect(hash["type"]).to eq("table_cell")
|
|
321
|
+
expect(hash["content"].size).to eq(1)
|
|
322
|
+
expect(hash["content"][0]["type"]).to eq("paragraph")
|
|
112
323
|
end
|
|
113
324
|
end
|
|
114
325
|
end
|