prosereflect 0.1.1 → 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.
Files changed (158) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/docs.yml +63 -0
  3. data/.github/workflows/links.yml +97 -0
  4. data/.github/workflows/rake.yml +4 -0
  5. data/.github/workflows/release.yml +5 -0
  6. data/.gitignore +4 -0
  7. data/.rubocop.yml +19 -1
  8. data/.rubocop_todo.yml +119 -183
  9. data/CLAUDE.md +78 -0
  10. data/Gemfile +8 -4
  11. data/README.adoc +2 -0
  12. data/Rakefile +3 -3
  13. data/docs/Gemfile +10 -0
  14. data/docs/INDEX.adoc +45 -0
  15. data/docs/_advanced/index.adoc +15 -0
  16. data/docs/_advanced/schema.adoc +112 -0
  17. data/docs/_advanced/step-map.adoc +66 -0
  18. data/docs/_advanced/steps.adoc +88 -0
  19. data/docs/_advanced/test-builder.adoc +61 -0
  20. data/docs/_advanced/transform.adoc +92 -0
  21. data/docs/_config.yml +174 -0
  22. data/docs/_features/html-input.adoc +69 -0
  23. data/docs/_features/html-output.adoc +45 -0
  24. data/docs/_features/index.adoc +15 -0
  25. data/docs/_features/marks.adoc +86 -0
  26. data/docs/_features/node-types.adoc +124 -0
  27. data/docs/_features/user-mentions.adoc +47 -0
  28. data/docs/_guides/custom-nodes.adoc +107 -0
  29. data/docs/_guides/index.adoc +13 -0
  30. data/docs/_guides/round-trip-html.adoc +91 -0
  31. data/docs/_guides/serialization.adoc +109 -0
  32. data/docs/_pages/index.adoc +67 -0
  33. data/docs/_reference/document-api.adoc +49 -0
  34. data/docs/_reference/index.adoc +14 -0
  35. data/docs/_reference/node-api.adoc +79 -0
  36. data/docs/_reference/schema-api.adoc +95 -0
  37. data/docs/_reference/transform-api.adoc +77 -0
  38. data/docs/_understanding/document-model.adoc +65 -0
  39. data/docs/_understanding/fragment.adoc +52 -0
  40. data/docs/_understanding/index.adoc +14 -0
  41. data/docs/_understanding/resolved-position.adoc +53 -0
  42. data/docs/_understanding/slice.adoc +54 -0
  43. data/docs/lychee.toml +63 -0
  44. data/lib/prosereflect/attribute/base.rb +4 -6
  45. data/lib/prosereflect/attribute/bold.rb +2 -4
  46. data/lib/prosereflect/attribute/href.rb +1 -3
  47. data/lib/prosereflect/attribute/id.rb +7 -7
  48. data/lib/prosereflect/attribute.rb +4 -7
  49. data/lib/prosereflect/blockquote.rb +19 -11
  50. data/lib/prosereflect/bullet_list.rb +36 -29
  51. data/lib/prosereflect/code_block.rb +23 -27
  52. data/lib/prosereflect/code_block_wrapper.rb +12 -13
  53. data/lib/prosereflect/document.rb +14 -22
  54. data/lib/prosereflect/fragment.rb +249 -0
  55. data/lib/prosereflect/hard_break.rb +6 -6
  56. data/lib/prosereflect/heading.rb +14 -15
  57. data/lib/prosereflect/horizontal_rule.rb +23 -14
  58. data/lib/prosereflect/image.rb +32 -23
  59. data/lib/prosereflect/input/html.rb +179 -104
  60. data/lib/prosereflect/input.rb +7 -0
  61. data/lib/prosereflect/list_item.rb +11 -12
  62. data/lib/prosereflect/mark/base.rb +9 -11
  63. data/lib/prosereflect/mark/bold.rb +1 -3
  64. data/lib/prosereflect/mark/code.rb +1 -3
  65. data/lib/prosereflect/mark/italic.rb +1 -3
  66. data/lib/prosereflect/mark/link.rb +1 -3
  67. data/lib/prosereflect/mark/strike.rb +1 -3
  68. data/lib/prosereflect/mark/subscript.rb +1 -3
  69. data/lib/prosereflect/mark/superscript.rb +1 -3
  70. data/lib/prosereflect/mark/underline.rb +1 -3
  71. data/lib/prosereflect/mark.rb +9 -5
  72. data/lib/prosereflect/node.rb +171 -33
  73. data/lib/prosereflect/ordered_list.rb +17 -14
  74. data/lib/prosereflect/output/html.rb +279 -50
  75. data/lib/prosereflect/output.rb +7 -0
  76. data/lib/prosereflect/paragraph.rb +11 -13
  77. data/lib/prosereflect/parser.rb +56 -66
  78. data/lib/prosereflect/resolved_pos.rb +256 -0
  79. data/lib/prosereflect/schema/attribute.rb +57 -0
  80. data/lib/prosereflect/schema/content_match.rb +656 -0
  81. data/lib/prosereflect/schema/fragment.rb +166 -0
  82. data/lib/prosereflect/schema/mark.rb +121 -0
  83. data/lib/prosereflect/schema/mark_type.rb +130 -0
  84. data/lib/prosereflect/schema/node.rb +236 -0
  85. data/lib/prosereflect/schema/node_type.rb +274 -0
  86. data/lib/prosereflect/schema/schema_main.rb +190 -0
  87. data/lib/prosereflect/schema/spec.rb +92 -0
  88. data/lib/prosereflect/schema.rb +39 -0
  89. data/lib/prosereflect/table.rb +12 -13
  90. data/lib/prosereflect/table_cell.rb +13 -13
  91. data/lib/prosereflect/table_header.rb +17 -17
  92. data/lib/prosereflect/table_row.rb +12 -12
  93. data/lib/prosereflect/text.rb +35 -11
  94. data/lib/prosereflect/transform/attr_step.rb +157 -0
  95. data/lib/prosereflect/transform/insert_step.rb +115 -0
  96. data/lib/prosereflect/transform/mapping.rb +82 -0
  97. data/lib/prosereflect/transform/mark_step.rb +269 -0
  98. data/lib/prosereflect/transform/replace_around_step.rb +181 -0
  99. data/lib/prosereflect/transform/replace_step.rb +157 -0
  100. data/lib/prosereflect/transform/slice.rb +91 -0
  101. data/lib/prosereflect/transform/step.rb +89 -0
  102. data/lib/prosereflect/transform/step_map.rb +126 -0
  103. data/lib/prosereflect/transform/structure.rb +120 -0
  104. data/lib/prosereflect/transform/transform.rb +341 -0
  105. data/lib/prosereflect/transform.rb +26 -0
  106. data/lib/prosereflect/user.rb +15 -15
  107. data/lib/prosereflect/version.rb +1 -1
  108. data/lib/prosereflect.rb +30 -17
  109. data/prosereflect.gemspec +17 -16
  110. data/spec/fixtures/documents/formatted_text.yaml +14 -0
  111. data/spec/fixtures/documents/heading_paragraph.yaml +16 -0
  112. data/spec/fixtures/documents/lists_doc.yaml +32 -0
  113. data/spec/fixtures/documents/mixed_content.yaml +40 -0
  114. data/spec/fixtures/documents/nested_doc.yaml +20 -0
  115. data/spec/fixtures/documents/simple_doc.yaml +6 -0
  116. data/spec/fixtures/documents/table_doc.yaml +32 -0
  117. data/spec/fixtures/documents/transform_test.yaml +14 -0
  118. data/spec/fixtures/schema/custom_schema.rb +37 -0
  119. data/spec/fixtures/schema/test_schema.rb +46 -0
  120. data/spec/fixtures/test_builder/helpers.rb +212 -0
  121. data/spec/prosereflect/document_spec.rb +332 -330
  122. data/spec/prosereflect/fragment_spec.rb +273 -0
  123. data/spec/prosereflect/hard_break_spec.rb +125 -125
  124. data/spec/prosereflect/input/html_spec.rb +718 -522
  125. data/spec/prosereflect/node_spec.rb +311 -182
  126. data/spec/prosereflect/output/html_spec.rb +105 -105
  127. data/spec/prosereflect/output/whitespace_spec.rb +248 -0
  128. data/spec/prosereflect/paragraph_spec.rb +275 -274
  129. data/spec/prosereflect/parser/round_trip_spec.rb +472 -0
  130. data/spec/prosereflect/parser_spec.rb +185 -180
  131. data/spec/prosereflect/resolved_pos_spec.rb +74 -0
  132. data/spec/prosereflect/schema/conftest.rb +68 -0
  133. data/spec/prosereflect/schema/content_match_spec.rb +237 -0
  134. data/spec/prosereflect/schema/mark_spec.rb +274 -0
  135. data/spec/prosereflect/schema/mark_type_spec.rb +86 -0
  136. data/spec/prosereflect/schema/node_type_spec.rb +142 -0
  137. data/spec/prosereflect/schema/schema_spec.rb +194 -0
  138. data/spec/prosereflect/table_cell_spec.rb +183 -183
  139. data/spec/prosereflect/table_row_spec.rb +149 -149
  140. data/spec/prosereflect/table_spec.rb +320 -318
  141. data/spec/prosereflect/test_builder/marks_spec.rb +127 -0
  142. data/spec/prosereflect/text_spec.rb +133 -132
  143. data/spec/prosereflect/transform/equivalence_spec.rb +487 -0
  144. data/spec/prosereflect/transform/mapping_spec.rb +226 -0
  145. data/spec/prosereflect/transform/replace_spec.rb +832 -0
  146. data/spec/prosereflect/transform/replace_step_spec.rb +157 -0
  147. data/spec/prosereflect/transform/slice_spec.rb +48 -0
  148. data/spec/prosereflect/transform/step_map_spec.rb +70 -0
  149. data/spec/prosereflect/transform/step_spec.rb +211 -0
  150. data/spec/prosereflect/transform/structure_spec.rb +98 -0
  151. data/spec/prosereflect/transform/transform_spec.rb +238 -0
  152. data/spec/prosereflect/user_spec.rb +31 -28
  153. data/spec/prosereflect_spec.rb +28 -26
  154. data/spec/spec_helper.rb +7 -6
  155. data/spec/support/matchers.rb +6 -6
  156. data/spec/support/shared_examples.rb +49 -49
  157. metadata +96 -5
  158. data/spec/prosereflect/version_spec.rb +0 -11
@@ -1,260 +1,260 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'spec_helper'
3
+ require "spec_helper"
4
4
 
5
5
  RSpec.describe Prosereflect::Table do
6
- describe 'initialization' do
7
- it 'initializes as a table node' do
8
- table = described_class.new({ 'type' => 'table' })
9
- expect(table.type).to eq('table')
6
+ describe "initialization" do
7
+ it "initializes as a table node" do
8
+ table = described_class.new({ "type" => "table" })
9
+ expect(table.type).to eq("table")
10
10
  end
11
11
  end
12
12
 
13
- describe '.create' do
14
- it 'creates an empty table' do
13
+ describe ".create" do
14
+ it "creates an empty table" do
15
15
  table = described_class.create
16
16
 
17
17
  expected = {
18
- 'type' => 'table',
19
- 'content' => []
18
+ "type" => "table",
19
+ "content" => [],
20
20
  }
21
21
 
22
22
  expect(table.to_h).to eq(expected)
23
23
  end
24
24
 
25
- it 'creates a table with attributes' do
25
+ it "creates a table with attributes" do
26
26
  table = described_class.create(attrs: {
27
- 'width' => '100%',
28
- 'alignment' => 'center',
29
- 'border' => '1'
27
+ "width" => "100%",
28
+ "alignment" => "center",
29
+ "border" => "1",
30
30
  })
31
31
 
32
32
  expected = {
33
- 'type' => 'table',
34
- 'attrs' => {
35
- 'width' => '100%',
36
- 'alignment' => 'center',
37
- 'border' => '1'
33
+ "type" => "table",
34
+ "attrs" => {
35
+ "width" => "100%",
36
+ "alignment" => "center",
37
+ "border" => "1",
38
38
  },
39
- 'content' => []
39
+ "content" => [],
40
40
  }
41
41
 
42
42
  expect(table.to_h).to eq(expected)
43
43
  end
44
44
  end
45
45
 
46
- describe 'table structure' do
47
- it 'creates a simple table with header and data' do
46
+ describe "table structure" do
47
+ it "creates a simple table with header and data" do
48
48
  table = described_class.create
49
49
  table.add_header(%w[Name Age City])
50
- table.add_row(['John', '25', 'New York'])
50
+ table.add_row(["John", "25", "New York"])
51
51
  table.add_row(%w[Alice 30 London])
52
52
 
53
53
  expected = {
54
- 'type' => 'table',
55
- 'content' => [{
56
- 'type' => 'table_row',
57
- 'content' => [{
58
- 'type' => 'table_header',
59
- 'content' => [{
60
- 'type' => 'paragraph',
61
- 'content' => [{
62
- 'type' => 'text',
63
- 'text' => 'Name'
64
- }]
65
- }]
54
+ "type" => "table",
55
+ "content" => [{
56
+ "type" => "table_row",
57
+ "content" => [{
58
+ "type" => "table_header",
59
+ "content" => [{
60
+ "type" => "paragraph",
61
+ "content" => [{
62
+ "type" => "text",
63
+ "text" => "Name",
64
+ }],
65
+ }],
66
66
  }, {
67
- 'type' => 'table_header',
68
- 'content' => [{
69
- 'type' => 'paragraph',
70
- 'content' => [{
71
- 'type' => 'text',
72
- 'text' => 'Age'
73
- }]
74
- }]
67
+ "type" => "table_header",
68
+ "content" => [{
69
+ "type" => "paragraph",
70
+ "content" => [{
71
+ "type" => "text",
72
+ "text" => "Age",
73
+ }],
74
+ }],
75
75
  }, {
76
- 'type' => 'table_header',
77
- 'content' => [{
78
- 'type' => 'paragraph',
79
- 'content' => [{
80
- 'type' => 'text',
81
- 'text' => 'City'
82
- }]
83
- }]
84
- }]
76
+ "type" => "table_header",
77
+ "content" => [{
78
+ "type" => "paragraph",
79
+ "content" => [{
80
+ "type" => "text",
81
+ "text" => "City",
82
+ }],
83
+ }],
84
+ }],
85
85
  }, {
86
- 'type' => 'table_row',
87
- 'content' => [{
88
- 'type' => 'table_cell',
89
- 'content' => [{
90
- 'type' => 'paragraph',
91
- 'content' => [{
92
- 'type' => 'text',
93
- 'text' => 'John'
94
- }]
95
- }]
86
+ "type" => "table_row",
87
+ "content" => [{
88
+ "type" => "table_cell",
89
+ "content" => [{
90
+ "type" => "paragraph",
91
+ "content" => [{
92
+ "type" => "text",
93
+ "text" => "John",
94
+ }],
95
+ }],
96
96
  }, {
97
- 'type' => 'table_cell',
98
- 'content' => [{
99
- 'type' => 'paragraph',
100
- 'content' => [{
101
- 'type' => 'text',
102
- 'text' => '25'
103
- }]
104
- }]
97
+ "type" => "table_cell",
98
+ "content" => [{
99
+ "type" => "paragraph",
100
+ "content" => [{
101
+ "type" => "text",
102
+ "text" => "25",
103
+ }],
104
+ }],
105
105
  }, {
106
- 'type' => 'table_cell',
107
- 'content' => [{
108
- 'type' => 'paragraph',
109
- 'content' => [{
110
- 'type' => 'text',
111
- 'text' => 'New York'
112
- }]
113
- }]
114
- }]
106
+ "type" => "table_cell",
107
+ "content" => [{
108
+ "type" => "paragraph",
109
+ "content" => [{
110
+ "type" => "text",
111
+ "text" => "New York",
112
+ }],
113
+ }],
114
+ }],
115
115
  }, {
116
- 'type' => 'table_row',
117
- 'content' => [{
118
- 'type' => 'table_cell',
119
- 'content' => [{
120
- 'type' => 'paragraph',
121
- 'content' => [{
122
- 'type' => 'text',
123
- 'text' => 'Alice'
124
- }]
125
- }]
116
+ "type" => "table_row",
117
+ "content" => [{
118
+ "type" => "table_cell",
119
+ "content" => [{
120
+ "type" => "paragraph",
121
+ "content" => [{
122
+ "type" => "text",
123
+ "text" => "Alice",
124
+ }],
125
+ }],
126
126
  }, {
127
- 'type' => 'table_cell',
128
- 'content' => [{
129
- 'type' => 'paragraph',
130
- 'content' => [{
131
- 'type' => 'text',
132
- 'text' => '30'
133
- }]
134
- }]
127
+ "type" => "table_cell",
128
+ "content" => [{
129
+ "type" => "paragraph",
130
+ "content" => [{
131
+ "type" => "text",
132
+ "text" => "30",
133
+ }],
134
+ }],
135
135
  }, {
136
- 'type' => 'table_cell',
137
- 'content' => [{
138
- 'type' => 'paragraph',
139
- 'content' => [{
140
- 'type' => 'text',
141
- 'text' => 'London'
142
- }]
143
- }]
144
- }]
145
- }]
136
+ "type" => "table_cell",
137
+ "content" => [{
138
+ "type" => "paragraph",
139
+ "content" => [{
140
+ "type" => "text",
141
+ "text" => "London",
142
+ }],
143
+ }],
144
+ }],
145
+ }],
146
146
  }
147
147
 
148
148
  expect(table.to_h).to eq(expected)
149
149
  end
150
150
 
151
- it 'creates a table with complex cell content' do
151
+ it "creates a table with complex cell content" do
152
152
  table = described_class.create
153
153
  table.add_header(%w[Description Status])
154
154
 
155
155
  row = table.add_row
156
156
  cell = row.add_cell
157
- para = cell.add_paragraph('This is ')
158
- para.add_text('bold', [Prosereflect::Mark::Bold.new])
159
- para.add_text(' and ')
160
- para.add_text('italic', [Prosereflect::Mark::Italic.new])
157
+ para = cell.add_paragraph("This is ")
158
+ para.add_text("bold", [Prosereflect::Mark::Bold.new])
159
+ para.add_text(" and ")
160
+ para.add_text("italic", [Prosereflect::Mark::Italic.new])
161
161
  para.add_hard_break
162
- para.add_text('text')
162
+ para.add_text("text")
163
163
 
164
164
  cell = row.add_cell
165
- para = cell.add_paragraph('Complete')
166
- para.add_text('', [Prosereflect::Mark::Bold.new, Prosereflect::Mark::Strike.new])
165
+ para = cell.add_paragraph("Complete")
166
+ para.add_text("", [Prosereflect::Mark::Bold.new, Prosereflect::Mark::Strike.new])
167
167
 
168
168
  expected = {
169
- 'type' => 'table',
170
- 'content' => [{
171
- 'type' => 'table_row',
172
- 'content' => [{
173
- 'type' => 'table_header',
174
- 'content' => [{
175
- 'type' => 'paragraph',
176
- 'content' => [{
177
- 'type' => 'text',
178
- 'text' => 'Description'
179
- }]
180
- }]
169
+ "type" => "table",
170
+ "content" => [{
171
+ "type" => "table_row",
172
+ "content" => [{
173
+ "type" => "table_header",
174
+ "content" => [{
175
+ "type" => "paragraph",
176
+ "content" => [{
177
+ "type" => "text",
178
+ "text" => "Description",
179
+ }],
180
+ }],
181
181
  }, {
182
- 'type' => 'table_header',
183
- 'content' => [{
184
- 'type' => 'paragraph',
185
- 'content' => [{
186
- 'type' => 'text',
187
- 'text' => 'Status'
188
- }]
189
- }]
190
- }]
182
+ "type" => "table_header",
183
+ "content" => [{
184
+ "type" => "paragraph",
185
+ "content" => [{
186
+ "type" => "text",
187
+ "text" => "Status",
188
+ }],
189
+ }],
190
+ }],
191
191
  }, {
192
- 'type' => 'table_row',
193
- 'content' => [{
194
- 'type' => 'table_cell',
195
- 'content' => [{
196
- 'type' => 'paragraph',
197
- 'content' => [{
198
- 'type' => 'text',
199
- 'text' => 'This is '
192
+ "type" => "table_row",
193
+ "content" => [{
194
+ "type" => "table_cell",
195
+ "content" => [{
196
+ "type" => "paragraph",
197
+ "content" => [{
198
+ "type" => "text",
199
+ "text" => "This is ",
200
200
  }, {
201
- 'type' => 'text',
202
- 'text' => 'bold',
203
- 'marks' => [{
204
- 'type' => 'bold'
205
- }]
201
+ "type" => "text",
202
+ "text" => "bold",
203
+ "marks" => [{
204
+ "type" => "bold",
205
+ }],
206
206
  }, {
207
- 'type' => 'text',
208
- 'text' => ' and '
207
+ "type" => "text",
208
+ "text" => " and ",
209
209
  }, {
210
- 'type' => 'text',
211
- 'text' => 'italic',
212
- 'marks' => [{
213
- 'type' => 'italic'
214
- }]
210
+ "type" => "text",
211
+ "text" => "italic",
212
+ "marks" => [{
213
+ "type" => "italic",
214
+ }],
215
215
  }, {
216
- 'type' => 'hard_break'
216
+ "type" => "hard_break",
217
217
  }, {
218
- 'type' => 'text',
219
- 'text' => 'text'
220
- }]
221
- }]
218
+ "type" => "text",
219
+ "text" => "text",
220
+ }],
221
+ }],
222
222
  }, {
223
- 'type' => 'table_cell',
224
- 'content' => [{
225
- 'type' => 'paragraph',
226
- 'content' => [{
227
- 'type' => 'text',
228
- 'text' => 'Complete'
223
+ "type" => "table_cell",
224
+ "content" => [{
225
+ "type" => "paragraph",
226
+ "content" => [{
227
+ "type" => "text",
228
+ "text" => "Complete",
229
229
  }, {
230
- 'type' => 'text',
231
- 'text' => '',
232
- 'marks' => [{
233
- 'type' => 'bold'
230
+ "type" => "text",
231
+ "text" => "",
232
+ "marks" => [{
233
+ "type" => "bold",
234
234
  }, {
235
- 'type' => 'strike'
236
- }]
237
- }]
238
- }]
239
- }]
240
- }]
235
+ "type" => "strike",
236
+ }],
237
+ }],
238
+ }],
239
+ }],
240
+ }],
241
241
  }
242
242
 
243
243
  expect(table.to_h).to eq(expected)
244
244
  end
245
245
  end
246
246
 
247
- describe 'table operations' do
247
+ describe "table operations" do
248
248
  let(:table) do
249
249
  t = described_class.create
250
250
  t.add_header(%w[Name Age City])
251
- t.add_row(['John', '25', 'New York'])
251
+ t.add_row(["John", "25", "New York"])
252
252
  t.add_row(%w[Alice 30 London])
253
253
  t
254
254
  end
255
255
 
256
- describe '#rows' do
257
- it 'returns all rows including header' do
256
+ describe "#rows" do
257
+ it "returns all rows including header" do
258
258
  expect(table.rows.size).to eq(3)
259
259
  expect(table.rows).to all(be_a(Prosereflect::TableRow))
260
260
  expect(table.rows.first.cells.first).to be_a(Prosereflect::TableHeader)
@@ -262,38 +262,40 @@ RSpec.describe Prosereflect::Table do
262
262
  end
263
263
  end
264
264
 
265
- describe '#header_row' do
266
- it 'returns the first row with header cells' do
265
+ describe "#header_row" do
266
+ it "returns the first row with header cells" do
267
267
  header = table.header_row
268
268
  expect(header).to be_a(Prosereflect::TableRow)
269
269
  expect(header.cells).to all(be_a(Prosereflect::TableHeader))
270
270
  expect(header.cells.map(&:text_content)).to eq(%w[Name Age City])
271
271
  end
272
272
 
273
- it 'returns nil for empty table' do
273
+ it "returns nil for empty table" do
274
274
  empty_table = described_class.create
275
275
  expect(empty_table.header_row).to be_nil
276
276
  end
277
277
  end
278
278
 
279
- describe '#data_rows' do
280
- it 'returns all non-header rows' do
279
+ describe "#data_rows" do
280
+ it "returns all non-header rows" do
281
281
  data_rows = table.data_rows
282
282
  expect(data_rows.size).to eq(2)
283
283
  expect(data_rows).to all(be_a(Prosereflect::TableRow))
284
- expect(data_rows.first.cells.map(&:text_content)).to eq(['John', '25', 'New York'])
285
- expect(data_rows.last.cells.map(&:text_content)).to eq(%w[Alice 30 London])
284
+ expect(data_rows.first.cells.map(&:text_content)).to eq(["John", "25",
285
+ "New York"])
286
+ expect(data_rows.last.cells.map(&:text_content)).to eq(%w[Alice 30
287
+ London])
286
288
  end
287
289
  end
288
290
 
289
- describe '#cell_at' do
290
- it 'returns cell at specified position' do
291
+ describe "#cell_at" do
292
+ it "returns cell at specified position" do
291
293
  cell = table.cell_at(0, 1)
292
294
  expect(cell).to be_a(Prosereflect::TableCell)
293
- expect(cell.text_content).to eq('25')
295
+ expect(cell.text_content).to eq("25")
294
296
  end
295
297
 
296
- it 'returns nil for invalid positions' do
298
+ it "returns nil for invalid positions" do
297
299
  expect(table.cell_at(-1, 0)).to be_nil
298
300
  expect(table.cell_at(0, -1)).to be_nil
299
301
  expect(table.cell_at(5, 0)).to be_nil
@@ -302,156 +304,156 @@ RSpec.describe Prosereflect::Table do
302
304
  end
303
305
  end
304
306
 
305
- describe 'table building' do
306
- describe '#add_header' do
307
- it 'adds a header row with styled cells' do
307
+ describe "table building" do
308
+ describe "#add_header" do
309
+ it "adds a header row with styled cells" do
308
310
  table = described_class.create
309
311
  header = table.add_header(%w[Title Description])
310
- header.cells.first.add_text(' (required)', [Prosereflect::Mark::Italic.new])
312
+ header.cells.first.add_text(" (required)", [Prosereflect::Mark::Italic.new])
311
313
 
312
314
  expected = {
313
- 'type' => 'table',
314
- 'content' => [{
315
- 'type' => 'table_row',
316
- 'content' => [{
317
- 'type' => 'table_header',
318
- 'content' => [{
319
- 'type' => 'paragraph',
320
- 'content' => [{
321
- 'type' => 'text',
322
- 'text' => 'Title'
315
+ "type" => "table",
316
+ "content" => [{
317
+ "type" => "table_row",
318
+ "content" => [{
319
+ "type" => "table_header",
320
+ "content" => [{
321
+ "type" => "paragraph",
322
+ "content" => [{
323
+ "type" => "text",
324
+ "text" => "Title",
323
325
  }, {
324
- 'type' => 'text',
325
- 'text' => ' (required)',
326
- 'marks' => [{
327
- 'type' => 'italic'
328
- }]
329
- }]
330
- }]
326
+ "type" => "text",
327
+ "text" => " (required)",
328
+ "marks" => [{
329
+ "type" => "italic",
330
+ }],
331
+ }],
332
+ }],
331
333
  }, {
332
- 'type' => 'table_header',
333
- 'content' => [{
334
- 'type' => 'paragraph',
335
- 'content' => [{
336
- 'type' => 'text',
337
- 'text' => 'Description'
338
- }]
339
- }]
340
- }]
341
- }]
334
+ "type" => "table_header",
335
+ "content" => [{
336
+ "type" => "paragraph",
337
+ "content" => [{
338
+ "type" => "text",
339
+ "text" => "Description",
340
+ }],
341
+ }],
342
+ }],
343
+ }],
342
344
  }
343
345
 
344
346
  expect(table.to_h).to eq(expected)
345
347
  end
346
348
  end
347
349
 
348
- describe '#add_rows' do
349
- it 'adds multiple rows with mixed content' do
350
+ describe "#add_rows" do
351
+ it "adds multiple rows with mixed content" do
350
352
  table = described_class.create
351
353
  table.add_header(%w[Item Status])
352
354
 
353
355
  table.add_rows([
354
- ['Task 1', 'Done'],
355
- ['Task 2', 'Pending']
356
+ ["Task 1", "Done"],
357
+ ["Task 2", "Pending"],
356
358
  ])
357
359
 
358
360
  row = table.add_row
359
361
  cell = row.add_cell
360
- cell.add_paragraph('Task 3')
362
+ cell.add_paragraph("Task 3")
361
363
  cell = row.add_cell
362
364
  para = cell.add_paragraph
363
- para.add_text('In Progress', [Prosereflect::Mark::Bold.new])
365
+ para.add_text("In Progress", [Prosereflect::Mark::Bold.new])
364
366
 
365
367
  expected = {
366
- 'type' => 'table',
367
- 'content' => [{
368
- 'type' => 'table_row',
369
- 'content' => [{
370
- 'type' => 'table_header',
371
- 'content' => [{
372
- 'type' => 'paragraph',
373
- 'content' => [{
374
- 'type' => 'text',
375
- 'text' => 'Item'
376
- }]
377
- }]
368
+ "type" => "table",
369
+ "content" => [{
370
+ "type" => "table_row",
371
+ "content" => [{
372
+ "type" => "table_header",
373
+ "content" => [{
374
+ "type" => "paragraph",
375
+ "content" => [{
376
+ "type" => "text",
377
+ "text" => "Item",
378
+ }],
379
+ }],
378
380
  }, {
379
- 'type' => 'table_header',
380
- 'content' => [{
381
- 'type' => 'paragraph',
382
- 'content' => [{
383
- 'type' => 'text',
384
- 'text' => 'Status'
385
- }]
386
- }]
387
- }]
381
+ "type" => "table_header",
382
+ "content" => [{
383
+ "type" => "paragraph",
384
+ "content" => [{
385
+ "type" => "text",
386
+ "text" => "Status",
387
+ }],
388
+ }],
389
+ }],
388
390
  }, {
389
- 'type' => 'table_row',
390
- 'content' => [{
391
- 'type' => 'table_cell',
392
- 'content' => [{
393
- 'type' => 'paragraph',
394
- 'content' => [{
395
- 'type' => 'text',
396
- 'text' => 'Task 1'
397
- }]
398
- }]
391
+ "type" => "table_row",
392
+ "content" => [{
393
+ "type" => "table_cell",
394
+ "content" => [{
395
+ "type" => "paragraph",
396
+ "content" => [{
397
+ "type" => "text",
398
+ "text" => "Task 1",
399
+ }],
400
+ }],
399
401
  }, {
400
- 'type' => 'table_cell',
401
- 'content' => [{
402
- 'type' => 'paragraph',
403
- 'content' => [{
404
- 'type' => 'text',
405
- 'text' => 'Done'
406
- }]
407
- }]
408
- }]
402
+ "type" => "table_cell",
403
+ "content" => [{
404
+ "type" => "paragraph",
405
+ "content" => [{
406
+ "type" => "text",
407
+ "text" => "Done",
408
+ }],
409
+ }],
410
+ }],
409
411
  }, {
410
- 'type' => 'table_row',
411
- 'content' => [{
412
- 'type' => 'table_cell',
413
- 'content' => [{
414
- 'type' => 'paragraph',
415
- 'content' => [{
416
- 'type' => 'text',
417
- 'text' => 'Task 2'
418
- }]
419
- }]
412
+ "type" => "table_row",
413
+ "content" => [{
414
+ "type" => "table_cell",
415
+ "content" => [{
416
+ "type" => "paragraph",
417
+ "content" => [{
418
+ "type" => "text",
419
+ "text" => "Task 2",
420
+ }],
421
+ }],
420
422
  }, {
421
- 'type' => 'table_cell',
422
- 'content' => [{
423
- 'type' => 'paragraph',
424
- 'content' => [{
425
- 'type' => 'text',
426
- 'text' => 'Pending'
427
- }]
428
- }]
429
- }]
423
+ "type" => "table_cell",
424
+ "content" => [{
425
+ "type" => "paragraph",
426
+ "content" => [{
427
+ "type" => "text",
428
+ "text" => "Pending",
429
+ }],
430
+ }],
431
+ }],
430
432
  }, {
431
- 'type' => 'table_row',
432
- 'content' => [{
433
- 'type' => 'table_cell',
434
- 'content' => [{
435
- 'type' => 'paragraph',
436
- 'content' => [{
437
- 'type' => 'text',
438
- 'text' => 'Task 3'
439
- }]
440
- }]
433
+ "type" => "table_row",
434
+ "content" => [{
435
+ "type" => "table_cell",
436
+ "content" => [{
437
+ "type" => "paragraph",
438
+ "content" => [{
439
+ "type" => "text",
440
+ "text" => "Task 3",
441
+ }],
442
+ }],
441
443
  }, {
442
- 'type' => 'table_cell',
443
- 'content' => [{
444
- 'type' => 'paragraph',
445
- 'content' => [{
446
- 'type' => 'text',
447
- 'text' => 'In Progress',
448
- 'marks' => [{
449
- 'type' => 'bold'
450
- }]
451
- }]
452
- }]
453
- }]
454
- }]
444
+ "type" => "table_cell",
445
+ "content" => [{
446
+ "type" => "paragraph",
447
+ "content" => [{
448
+ "type" => "text",
449
+ "text" => "In Progress",
450
+ "marks" => [{
451
+ "type" => "bold",
452
+ }],
453
+ }],
454
+ }],
455
+ }],
456
+ }],
455
457
  }
456
458
 
457
459
  expect(table.to_h).to eq(expected)
@@ -459,17 +461,17 @@ RSpec.describe Prosereflect::Table do
459
461
  end
460
462
  end
461
463
 
462
- describe 'serialization' do
463
- it 'converts to hash representation' do
464
+ describe "serialization" do
465
+ it "converts to hash representation" do
464
466
  table = described_class.new
465
- table.add_header(['Col 1', 'Col 2'])
466
- table.add_row(['Data 1', 'Data 2'])
467
+ table.add_header(["Col 1", "Col 2"])
468
+ table.add_row(["Data 1", "Data 2"])
467
469
 
468
470
  hash = table.to_h
469
- expect(hash['type']).to eq('table')
470
- expect(hash['content'].size).to eq(2)
471
- expect(hash['content'][0]['type']).to eq('table_row')
472
- expect(hash['content'][1]['type']).to eq('table_row')
471
+ expect(hash["type"]).to eq("table")
472
+ expect(hash["content"].size).to eq(2)
473
+ expect(hash["content"][0]["type"]).to eq("table_row")
474
+ expect(hash["content"][1]["type"]).to eq("table_row")
473
475
  end
474
476
  end
475
477
  end