commenter 0.3.0 → 0.4.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/.rubocop.yml +4 -0
- data/CLAUDE.md +17 -7
- data/CONTEXT.md +27 -0
- data/README.adoc +92 -17
- data/commenter.gemspec +1 -0
- data/lib/commenter/ballot.rb +67 -0
- data/lib/commenter/ballot_report.rb +60 -0
- data/lib/commenter/cli.rb +62 -43
- data/lib/commenter/comment.rb +103 -126
- data/lib/commenter/comment_locality.rb +18 -0
- data/lib/commenter/comment_sheet.rb +31 -63
- data/lib/commenter/comment_type.rb +39 -0
- data/lib/commenter/disposition_status.rb +37 -0
- data/lib/commenter/filler.rb +91 -59
- data/lib/commenter/github_info.rb +23 -0
- data/lib/commenter/github_integration.rb +17 -71
- data/lib/commenter/github_session.rb +35 -0
- data/lib/commenter/parser/osd_xlsx_parser.rb +1 -12
- data/lib/commenter/parser/track_change_docx_parser.rb +330 -0
- data/lib/commenter/parser.rb +10 -13
- data/lib/commenter/version.rb +1 -1
- data/lib/commenter.rb +16 -5
- data/spec/commenter/ballot_report_spec.rb +62 -0
- data/spec/commenter/ballot_spec.rb +68 -0
- data/spec/commenter/cli_spec.rb +41 -0
- data/spec/commenter/comment_sheet_spec.rb +35 -10
- data/spec/commenter/comment_spec.rb +22 -9
- data/spec/commenter/comment_type_spec.rb +45 -0
- data/spec/commenter/disposition_status_spec.rb +40 -0
- data/spec/commenter/filler_spec.rb +119 -0
- data/spec/commenter/github_integration_spec.rb +65 -32
- data/spec/commenter/osd_xlsx_parser_spec.rb +3 -2
- data/spec/commenter/track_change_docx_parser_spec.rb +277 -0
- data/spec/support/redline_docx_builder.rb +95 -0
- data/spec/support/xlsx_builder.rb +17 -12
- metadata +32 -2
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "zip"
|
|
4
|
+
|
|
5
|
+
# Builds minimal redline DOCX files for specs, without binary fixtures. The
|
|
6
|
+
# document body is assembled from a paragraph DSL:
|
|
7
|
+
#
|
|
8
|
+
# { heading: "1", level: 1, text: "Scope" }
|
|
9
|
+
# { text: "Body text " }
|
|
10
|
+
# { change: :ins, id: "1", author: "CHEN Yvonne", date: "2025-12-05T20:10:00Z",
|
|
11
|
+
# text: "inserted" }
|
|
12
|
+
# { marker: true } # self-closing paragraph-mark insertion inside rPr
|
|
13
|
+
# { anchor: "74", text: "Anchored text" } # w:commentRangeStart/End around text
|
|
14
|
+
#
|
|
15
|
+
# Multiple entries in one paragraph can be given via :parts.
|
|
16
|
+
#
|
|
17
|
+
# comments: [{ id: "74", author: "CHEN Yvonne", date: "...", text: "remark" }]
|
|
18
|
+
# are written to word/comments.xml.
|
|
19
|
+
module RedlineDocxBuilder
|
|
20
|
+
W_NS = "http://schemas.openxmlformats.org/wordprocessingml/2006/main"
|
|
21
|
+
private_constant :W_NS
|
|
22
|
+
|
|
23
|
+
HEADING_LEVEL_STYLE = { 1 => "Heading1", 2 => "Heading2", 3 => "Heading3" }.freeze
|
|
24
|
+
CHANGE_TAG = { ins: "w:ins", del: "w:del", moveFrom: "w:moveFrom", moveTo: "w:moveTo" }.freeze
|
|
25
|
+
private_constant :HEADING_LEVEL_STYLE, :CHANGE_TAG
|
|
26
|
+
|
|
27
|
+
def self.write(path, paragraphs, comments: [])
|
|
28
|
+
# Written via an in-memory buffer and File.binwrite rather than
|
|
29
|
+
# Zip::File#create + close: rubyzip commits the latter with File.rename,
|
|
30
|
+
# which fails with EACCES on Windows when the caller still holds an open
|
|
31
|
+
# handle on the destination (e.g. an unclosed Tempfile).
|
|
32
|
+
buffer = Zip::OutputStream.write_buffer do |out|
|
|
33
|
+
out.put_next_entry("word/document.xml")
|
|
34
|
+
out.write(document_xml(paragraphs))
|
|
35
|
+
out.put_next_entry("word/comments.xml")
|
|
36
|
+
out.write(comments_xml(comments))
|
|
37
|
+
end
|
|
38
|
+
File.binwrite(path, buffer.string)
|
|
39
|
+
path
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.document_xml(paragraphs)
|
|
43
|
+
<<~XML
|
|
44
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
45
|
+
<w:document xmlns:w="#{W_NS}"><w:body>#{paragraphs.map { |p| paragraph_xml(p) }.join}</w:body></w:document>
|
|
46
|
+
XML
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def self.comments_xml(comments)
|
|
50
|
+
entries = comments.map do |c|
|
|
51
|
+
attrs = %(w:id="#{c[:id]}" w:author="#{c[:author]}" w:date="#{c[:date]}" w:initials="X")
|
|
52
|
+
%(<w:comment #{attrs}><w:p><w:r><w:t>#{c[:text]}</w:t></w:r></w:p></w:comment>)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
<<~XML
|
|
56
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
57
|
+
<w:comments xmlns:w="#{W_NS}">#{entries.join}</w:comments>
|
|
58
|
+
XML
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def self.paragraph_xml(paragraph)
|
|
62
|
+
if paragraph[:marker]
|
|
63
|
+
return %(<w:p><w:pPr><w:rPr><w:ins w:id="#{paragraph[:id]}" w:author="#{paragraph[:author]}"/></w:rPr></w:pPr></w:p>)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
inner = if paragraph[:parts]
|
|
67
|
+
paragraph[:parts].map { |part| part_xml(part) }.join
|
|
68
|
+
elsif paragraph[:heading]
|
|
69
|
+
%(<w:r><w:t>#{paragraph[:text]}</w:t></w:r>)
|
|
70
|
+
elsif paragraph[:change]
|
|
71
|
+
change_xml(paragraph)
|
|
72
|
+
else
|
|
73
|
+
%(<w:r><w:t>#{paragraph[:text]}</w:t></w:r>)
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
ppr = paragraph[:heading] ? %(<w:pPr><w:pStyle w:val="#{HEADING_LEVEL_STYLE.fetch(paragraph[:level], 1)}"/></w:pPr>) : ""
|
|
77
|
+
anchor = paragraph[:anchor] ? %(<w:commentRangeStart w:id="#{paragraph[:anchor]}"/>) : ""
|
|
78
|
+
anchor_end = paragraph[:anchor] ? %(<w:commentRangeEnd w:id="#{paragraph[:anchor]}"/>) : ""
|
|
79
|
+
"<w:p>#{ppr}#{anchor}#{inner}#{anchor_end}</w:p>"
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def self.part_xml(part)
|
|
83
|
+
part[:change] ? change_xml(part) : %(<w:r><w:t>#{part[:text]}</w:t></w:r>)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.change_xml(change)
|
|
87
|
+
tag = CHANGE_TAG.fetch(change[:change])
|
|
88
|
+
attrs = %(w:id="#{change[:id]}" w:author="#{change[:author]}" w:date="#{change[:date]}")
|
|
89
|
+
|
|
90
|
+
return %(<#{tag} #{attrs}>#{change[:nested].map { |child| change_xml(child) }.join}</#{tag}>) if change[:nested]
|
|
91
|
+
|
|
92
|
+
text_tag = change[:change] == :del ? "w:delText" : "w:t"
|
|
93
|
+
%(<#{tag} #{attrs}><w:r><#{text_tag}>#{change[:text]}</#{text_tag}></w:r></#{tag}>)
|
|
94
|
+
end
|
|
95
|
+
end
|
|
@@ -18,22 +18,27 @@ module XlsxBuilder
|
|
|
18
18
|
shared = []
|
|
19
19
|
worksheets = sheets.map { |name, rows| [name, worksheet_xml(rows, shared)] }
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
write_entry(
|
|
21
|
+
# In-memory buffer + File.binwrite rather than Zip::File#create + close:
|
|
22
|
+
# rubyzip commits the latter with File.rename, which fails with EACCES on
|
|
23
|
+
# Windows when the caller still holds an open handle on the destination.
|
|
24
|
+
buffer = Zip::OutputStream.write_buffer do |out|
|
|
25
|
+
write_entry(out, "[Content_Types].xml", content_types_xml(worksheets.size))
|
|
26
|
+
write_entry(out, "_rels/.rels", root_rels_xml)
|
|
27
|
+
write_entry(out, "xl/workbook.xml", workbook_xml(worksheets.map(&:first)))
|
|
28
|
+
write_entry(out, "xl/_rels/workbook.xml.rels", workbook_rels_xml(worksheets.size))
|
|
29
|
+
write_entry(out, "xl/sharedStrings.xml", shared_strings_xml(shared))
|
|
30
|
+
write_entry(out, "xl/styles.xml", styles_xml)
|
|
31
|
+
worksheets.each_with_index do |(_name, xml), index|
|
|
32
|
+
write_entry(out, "xl/worksheets/sheet#{index + 1}.xml", xml)
|
|
33
|
+
end
|
|
30
34
|
end
|
|
31
|
-
|
|
35
|
+
File.binwrite(path, buffer.string)
|
|
32
36
|
path
|
|
33
37
|
end
|
|
34
38
|
|
|
35
|
-
def self.write_entry(
|
|
36
|
-
|
|
39
|
+
def self.write_entry(out, name, content)
|
|
40
|
+
out.put_next_entry(name)
|
|
41
|
+
out.write(content)
|
|
37
42
|
end
|
|
38
43
|
|
|
39
44
|
def self.worksheet_xml(rows, shared)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: commenter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.4.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Ribose
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-08-
|
|
11
|
+
date: 2026-08-29 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: base64
|
|
@@ -66,6 +66,20 @@ dependencies:
|
|
|
66
66
|
- - "~>"
|
|
67
67
|
- !ruby/object:Gem::Version
|
|
68
68
|
version: '5.0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: lutaml-model
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - "~>"
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0.8'
|
|
76
|
+
type: :runtime
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - "~>"
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0.8'
|
|
69
83
|
- !ruby/object:Gem::Dependency
|
|
70
84
|
name: octokit
|
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -125,6 +139,7 @@ files:
|
|
|
125
139
|
- ".rubocop_todo.yml"
|
|
126
140
|
- CLAUDE.md
|
|
127
141
|
- CODE_OF_CONDUCT.md
|
|
142
|
+
- CONTEXT.md
|
|
128
143
|
- Gemfile
|
|
129
144
|
- README.adoc
|
|
130
145
|
- Rakefile
|
|
@@ -136,25 +151,40 @@ files:
|
|
|
136
151
|
- data/iso_comment_template_2012-03.docx
|
|
137
152
|
- exe/commenter
|
|
138
153
|
- lib/commenter.rb
|
|
154
|
+
- lib/commenter/ballot.rb
|
|
155
|
+
- lib/commenter/ballot_report.rb
|
|
139
156
|
- lib/commenter/cli.rb
|
|
140
157
|
- lib/commenter/comment.rb
|
|
158
|
+
- lib/commenter/comment_locality.rb
|
|
141
159
|
- lib/commenter/comment_sheet.rb
|
|
160
|
+
- lib/commenter/comment_type.rb
|
|
161
|
+
- lib/commenter/disposition_status.rb
|
|
142
162
|
- lib/commenter/filler.rb
|
|
163
|
+
- lib/commenter/github_info.rb
|
|
143
164
|
- lib/commenter/github_integration.rb
|
|
165
|
+
- lib/commenter/github_session.rb
|
|
144
166
|
- lib/commenter/parser.rb
|
|
145
167
|
- lib/commenter/parser/osd_xlsx_parser.rb
|
|
168
|
+
- lib/commenter/parser/track_change_docx_parser.rb
|
|
146
169
|
- lib/commenter/version.rb
|
|
147
170
|
- schema/iso_comment_2012-03.yaml
|
|
148
171
|
- schema/iso_comment_osd.yaml
|
|
149
172
|
- sig/commenter.rbs
|
|
173
|
+
- spec/commenter/ballot_report_spec.rb
|
|
174
|
+
- spec/commenter/ballot_spec.rb
|
|
150
175
|
- spec/commenter/cli_spec.rb
|
|
151
176
|
- spec/commenter/comment_sheet_spec.rb
|
|
152
177
|
- spec/commenter/comment_spec.rb
|
|
178
|
+
- spec/commenter/comment_type_spec.rb
|
|
179
|
+
- spec/commenter/disposition_status_spec.rb
|
|
180
|
+
- spec/commenter/filler_spec.rb
|
|
153
181
|
- spec/commenter/github_integration_spec.rb
|
|
154
182
|
- spec/commenter/osd_xlsx_parser_spec.rb
|
|
183
|
+
- spec/commenter/track_change_docx_parser_spec.rb
|
|
155
184
|
- spec/commenter_spec.rb
|
|
156
185
|
- spec/spec_helper.rb
|
|
157
186
|
- spec/support/osd_fixtures.rb
|
|
187
|
+
- spec/support/redline_docx_builder.rb
|
|
158
188
|
- spec/support/xlsx_builder.rb
|
|
159
189
|
homepage: https://github.com/metanorma/commenter
|
|
160
190
|
licenses:
|