prosereflect 0.1.1 → 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 +141 -191
- data/CLAUDE.md +78 -0
- data/Gemfile +8 -4
- data/Rakefile +3 -3
- data/lib/prosereflect/attribute/base.rb +4 -6
- data/lib/prosereflect/attribute/bold.rb +2 -4
- data/lib/prosereflect/attribute/href.rb +1 -3
- data/lib/prosereflect/attribute/id.rb +7 -7
- data/lib/prosereflect/attribute.rb +4 -7
- data/lib/prosereflect/blockquote.rb +10 -11
- data/lib/prosereflect/bullet_list.rb +16 -15
- data/lib/prosereflect/code_block.rb +26 -26
- data/lib/prosereflect/code_block_wrapper.rb +12 -13
- data/lib/prosereflect/document.rb +14 -22
- data/lib/prosereflect/hard_break.rb +6 -6
- data/lib/prosereflect/heading.rb +14 -15
- data/lib/prosereflect/horizontal_rule.rb +14 -14
- data/lib/prosereflect/image.rb +23 -23
- data/lib/prosereflect/input/html.rb +83 -104
- data/lib/prosereflect/input.rb +7 -0
- data/lib/prosereflect/list_item.rb +11 -12
- data/lib/prosereflect/mark/base.rb +9 -11
- data/lib/prosereflect/mark/bold.rb +1 -3
- data/lib/prosereflect/mark/code.rb +1 -3
- data/lib/prosereflect/mark/italic.rb +1 -3
- data/lib/prosereflect/mark/link.rb +1 -3
- data/lib/prosereflect/mark/strike.rb +1 -3
- data/lib/prosereflect/mark/subscript.rb +1 -3
- data/lib/prosereflect/mark/superscript.rb +1 -3
- data/lib/prosereflect/mark/underline.rb +1 -3
- data/lib/prosereflect/mark.rb +9 -5
- data/lib/prosereflect/node.rb +31 -31
- data/lib/prosereflect/ordered_list.rb +15 -14
- data/lib/prosereflect/output/html.rb +52 -50
- data/lib/prosereflect/output.rb +7 -0
- data/lib/prosereflect/paragraph.rb +11 -13
- data/lib/prosereflect/parser.rb +47 -66
- data/lib/prosereflect/table.rb +12 -13
- data/lib/prosereflect/table_cell.rb +13 -13
- data/lib/prosereflect/table_header.rb +17 -17
- data/lib/prosereflect/table_row.rb +12 -12
- data/lib/prosereflect/text.rb +11 -11
- data/lib/prosereflect/user.rb +15 -15
- data/lib/prosereflect/version.rb +1 -1
- data/lib/prosereflect.rb +27 -17
- data/prosereflect.gemspec +17 -16
- data/spec/prosereflect/document_spec.rb +332 -330
- data/spec/prosereflect/hard_break_spec.rb +125 -125
- data/spec/prosereflect/input/html_spec.rb +522 -522
- data/spec/prosereflect/node_spec.rb +183 -182
- data/spec/prosereflect/output/html_spec.rb +105 -105
- data/spec/prosereflect/paragraph_spec.rb +275 -274
- data/spec/prosereflect/parser_spec.rb +185 -180
- data/spec/prosereflect/table_cell_spec.rb +183 -183
- data/spec/prosereflect/table_row_spec.rb +149 -149
- data/spec/prosereflect/table_spec.rb +320 -318
- data/spec/prosereflect/text_spec.rb +133 -132
- data/spec/prosereflect/user_spec.rb +31 -28
- data/spec/prosereflect_spec.rb +28 -26
- data/spec/spec_helper.rb +6 -6
- data/spec/support/matchers.rb +6 -6
- data/spec/support/shared_examples.rb +49 -49
- metadata +8 -5
- data/spec/prosereflect/version_spec.rb +0 -11
|
@@ -1,443 +1,445 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require
|
|
3
|
+
require "spec_helper"
|
|
4
4
|
|
|
5
5
|
RSpec.describe Prosereflect::Document do
|
|
6
|
-
let(:fixtures_path) { File.join(__dir__,
|
|
7
|
-
let(:yaml_file)
|
|
6
|
+
let(:fixtures_path) { File.join(__dir__, "..", "fixtures") }
|
|
7
|
+
let(:yaml_file) do
|
|
8
|
+
File.join(fixtures_path, "ituob-1000", "ituob-1000-DP.yaml")
|
|
9
|
+
end
|
|
8
10
|
let(:file_content) { File.read(yaml_file) }
|
|
9
11
|
let(:document) { Prosereflect::Parser.parse_document(YAML.safe_load(file_content)) }
|
|
10
12
|
|
|
11
|
-
describe
|
|
12
|
-
it
|
|
13
|
-
expect(document.type).to eq(
|
|
13
|
+
describe "basic properties" do
|
|
14
|
+
it "has the correct type" do
|
|
15
|
+
expect(document.type).to eq("doc")
|
|
14
16
|
end
|
|
15
17
|
|
|
16
|
-
it
|
|
18
|
+
it "is a node" do
|
|
17
19
|
expect(document).to be_a(Prosereflect::Node)
|
|
18
20
|
end
|
|
19
21
|
end
|
|
20
22
|
|
|
21
|
-
describe
|
|
22
|
-
it
|
|
23
|
+
describe ".create" do
|
|
24
|
+
it "creates a simple document with a paragraph" do
|
|
23
25
|
document = described_class.new
|
|
24
|
-
document.add_paragraph(
|
|
26
|
+
document.add_paragraph("This is a test paragraph.")
|
|
25
27
|
|
|
26
28
|
expected = {
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}]
|
|
34
|
-
}]
|
|
29
|
+
"type" => "doc",
|
|
30
|
+
"content" => [{
|
|
31
|
+
"type" => "paragraph",
|
|
32
|
+
"content" => [{
|
|
33
|
+
"type" => "text",
|
|
34
|
+
"text" => "This is a test paragraph.",
|
|
35
|
+
}],
|
|
36
|
+
}],
|
|
35
37
|
}
|
|
36
38
|
|
|
37
39
|
expect(document.to_h).to eq(expected)
|
|
38
40
|
end
|
|
39
41
|
|
|
40
|
-
it
|
|
42
|
+
it "creates a document with multiple paragraphs and styles" do
|
|
41
43
|
document = described_class.new
|
|
42
44
|
|
|
43
45
|
# Add a heading
|
|
44
46
|
heading = document.add_heading(1)
|
|
45
|
-
heading.add_text(
|
|
47
|
+
heading.add_text("Document Title")
|
|
46
48
|
|
|
47
49
|
# Add paragraphs with different styles
|
|
48
50
|
para1 = document.add_paragraph
|
|
49
|
-
para1.add_text(
|
|
50
|
-
para1.add_text(
|
|
51
|
-
para1.add_text(
|
|
52
|
-
para1.add_text(
|
|
53
|
-
para1.add_text(
|
|
51
|
+
para1.add_text("This is a paragraph with ")
|
|
52
|
+
para1.add_text("bold", [Prosereflect::Mark::Bold.new])
|
|
53
|
+
para1.add_text(" and ")
|
|
54
|
+
para1.add_text("italic", [Prosereflect::Mark::Italic.new])
|
|
55
|
+
para1.add_text(" text.")
|
|
54
56
|
|
|
55
57
|
para2 = document.add_paragraph
|
|
56
|
-
para2.add_text(
|
|
57
|
-
para2.add_text(
|
|
58
|
-
para2.add_text(
|
|
59
|
-
para2.add_text(
|
|
60
|
-
para2.add_text(
|
|
58
|
+
para2.add_text("This paragraph has ")
|
|
59
|
+
para2.add_text("underlined", [Prosereflect::Mark::Underline.new])
|
|
60
|
+
para2.add_text(" and ")
|
|
61
|
+
para2.add_text("struck through", [Prosereflect::Mark::Strike.new])
|
|
62
|
+
para2.add_text(" text.")
|
|
61
63
|
|
|
62
64
|
expected = {
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
}]
|
|
65
|
+
"type" => "doc",
|
|
66
|
+
"content" => [{
|
|
67
|
+
"type" => "heading",
|
|
68
|
+
"attrs" => { "level" => 1 },
|
|
69
|
+
"content" => [{
|
|
70
|
+
"type" => "text",
|
|
71
|
+
"text" => "Document Title",
|
|
72
|
+
}],
|
|
71
73
|
}, {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
"type" => "paragraph",
|
|
75
|
+
"content" => [{
|
|
76
|
+
"type" => "text",
|
|
77
|
+
"text" => "This is a paragraph with ",
|
|
76
78
|
}, {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
79
|
+
"type" => "text",
|
|
80
|
+
"text" => "bold",
|
|
81
|
+
"marks" => [{ "type" => "bold" }],
|
|
80
82
|
}, {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
"type" => "text",
|
|
84
|
+
"text" => " and ",
|
|
83
85
|
}, {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
86
|
+
"type" => "text",
|
|
87
|
+
"text" => "italic",
|
|
88
|
+
"marks" => [{ "type" => "italic" }],
|
|
87
89
|
}, {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
}]
|
|
90
|
+
"type" => "text",
|
|
91
|
+
"text" => " text.",
|
|
92
|
+
}],
|
|
91
93
|
}, {
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
94
|
+
"type" => "paragraph",
|
|
95
|
+
"content" => [{
|
|
96
|
+
"type" => "text",
|
|
97
|
+
"text" => "This paragraph has ",
|
|
96
98
|
}, {
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
"type" => "text",
|
|
100
|
+
"text" => "underlined",
|
|
101
|
+
"marks" => [{ "type" => "underline" }],
|
|
100
102
|
}, {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
"type" => "text",
|
|
104
|
+
"text" => " and ",
|
|
103
105
|
}, {
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
"type" => "text",
|
|
107
|
+
"text" => "struck through",
|
|
108
|
+
"marks" => [{ "type" => "strike" }],
|
|
107
109
|
}, {
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
}]
|
|
111
|
-
}]
|
|
110
|
+
"type" => "text",
|
|
111
|
+
"text" => " text.",
|
|
112
|
+
}],
|
|
113
|
+
}],
|
|
112
114
|
}
|
|
113
115
|
|
|
114
116
|
expect(document.to_h).to eq(expected)
|
|
115
117
|
end
|
|
116
118
|
|
|
117
|
-
it
|
|
119
|
+
it "creates a document with tables and lists" do
|
|
118
120
|
document = described_class.new
|
|
119
121
|
|
|
120
122
|
# Add a table
|
|
121
123
|
table = document.add_table
|
|
122
124
|
table.add_header(%w[Product Price Quantity])
|
|
123
|
-
table.add_row([
|
|
124
|
-
table.add_row([
|
|
125
|
+
table.add_row(["Widget", "$10.00", "5"])
|
|
126
|
+
table.add_row(["Gadget", "$15.00", "3"])
|
|
125
127
|
|
|
126
128
|
# Add an ordered list
|
|
127
129
|
list = document.add_ordered_list
|
|
128
130
|
list.start = 1
|
|
129
|
-
list.add_item(
|
|
130
|
-
list.add_item(
|
|
131
|
-
|
|
131
|
+
list.add_item("First item")
|
|
132
|
+
list.add_item("Second item with ")
|
|
133
|
+
.add_text("emphasis", [Prosereflect::Mark::Italic.new])
|
|
132
134
|
|
|
133
135
|
expected = {
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
}]
|
|
147
|
-
}]
|
|
136
|
+
"type" => "doc",
|
|
137
|
+
"content" => [{
|
|
138
|
+
"type" => "table",
|
|
139
|
+
"content" => [{
|
|
140
|
+
"type" => "table_row",
|
|
141
|
+
"content" => [{
|
|
142
|
+
"type" => "table_header",
|
|
143
|
+
"content" => [{
|
|
144
|
+
"type" => "paragraph",
|
|
145
|
+
"content" => [{
|
|
146
|
+
"type" => "text",
|
|
147
|
+
"text" => "Product",
|
|
148
|
+
}],
|
|
149
|
+
}],
|
|
148
150
|
}, {
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}]
|
|
156
|
-
}]
|
|
151
|
+
"type" => "table_header",
|
|
152
|
+
"content" => [{
|
|
153
|
+
"type" => "paragraph",
|
|
154
|
+
"content" => [{
|
|
155
|
+
"type" => "text",
|
|
156
|
+
"text" => "Price",
|
|
157
|
+
}],
|
|
158
|
+
}],
|
|
157
159
|
}, {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
}]
|
|
165
|
-
}]
|
|
166
|
-
}]
|
|
160
|
+
"type" => "table_header",
|
|
161
|
+
"content" => [{
|
|
162
|
+
"type" => "paragraph",
|
|
163
|
+
"content" => [{
|
|
164
|
+
"type" => "text",
|
|
165
|
+
"text" => "Quantity",
|
|
166
|
+
}],
|
|
167
|
+
}],
|
|
168
|
+
}],
|
|
167
169
|
}, {
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}]
|
|
177
|
-
}]
|
|
170
|
+
"type" => "table_row",
|
|
171
|
+
"content" => [{
|
|
172
|
+
"type" => "table_cell",
|
|
173
|
+
"content" => [{
|
|
174
|
+
"type" => "paragraph",
|
|
175
|
+
"content" => [{
|
|
176
|
+
"type" => "text",
|
|
177
|
+
"text" => "Widget",
|
|
178
|
+
}],
|
|
179
|
+
}],
|
|
178
180
|
}, {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
}]
|
|
186
|
-
}]
|
|
181
|
+
"type" => "table_cell",
|
|
182
|
+
"content" => [{
|
|
183
|
+
"type" => "paragraph",
|
|
184
|
+
"content" => [{
|
|
185
|
+
"type" => "text",
|
|
186
|
+
"text" => "$10.00",
|
|
187
|
+
}],
|
|
188
|
+
}],
|
|
187
189
|
}, {
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
}]
|
|
195
|
-
}]
|
|
196
|
-
}]
|
|
190
|
+
"type" => "table_cell",
|
|
191
|
+
"content" => [{
|
|
192
|
+
"type" => "paragraph",
|
|
193
|
+
"content" => [{
|
|
194
|
+
"type" => "text",
|
|
195
|
+
"text" => "5",
|
|
196
|
+
}],
|
|
197
|
+
}],
|
|
198
|
+
}],
|
|
197
199
|
}, {
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
}]
|
|
207
|
-
}]
|
|
200
|
+
"type" => "table_row",
|
|
201
|
+
"content" => [{
|
|
202
|
+
"type" => "table_cell",
|
|
203
|
+
"content" => [{
|
|
204
|
+
"type" => "paragraph",
|
|
205
|
+
"content" => [{
|
|
206
|
+
"type" => "text",
|
|
207
|
+
"text" => "Gadget",
|
|
208
|
+
}],
|
|
209
|
+
}],
|
|
208
210
|
}, {
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
}]
|
|
216
|
-
}]
|
|
211
|
+
"type" => "table_cell",
|
|
212
|
+
"content" => [{
|
|
213
|
+
"type" => "paragraph",
|
|
214
|
+
"content" => [{
|
|
215
|
+
"type" => "text",
|
|
216
|
+
"text" => "$15.00",
|
|
217
|
+
}],
|
|
218
|
+
}],
|
|
217
219
|
}, {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
}]
|
|
225
|
-
}]
|
|
226
|
-
}]
|
|
227
|
-
}]
|
|
220
|
+
"type" => "table_cell",
|
|
221
|
+
"content" => [{
|
|
222
|
+
"type" => "paragraph",
|
|
223
|
+
"content" => [{
|
|
224
|
+
"type" => "text",
|
|
225
|
+
"text" => "3",
|
|
226
|
+
}],
|
|
227
|
+
}],
|
|
228
|
+
}],
|
|
229
|
+
}],
|
|
228
230
|
}, {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
}]
|
|
239
|
-
}]
|
|
231
|
+
"type" => "ordered_list",
|
|
232
|
+
"attrs" => { "start" => 1 },
|
|
233
|
+
"content" => [{
|
|
234
|
+
"type" => "list_item",
|
|
235
|
+
"content" => [{
|
|
236
|
+
"type" => "paragraph",
|
|
237
|
+
"content" => [{
|
|
238
|
+
"type" => "text",
|
|
239
|
+
"text" => "First item",
|
|
240
|
+
}],
|
|
241
|
+
}],
|
|
240
242
|
}, {
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
243
|
+
"type" => "list_item",
|
|
244
|
+
"content" => [{
|
|
245
|
+
"type" => "paragraph",
|
|
246
|
+
"content" => [{
|
|
247
|
+
"type" => "text",
|
|
248
|
+
"text" => "Second item with ",
|
|
247
249
|
}, {
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
}]
|
|
252
|
-
}]
|
|
253
|
-
}]
|
|
254
|
-
}]
|
|
250
|
+
"type" => "text",
|
|
251
|
+
"text" => "emphasis",
|
|
252
|
+
"marks" => [{ "type" => "italic" }],
|
|
253
|
+
}],
|
|
254
|
+
}],
|
|
255
|
+
}],
|
|
256
|
+
}],
|
|
255
257
|
}
|
|
256
258
|
|
|
257
259
|
expect(document.to_h).to eq(expected)
|
|
258
260
|
end
|
|
259
261
|
|
|
260
|
-
it
|
|
262
|
+
it "creates a document with code blocks and blockquotes" do
|
|
261
263
|
document = described_class.create
|
|
262
264
|
wrapper = document.add_code_block_wrapper
|
|
263
265
|
wrapper.line_numbers = true
|
|
264
266
|
|
|
265
267
|
code_block = wrapper.add_code_block
|
|
266
|
-
code_block.language =
|
|
268
|
+
code_block.language = "ruby"
|
|
267
269
|
code_block.content = "def example\n puts 'Hello'\nend"
|
|
268
270
|
|
|
269
271
|
quote = document.add_blockquote
|
|
270
|
-
quote.citation =
|
|
271
|
-
quote.add_paragraph(
|
|
272
|
+
quote.citation = "https://example.com"
|
|
273
|
+
quote.add_paragraph("A test quote")
|
|
272
274
|
|
|
273
275
|
expected = {
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
276
|
+
"type" => "doc",
|
|
277
|
+
"content" => [{
|
|
278
|
+
"type" => "code_block_wrapper",
|
|
279
|
+
"attrs" => {
|
|
280
|
+
"line_numbers" => true,
|
|
279
281
|
},
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
}
|
|
286
|
-
}]
|
|
282
|
+
"content" => [{
|
|
283
|
+
"type" => "code_block",
|
|
284
|
+
"attrs" => {
|
|
285
|
+
"content" => "def example\n puts 'Hello'\nend",
|
|
286
|
+
"language" => "ruby",
|
|
287
|
+
},
|
|
288
|
+
}],
|
|
287
289
|
}, {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
290
|
+
"type" => "blockquote",
|
|
291
|
+
"attrs" => {
|
|
292
|
+
"citation" => "https://example.com",
|
|
291
293
|
},
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
}]
|
|
298
|
-
}]
|
|
299
|
-
}]
|
|
294
|
+
"content" => [{
|
|
295
|
+
"type" => "paragraph",
|
|
296
|
+
"content" => [{
|
|
297
|
+
"type" => "text",
|
|
298
|
+
"text" => "A test quote",
|
|
299
|
+
}],
|
|
300
|
+
}],
|
|
301
|
+
}],
|
|
300
302
|
}
|
|
301
303
|
|
|
302
304
|
expect(document.to_h).to eq(expected)
|
|
303
305
|
end
|
|
304
306
|
|
|
305
|
-
it
|
|
307
|
+
it "creates a document with mathematical content" do
|
|
306
308
|
document = described_class.new
|
|
307
309
|
|
|
308
310
|
# Add a heading
|
|
309
311
|
heading = document.add_heading(2)
|
|
310
|
-
heading.add_text(
|
|
312
|
+
heading.add_text("Mathematical Expressions")
|
|
311
313
|
|
|
312
314
|
# Add equations
|
|
313
315
|
para1 = document.add_paragraph
|
|
314
|
-
para1.add_text(
|
|
315
|
-
para1.add_text(
|
|
316
|
-
para1.add_text(
|
|
317
|
-
para1.add_text(
|
|
318
|
-
para1.add_text(
|
|
316
|
+
para1.add_text("The quadratic formula: x = -b ± ")
|
|
317
|
+
para1.add_text("√", [Prosereflect::Mark::Bold.new])
|
|
318
|
+
para1.add_text("(b")
|
|
319
|
+
para1.add_text("2", [Prosereflect::Mark::Superscript.new])
|
|
320
|
+
para1.add_text(" - 4ac) / 2a")
|
|
319
321
|
|
|
320
322
|
para2 = document.add_paragraph
|
|
321
|
-
para2.add_text(
|
|
322
|
-
para2.add_text(
|
|
323
|
-
para2.add_text(
|
|
323
|
+
para2.add_text("Water molecule: H")
|
|
324
|
+
para2.add_text("2", [Prosereflect::Mark::Subscript.new])
|
|
325
|
+
para2.add_text("O")
|
|
324
326
|
|
|
325
327
|
expected = {
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
}]
|
|
328
|
+
"type" => "doc",
|
|
329
|
+
"content" => [{
|
|
330
|
+
"type" => "heading",
|
|
331
|
+
"attrs" => { "level" => 2 },
|
|
332
|
+
"content" => [{
|
|
333
|
+
"type" => "text",
|
|
334
|
+
"text" => "Mathematical Expressions",
|
|
335
|
+
}],
|
|
334
336
|
}, {
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
337
|
+
"type" => "paragraph",
|
|
338
|
+
"content" => [{
|
|
339
|
+
"type" => "text",
|
|
340
|
+
"text" => "The quadratic formula: x = -b ± ",
|
|
339
341
|
}, {
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
342
|
+
"type" => "text",
|
|
343
|
+
"text" => "√",
|
|
344
|
+
"marks" => [{ "type" => "bold" }],
|
|
343
345
|
}, {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
+
"type" => "text",
|
|
347
|
+
"text" => "(b",
|
|
346
348
|
}, {
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
349
|
+
"type" => "text",
|
|
350
|
+
"text" => "2",
|
|
351
|
+
"marks" => [{ "type" => "superscript" }],
|
|
350
352
|
}, {
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
}]
|
|
353
|
+
"type" => "text",
|
|
354
|
+
"text" => " - 4ac) / 2a",
|
|
355
|
+
}],
|
|
354
356
|
}, {
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
357
|
+
"type" => "paragraph",
|
|
358
|
+
"content" => [{
|
|
359
|
+
"type" => "text",
|
|
360
|
+
"text" => "Water molecule: H",
|
|
359
361
|
}, {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
362
|
+
"type" => "text",
|
|
363
|
+
"text" => "2",
|
|
364
|
+
"marks" => [{ "type" => "subscript" }],
|
|
363
365
|
}, {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
}]
|
|
367
|
-
}]
|
|
366
|
+
"type" => "text",
|
|
367
|
+
"text" => "O",
|
|
368
|
+
}],
|
|
369
|
+
}],
|
|
368
370
|
}
|
|
369
371
|
|
|
370
372
|
expect(document.to_h).to eq(expected)
|
|
371
373
|
end
|
|
372
374
|
|
|
373
|
-
it
|
|
375
|
+
it "creates a document with images and links" do
|
|
374
376
|
document = described_class.new
|
|
375
377
|
|
|
376
378
|
# Add an image
|
|
377
|
-
image = document.add_image(
|
|
378
|
-
image.title =
|
|
379
|
+
image = document.add_image("example.jpg", "Example image")
|
|
380
|
+
image.title = "A beautiful landscape"
|
|
379
381
|
image.width = 800
|
|
380
382
|
image.height = 600
|
|
381
383
|
|
|
382
384
|
# Add a paragraph with links
|
|
383
385
|
para = document.add_paragraph
|
|
384
|
-
para.add_text(
|
|
385
|
-
link_text = Prosereflect::Text.new(text:
|
|
386
|
+
para.add_text("Visit our ")
|
|
387
|
+
link_text = Prosereflect::Text.new(text: "website")
|
|
386
388
|
link_mark = Prosereflect::Mark::Link.new
|
|
387
|
-
link_mark.attrs = {
|
|
389
|
+
link_mark.attrs = { "href" => "https://example.com" }
|
|
388
390
|
link_text.marks = [link_mark]
|
|
389
391
|
para.add_child(link_text)
|
|
390
|
-
para.add_text(
|
|
392
|
+
para.add_text(" for more information.")
|
|
391
393
|
|
|
392
394
|
expected = {
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
}
|
|
395
|
+
"type" => "doc",
|
|
396
|
+
"content" => [{
|
|
397
|
+
"type" => "image",
|
|
398
|
+
"attrs" => {
|
|
399
|
+
"src" => "example.jpg",
|
|
400
|
+
"alt" => "Example image",
|
|
401
|
+
"title" => "A beautiful landscape",
|
|
402
|
+
"width" => 800,
|
|
403
|
+
"height" => 600,
|
|
404
|
+
},
|
|
403
405
|
}, {
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
406
|
+
"type" => "paragraph",
|
|
407
|
+
"content" => [{
|
|
408
|
+
"type" => "text",
|
|
409
|
+
"text" => "Visit our ",
|
|
408
410
|
}, {
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
}
|
|
416
|
-
}]
|
|
411
|
+
"type" => "text",
|
|
412
|
+
"text" => "website",
|
|
413
|
+
"marks" => [{
|
|
414
|
+
"type" => "link",
|
|
415
|
+
"attrs" => {
|
|
416
|
+
"href" => "https://example.com",
|
|
417
|
+
},
|
|
418
|
+
}],
|
|
417
419
|
}, {
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
}]
|
|
421
|
-
}]
|
|
420
|
+
"type" => "text",
|
|
421
|
+
"text" => " for more information.",
|
|
422
|
+
}],
|
|
423
|
+
}],
|
|
422
424
|
}
|
|
423
425
|
|
|
424
426
|
expect(document.to_h).to eq(expected)
|
|
425
427
|
end
|
|
426
428
|
end
|
|
427
429
|
|
|
428
|
-
describe
|
|
429
|
-
it
|
|
430
|
+
describe "#paragraphs" do
|
|
431
|
+
it "returns all paragraphs in the document" do
|
|
430
432
|
doc = described_class.new
|
|
431
|
-
doc.add_paragraph(
|
|
432
|
-
doc.add_paragraph(
|
|
433
|
+
doc.add_paragraph("First paragraph")
|
|
434
|
+
doc.add_paragraph("Second paragraph")
|
|
433
435
|
|
|
434
436
|
expect(doc.paragraphs.size).to eq(2)
|
|
435
437
|
expect(doc.paragraphs).to all(be_a(Prosereflect::Paragraph))
|
|
436
438
|
end
|
|
437
439
|
end
|
|
438
440
|
|
|
439
|
-
describe
|
|
440
|
-
it
|
|
441
|
+
describe "#tables" do
|
|
442
|
+
it "returns all tables in the document" do
|
|
441
443
|
doc = described_class.new
|
|
442
444
|
doc.add_table
|
|
443
445
|
doc.add_table
|
|
@@ -447,28 +449,28 @@ RSpec.describe Prosereflect::Document do
|
|
|
447
449
|
end
|
|
448
450
|
end
|
|
449
451
|
|
|
450
|
-
describe
|
|
451
|
-
it
|
|
452
|
+
describe "#add_paragraph" do
|
|
453
|
+
it "adds a paragraph with text" do
|
|
452
454
|
doc = described_class.new
|
|
453
|
-
para = doc.add_paragraph(
|
|
455
|
+
para = doc.add_paragraph("Test paragraph")
|
|
454
456
|
|
|
455
457
|
expect(doc.paragraphs.size).to eq(1)
|
|
456
458
|
expect(para).to be_a(Prosereflect::Paragraph)
|
|
457
|
-
expect(para.text_content).to eq(
|
|
459
|
+
expect(para.text_content).to eq("Test paragraph")
|
|
458
460
|
end
|
|
459
461
|
|
|
460
|
-
it
|
|
462
|
+
it "adds an empty paragraph" do
|
|
461
463
|
doc = described_class.new
|
|
462
464
|
para = doc.add_paragraph
|
|
463
465
|
|
|
464
466
|
expect(doc.paragraphs.size).to eq(1)
|
|
465
467
|
expect(para).to be_a(Prosereflect::Paragraph)
|
|
466
|
-
expect(para.text_content).to eq(
|
|
468
|
+
expect(para.text_content).to eq("")
|
|
467
469
|
end
|
|
468
470
|
end
|
|
469
471
|
|
|
470
|
-
describe
|
|
471
|
-
it
|
|
472
|
+
describe "#add_table" do
|
|
473
|
+
it "adds a table to the document" do
|
|
472
474
|
doc = described_class.new
|
|
473
475
|
table = doc.add_table
|
|
474
476
|
|
|
@@ -476,43 +478,43 @@ RSpec.describe Prosereflect::Document do
|
|
|
476
478
|
expect(table).to be_a(Prosereflect::Table)
|
|
477
479
|
end
|
|
478
480
|
|
|
479
|
-
it
|
|
481
|
+
it "adds a table with attributes" do
|
|
480
482
|
doc = described_class.new
|
|
481
|
-
attrs = {
|
|
483
|
+
attrs = { "width" => "100%" }
|
|
482
484
|
table = doc.add_table(attrs)
|
|
483
485
|
|
|
484
486
|
expect(table.attrs).to eq(attrs)
|
|
485
487
|
end
|
|
486
488
|
end
|
|
487
489
|
|
|
488
|
-
describe
|
|
489
|
-
it
|
|
490
|
+
describe "serialization" do
|
|
491
|
+
it "converts to hash representation" do
|
|
490
492
|
doc = described_class.new
|
|
491
|
-
doc.add_paragraph(
|
|
493
|
+
doc.add_paragraph("Test paragraph")
|
|
492
494
|
table = doc.add_table
|
|
493
|
-
table.add_header([
|
|
494
|
-
table.add_row([
|
|
495
|
+
table.add_header(["Header"])
|
|
496
|
+
table.add_row(["Data"])
|
|
495
497
|
|
|
496
498
|
hash = doc.to_h
|
|
497
|
-
expect(hash[
|
|
498
|
-
expect(hash[
|
|
499
|
-
expect(hash[
|
|
500
|
-
expect(hash[
|
|
499
|
+
expect(hash["type"]).to eq("doc")
|
|
500
|
+
expect(hash["content"].size).to eq(2)
|
|
501
|
+
expect(hash["content"][0]["type"]).to eq("paragraph")
|
|
502
|
+
expect(hash["content"][1]["type"]).to eq("table")
|
|
501
503
|
end
|
|
502
504
|
|
|
503
|
-
it
|
|
505
|
+
it "converts to YAML" do
|
|
504
506
|
doc = described_class.new
|
|
505
|
-
doc.add_paragraph(
|
|
507
|
+
doc.add_paragraph("Test paragraph")
|
|
506
508
|
|
|
507
509
|
yaml = doc.to_yaml
|
|
508
510
|
expect(yaml).to be_a(String)
|
|
509
|
-
expect(yaml).to include(
|
|
510
|
-
expect(yaml).to include(
|
|
511
|
+
expect(yaml).to include("type: doc")
|
|
512
|
+
expect(yaml).to include("type: paragraph")
|
|
511
513
|
end
|
|
512
514
|
|
|
513
|
-
it
|
|
515
|
+
it "converts to JSON" do
|
|
514
516
|
doc = described_class.new
|
|
515
|
-
doc.add_paragraph(
|
|
517
|
+
doc.add_paragraph("Test paragraph")
|
|
516
518
|
|
|
517
519
|
json = doc.to_json
|
|
518
520
|
expect(json).to be_a(String)
|
|
@@ -521,29 +523,29 @@ RSpec.describe Prosereflect::Document do
|
|
|
521
523
|
end
|
|
522
524
|
end
|
|
523
525
|
|
|
524
|
-
describe
|
|
525
|
-
it
|
|
526
|
+
describe "#text_content" do
|
|
527
|
+
it "returns plain text content from all nodes" do
|
|
526
528
|
document = described_class.new
|
|
527
529
|
|
|
528
530
|
# Add various types of content
|
|
529
|
-
document.add_heading(1).add_text(
|
|
531
|
+
document.add_heading(1).add_text("Title")
|
|
530
532
|
|
|
531
533
|
para = document.add_paragraph
|
|
532
|
-
para.add_text(
|
|
533
|
-
para.add_text(
|
|
534
|
-
para.add_text(
|
|
534
|
+
para.add_text("This is ")
|
|
535
|
+
para.add_text("bold", [Prosereflect::Mark::Bold.new])
|
|
536
|
+
para.add_text(" text.")
|
|
535
537
|
|
|
536
538
|
list = document.add_bullet_list
|
|
537
|
-
list.add_item(
|
|
538
|
-
list.add_item(
|
|
539
|
+
list.add_item("First item")
|
|
540
|
+
list.add_item("Second item")
|
|
539
541
|
|
|
540
542
|
expected_text = "Title\nThis is bold text.\nFirst item\nSecond item"
|
|
541
543
|
expect(document.text_content).to eq(expected_text)
|
|
542
544
|
end
|
|
543
545
|
|
|
544
|
-
it
|
|
546
|
+
it "handles empty documents" do
|
|
545
547
|
document = described_class.new
|
|
546
|
-
expect(document.text_content).to eq(
|
|
548
|
+
expect(document.text_content).to eq("")
|
|
547
549
|
end
|
|
548
550
|
end
|
|
549
551
|
end
|