mn-requirements 0.0.1
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/.github/workflows/rake.yml +15 -0
- data/.github/workflows/release.yml +24 -0
- data/.hound.yml +5 -0
- data/.rubocop.yml +13 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +12 -0
- data/LICENSE +25 -0
- data/README.adoc +26 -0
- data/Rakefile +8 -0
- data/bin/console +14 -0
- data/bin/rspec +17 -0
- data/bin/setup +8 -0
- data/lib/metanorma/default/cleanup.rb +146 -0
- data/lib/metanorma/default/default.rb +99 -0
- data/lib/metanorma/default/isodoc.rb +106 -0
- data/lib/metanorma/default/utils.rb +27 -0
- data/lib/metanorma/default/xrefs.rb +35 -0
- data/lib/metanorma/modspec/cleanup.rb +89 -0
- data/lib/metanorma/modspec/isodoc.rb +238 -0
- data/lib/metanorma/modspec/modspec.rb +9 -0
- data/lib/metanorma/modspec/reqt_label.rb +80 -0
- data/lib/metanorma/modspec/xrefs.rb +66 -0
- data/lib/metanorma/requirements/selector.rb +94 -0
- data/lib/metanorma/requirements/version.rb +5 -0
- data/lib/mn-requirements.rb +2 -0
- data/mn-requirements.gemspec +42 -0
- metadata +267 -0
@@ -0,0 +1,89 @@
|
|
1
|
+
module Metanorma
|
2
|
+
class Requirements
|
3
|
+
class Modspec < Default
|
4
|
+
def requirement_type_cleanup(reqt)
|
5
|
+
reqt["type"] = case reqt["type"]
|
6
|
+
when "requirement", "recommendation", "permission"
|
7
|
+
"general"
|
8
|
+
when "requirements_class" then "class"
|
9
|
+
when "conformance_test" then "verification"
|
10
|
+
when "conformance_class" then "conformanceclass"
|
11
|
+
when "abstract_test" then "abstracttest"
|
12
|
+
else reqt["type"]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def requirement_metadata_component_tags
|
17
|
+
%w(test-purpose test-method test-method-type conditions part description
|
18
|
+
reference step requirement permission recommendation)
|
19
|
+
end
|
20
|
+
|
21
|
+
def requirement_metadata1(reqt, dlist, ins)
|
22
|
+
ins1 = super
|
23
|
+
dlist.xpath("./dt").each do |e|
|
24
|
+
tag = e&.text&.gsub(/ /, "-")&.downcase
|
25
|
+
next unless requirement_metadata_component_tags.include? tag
|
26
|
+
|
27
|
+
ins1.next = requirement_metadata1_component(e, tag)
|
28
|
+
ins1 = ins1.next
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def requirement_metadata1_component(term, tag)
|
33
|
+
val = term.at("./following::dd")
|
34
|
+
val.name = tag
|
35
|
+
val.xpath("./dl").each do |d|
|
36
|
+
requirement_metadata1(val, d, d)
|
37
|
+
d.remove
|
38
|
+
end
|
39
|
+
if REQS.include?(term.text) && !val.text.empty?
|
40
|
+
val["label"] = val.text.strip
|
41
|
+
val.children.remove
|
42
|
+
end
|
43
|
+
val
|
44
|
+
end
|
45
|
+
|
46
|
+
# separate from default model requirement_metadata_cleanup,
|
47
|
+
# which extracts model:: ogc into reqt["model"]
|
48
|
+
def requirement_metadata_cleanup(reqt)
|
49
|
+
super
|
50
|
+
requirement_metadata_to_component(reqt)
|
51
|
+
requirement_metadata_to_requirement(reqt)
|
52
|
+
requirement_subparts_to_blocks(reqt)
|
53
|
+
requirement_target_identifiers(reqt)
|
54
|
+
end
|
55
|
+
|
56
|
+
def requirement_target_identifiers(reqt)
|
57
|
+
reqt.xpath("./classification[tag = 'target']/value[link]").each do |v|
|
58
|
+
v.children = v.at("./link/@target").text
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def requirement_metadata_to_component(reqt)
|
63
|
+
reqt.xpath(".//test-method | .//test-purpose | .//conditions | "\
|
64
|
+
".//part | .//test-method-type | .//step | .//reference")
|
65
|
+
.each do |c|
|
66
|
+
c["class"] = c.name
|
67
|
+
c.name = "component"
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def requirement_metadata_to_requirement(reqt)
|
72
|
+
reqt.xpath("./requirement | ./permission | ./recommendation")
|
73
|
+
.each do |c|
|
74
|
+
c["id"] = Metanorma::Utils::anchor_or_uuid
|
75
|
+
c["model"] = reqt["model"] # all requirements must have a model
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def requirement_subparts_to_blocks(reqt)
|
80
|
+
reqt.xpath(".//component | .//description").each do |c|
|
81
|
+
next if %w(p ol ul dl table component description)
|
82
|
+
.include?(c&.elements&.first&.name)
|
83
|
+
|
84
|
+
c.children = "<p>#{c.children.to_xml}</p>"
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1,238 @@
|
|
1
|
+
require_relative "xrefs"
|
2
|
+
require_relative "reqt_label"
|
3
|
+
|
4
|
+
module Metanorma
|
5
|
+
class Requirements
|
6
|
+
class Modspec < Default
|
7
|
+
def requirement_render1(node)
|
8
|
+
requirement_table_cleanup(super)
|
9
|
+
end
|
10
|
+
|
11
|
+
def recommendation_base(node, klass)
|
12
|
+
out = node.document.create_element("table")
|
13
|
+
out.default_namespace = node.namespace.href
|
14
|
+
%w(id keep-with-next keep-lines-together unnumbered).each do |x|
|
15
|
+
out[x] = node[x] if node[x]
|
16
|
+
end
|
17
|
+
out["class"] = klass
|
18
|
+
out["type"] = recommend_class(node)
|
19
|
+
recommendation_component_labels(node)
|
20
|
+
out
|
21
|
+
end
|
22
|
+
|
23
|
+
def recommendation_component_labels(node)
|
24
|
+
node.xpath(ns("./component[@class = 'part']")).each_with_index do |c, i|
|
25
|
+
c["label"] = (i + "A".ord).chr.to_s
|
26
|
+
end
|
27
|
+
node.xpath(ns("./component[not(@class = 'part')]")).each do |c|
|
28
|
+
c["label"] = recommend_component_label(c)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def recommendation_header(recommend, out)
|
33
|
+
h = out.add_child("<thead><tr><th scope='colgroup' colspan='2'>"\
|
34
|
+
"</th></tr></thead>").first
|
35
|
+
recommendation_name(recommend, h.at(ns(".//th")))
|
36
|
+
out
|
37
|
+
end
|
38
|
+
|
39
|
+
def recommendation_name(node, out)
|
40
|
+
b = out.add_child("<p class='#{recommend_name_class(node)}'></p>").first
|
41
|
+
name = node.at(ns("./name")) and name.children.each do |n|
|
42
|
+
b << n
|
43
|
+
end
|
44
|
+
title = node.at(ns("./title"))
|
45
|
+
return unless title &&
|
46
|
+
node.ancestors("requirement, recommendation, permission").empty?
|
47
|
+
|
48
|
+
b << l10n(": ") if name
|
49
|
+
title.children.each { |n| b << n }
|
50
|
+
end
|
51
|
+
|
52
|
+
def recommendation_attributes(node, out)
|
53
|
+
ins = out.add_child("<tbody></tbody>").first
|
54
|
+
recommend_title(node, ins)
|
55
|
+
recommendation_attributes1(node).each do |i|
|
56
|
+
ins.add_child("<tr><td>#{i[0]}</td><td>#{i[1]}</td></tr>")
|
57
|
+
end
|
58
|
+
ins
|
59
|
+
end
|
60
|
+
|
61
|
+
def recommend_title(node, out)
|
62
|
+
label = node.at(ns("./identifier")) or return
|
63
|
+
b = out.add_child("<tr><td colspan='2'><p></p></td></tr>")
|
64
|
+
p = b.at(ns(".//p"))
|
65
|
+
p["class"] = "RecommendationLabel"
|
66
|
+
p << label.children.to_xml
|
67
|
+
end
|
68
|
+
|
69
|
+
def recommendation_attributes1(node)
|
70
|
+
ret = recommendation_attributes1_head(node, [])
|
71
|
+
node.xpath(ns("./classification")).each do |c|
|
72
|
+
line = recommendation_attr_keyvalue(c, "tag",
|
73
|
+
"value") and ret << line
|
74
|
+
end
|
75
|
+
ret
|
76
|
+
end
|
77
|
+
|
78
|
+
def recommendation_attributes1_head(node, head)
|
79
|
+
oblig = node["obligation"] and head << ["Obligation", oblig]
|
80
|
+
subj = node.at(ns("./subject"))&.children and
|
81
|
+
head << [rec_subj(node), subj]
|
82
|
+
node.xpath(ns("./classification[tag = 'target']/value")).each do |v|
|
83
|
+
xref = recommendation_id(node.document, v.text) and head << [
|
84
|
+
rec_target(node), xref
|
85
|
+
]
|
86
|
+
end
|
87
|
+
%w(general class).include?(node["type"]) and
|
88
|
+
xref = recommendation_link(node.document,
|
89
|
+
node.at(ns("./identifier"))&.text) and
|
90
|
+
head << ["Conformance test", xref]
|
91
|
+
recommendation_attributes1_dependencies(node, head)
|
92
|
+
end
|
93
|
+
|
94
|
+
def recommendation_attributes1_dependencies(node, head)
|
95
|
+
node.xpath(ns("./inherit")).each do |i|
|
96
|
+
head << ["Dependency",
|
97
|
+
recommendation_id(node.document, i.children.to_xml)]
|
98
|
+
end
|
99
|
+
node.xpath(ns("./classification[tag = 'indirect-dependency']/value"))
|
100
|
+
.each do |v|
|
101
|
+
xref = recommendation_id(node.document, v.children.to_xml) and
|
102
|
+
head << ["Indirect Dependency", xref]
|
103
|
+
end
|
104
|
+
head
|
105
|
+
end
|
106
|
+
|
107
|
+
def recommendation_steps(node)
|
108
|
+
node.elements.each { |e| recommendation_steps(e) }
|
109
|
+
return node unless node.at(ns("./component[@class = 'step']"))
|
110
|
+
|
111
|
+
d = node.at(ns("./component[@class = 'step']"))
|
112
|
+
d = d.replace("<ol class='steps'><li>#{d.children.to_xml}</li></ol>")
|
113
|
+
.first
|
114
|
+
node.xpath(ns("./component[@class = 'step']")).each do |f|
|
115
|
+
f = f.replace("<li>#{f.children.to_xml}</li>").first
|
116
|
+
d << f
|
117
|
+
end
|
118
|
+
node
|
119
|
+
end
|
120
|
+
|
121
|
+
def recommendation_attributes1_component(node, out)
|
122
|
+
node = recommendation_steps(node)
|
123
|
+
out << "<tr><td>#{node['label']}</td><td>#{node.children}</td></tr>"
|
124
|
+
out
|
125
|
+
end
|
126
|
+
|
127
|
+
def recommendation_attr_keyvalue(node, key, value)
|
128
|
+
tag = node.at(ns("./#{key}"))
|
129
|
+
value = node.at(ns("./#{value}"))
|
130
|
+
(tag && value && !%w(target
|
131
|
+
indirect-dependency).include?(tag.text)) or
|
132
|
+
return nil
|
133
|
+
[tag.text.capitalize, value.children]
|
134
|
+
end
|
135
|
+
|
136
|
+
def reqt_component_type(node)
|
137
|
+
klass = node.name
|
138
|
+
klass == "component" and klass = node["class"]
|
139
|
+
"requirement-#{klass}"
|
140
|
+
end
|
141
|
+
|
142
|
+
def preserve_in_nested_table?(node)
|
143
|
+
%w(recommendation requirement permission
|
144
|
+
table ol dl ul).include?(node.name)
|
145
|
+
end
|
146
|
+
|
147
|
+
def requirement_component_parse(node, out)
|
148
|
+
return out if node["exclude"] == "true"
|
149
|
+
|
150
|
+
node.elements.size == 1 && node.first_element_child.name == "dl" and
|
151
|
+
return reqt_dl(node.first_element_child, out)
|
152
|
+
node.name == "component" and
|
153
|
+
return recommendation_attributes1_component(node, out)
|
154
|
+
out.add_child("<tr><td colspan='2'></td></tr>").first
|
155
|
+
.at(ns(".//td")) <<
|
156
|
+
(preserve_in_nested_table?(node) ? node : node.children)
|
157
|
+
out
|
158
|
+
end
|
159
|
+
|
160
|
+
def reqt_dl(node, out)
|
161
|
+
node.xpath(ns("./dt")).each do |dt|
|
162
|
+
dd = dt.next_element
|
163
|
+
dd&.name == "dd" or next
|
164
|
+
out.add_child("<tr><td>#{dt.children.to_xml}</td>"\
|
165
|
+
"<td>#{dd.children.to_xml}</td></tr>")
|
166
|
+
end
|
167
|
+
out
|
168
|
+
end
|
169
|
+
|
170
|
+
def requirement_table_cleanup(table)
|
171
|
+
return table unless table["type"] == "recommendclass"
|
172
|
+
|
173
|
+
table.xpath(ns("./tbody/tr/td/table")).each do |t|
|
174
|
+
t.xpath(ns("./thead | ./tbody |./tfoot")).each do |x|
|
175
|
+
x.replace(x.children)
|
176
|
+
end
|
177
|
+
(x = t.at(ns("./tr/th[@colspan = '2']"))) &&
|
178
|
+
(y = t.at(ns("./tr/td[@colspan = '2']"))) and
|
179
|
+
requirement_table_cleanup1(x, y)
|
180
|
+
t.parent.parent.replace(t.children)
|
181
|
+
end
|
182
|
+
table
|
183
|
+
end
|
184
|
+
|
185
|
+
# table nested in table: merge label and caption into a single row
|
186
|
+
def requirement_table_cleanup1(outer, inner)
|
187
|
+
outer.delete("colspan")
|
188
|
+
outer.delete("scope")
|
189
|
+
inner.delete("colspan")
|
190
|
+
inner.delete("scope")
|
191
|
+
outer.name = "td"
|
192
|
+
p = outer.at(ns("./p[@class = 'RecommendationTitle']")) and
|
193
|
+
p.delete("class")
|
194
|
+
outer.parent << inner.dup
|
195
|
+
inner.parent.remove
|
196
|
+
end
|
197
|
+
|
198
|
+
def rec_subj(node)
|
199
|
+
case node["type"]
|
200
|
+
when "class" then "Target type"
|
201
|
+
else "Subject"
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def rec_target(node)
|
206
|
+
case node["type"]
|
207
|
+
when "class" then "Target type"
|
208
|
+
when "conformanceclass" then "Requirements class"
|
209
|
+
when "verification", "abstracttest" then "Requirement"
|
210
|
+
else "Target"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def recommend_class(node)
|
215
|
+
case node["type"]
|
216
|
+
when "verification", "abstracttest" then "recommendtest"
|
217
|
+
when "class", "conformanceclass" then "recommendclass"
|
218
|
+
else "recommend"
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def recommend_name_class(node)
|
223
|
+
if %w(verification abstracttest).include?(node["type"])
|
224
|
+
"RecommendationTestTitle"
|
225
|
+
else "RecommendationTitle"
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def recommend_component_label(node)
|
230
|
+
case node["class"]
|
231
|
+
when "test-purpose" then "Test purpose"
|
232
|
+
when "test-method" then "Test method"
|
233
|
+
else Metanorma::Utils.strict_capitalize_first(node["class"])
|
234
|
+
end
|
235
|
+
end
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Metanorma
|
2
|
+
class Requirements
|
3
|
+
class Modspec < Default
|
4
|
+
def recommendation_label(elem, type, xrefs)
|
5
|
+
label = elem.at(ns("./identifier"))&.text
|
6
|
+
if inject_crossreference_reqt?(elem, label)
|
7
|
+
number = xrefs.anchor(reqtlabels(elem.document, label), :xref, false)
|
8
|
+
number.nil? ? type : number
|
9
|
+
else
|
10
|
+
type = recommendation_class_label(elem)
|
11
|
+
super
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def reqtlabels(doc, label)
|
16
|
+
@reqtlabels ||= doc
|
17
|
+
.xpath(ns("//requirement | //recommendation | //permission"))
|
18
|
+
.each_with_object({}) do |r, m|
|
19
|
+
l = r.at(ns("./label"))&.text and m[l] = r["id"]
|
20
|
+
end
|
21
|
+
@reqtlabels[label]
|
22
|
+
end
|
23
|
+
|
24
|
+
# embedded reqts xref to top level reqts via label lookup
|
25
|
+
def inject_crossreference_reqt?(node, label)
|
26
|
+
!node.ancestors("requirement, recommendation, permission").empty? &&
|
27
|
+
reqtlabels(node.document, label)
|
28
|
+
end
|
29
|
+
|
30
|
+
def recommendation_class_label(node)
|
31
|
+
case node["type"]
|
32
|
+
when "verification" then @labels["#{node.name}test"]
|
33
|
+
when "class" then @labels["#{node.name}class"]
|
34
|
+
when "abstracttest" then @labels["abstracttest"]
|
35
|
+
when "conformanceclass" then @labels["conformanceclass"]
|
36
|
+
else
|
37
|
+
case node.name
|
38
|
+
when "recommendation" then @labels["recommendation"]
|
39
|
+
when "requirement" then @labels["requirement"]
|
40
|
+
when "permission" then @labels["permission"]
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def reqt_ids(docxml)
|
46
|
+
docxml.xpath(ns("//requirement | //recommendation | //permission"))
|
47
|
+
.each_with_object({}) do |r, m|
|
48
|
+
id = r.at(ns("./identifier")) or next
|
49
|
+
m[id.text] = r["id"]
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def reqt_links(docxml)
|
54
|
+
docxml.xpath(ns("//requirement | //recommendation | //permission"))
|
55
|
+
.each_with_object({}) do |r, m|
|
56
|
+
next unless %w(conformanceclass
|
57
|
+
verification).include?(r["type"])
|
58
|
+
|
59
|
+
subj = r.at(ns("./classification[tag = 'target']/value"))
|
60
|
+
id = r.at(ns("./identifier"))
|
61
|
+
next unless subj && id
|
62
|
+
|
63
|
+
m[subj.text] = { lbl: id.text, id: r["id"] }
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def recommendation_link(docxml, ident)
|
68
|
+
@reqt_links ||= reqt_links(docxml)
|
69
|
+
test = @reqt_links[ident&.strip] or return nil
|
70
|
+
"<xref target='#{test[:id]}'>#{test[:lbl]}</xref>"
|
71
|
+
end
|
72
|
+
|
73
|
+
def recommendation_id(docxml, ident)
|
74
|
+
@reqt_ids ||= reqt_ids(docxml)
|
75
|
+
test = @reqt_ids[ident&.strip] or return ident&.strip
|
76
|
+
"<xref target='#{test}'>#{ident.strip}</xref>"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Metanorma
|
2
|
+
class Requirements
|
3
|
+
class Modspec < Default
|
4
|
+
def req_class_paths
|
5
|
+
[
|
6
|
+
{ klass: "permissionclass", label: "permissionclass",
|
7
|
+
xpath: "permission[@type = 'class']" },
|
8
|
+
{ klass: "requirementclass", label: "requirementclass",
|
9
|
+
xpath: "requirement[@type = 'class']" },
|
10
|
+
{ klass: "recommendationclass", label: "recommendationclass",
|
11
|
+
xpath: "recommendation[@type = 'class']" },
|
12
|
+
{ klass: "permissiontest", label: "permissiontest",
|
13
|
+
xpath: "permission[@type = 'verification']" },
|
14
|
+
{ klass: "recommendationtest", label: "recommendationtest",
|
15
|
+
xpath: "recommendation[@type = 'verification']" },
|
16
|
+
{ klass: "requirementtest", label: "requirementtest",
|
17
|
+
xpath: "requirement[@type = 'verification']" },
|
18
|
+
{ klass: "abstracttest", label: "abstracttest",
|
19
|
+
xpath: "permission[@type = 'abstracttest']" },
|
20
|
+
{ klass: "abstracttest", label: "abstracttest",
|
21
|
+
xpath: "requirement[@type = 'abstracttest']" },
|
22
|
+
{ klass: "abstracttest", label: "abstracttest",
|
23
|
+
xpath: "recommendation[@type = 'abstracttest']" },
|
24
|
+
{ klass: "conformanceclass", label: "conformanceclass",
|
25
|
+
xpath: "permission[@type = 'conformanceclass']" },
|
26
|
+
{ klass: "conformanceclass", label: "conformanceclass",
|
27
|
+
xpath: "requirement[@type = 'conformanceclass']" },
|
28
|
+
{ klass: "conformanceclass", label: "conformanceclass",
|
29
|
+
xpath: "recommendation[@type = 'conformanceclass']" },
|
30
|
+
{ klass: "permission", label: "permission",
|
31
|
+
xpath: "permission[not(@type = 'verification' or @type = 'class' "\
|
32
|
+
"or @type = 'abstracttest' or @type = 'conformanceclass')]" },
|
33
|
+
{ klass: "recommendation", label: "recommendation",
|
34
|
+
xpath: "recommendation[not(@type = 'verification' or "\
|
35
|
+
"@type = 'class' or @type = 'abstracttest' or "\
|
36
|
+
"@type = 'conformanceclass')]" },
|
37
|
+
{ klass: "requirement", label: "requirement",
|
38
|
+
xpath: "requirement[not(@type = 'verification' or @type = 'class' "\
|
39
|
+
"or @type = 'abstracttest' or @type = 'conformanceclass')]" },
|
40
|
+
]
|
41
|
+
end
|
42
|
+
|
43
|
+
def req_nested_class_paths
|
44
|
+
req_class_paths
|
45
|
+
end
|
46
|
+
|
47
|
+
def permission_parts(block, block_id, label, klass)
|
48
|
+
block.xpath(ns("./component[@class = 'part']"))
|
49
|
+
.each_with_index.with_object([]) do |(c, i), m|
|
50
|
+
next if c["id"].nil? || c["id"].empty?
|
51
|
+
|
52
|
+
m << { id: c["id"], number: l10n("#{block_id} #{(i + 'A'.ord).chr}"),
|
53
|
+
elem: c, label: label, klass: klass }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def postprocess_anchor_struct(block, anchor)
|
58
|
+
super
|
59
|
+
if l = block.at(ns("./identifier"))&.text
|
60
|
+
anchor[:xref] += l10n(": ") + "<tt>#{l}</tt>"
|
61
|
+
end
|
62
|
+
anchor
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require_relative "../default/default"
|
2
|
+
require_relative "../modspec/modspec"
|
3
|
+
require "isodoc-i18n"
|
4
|
+
|
5
|
+
module Metanorma
|
6
|
+
class Requirements
|
7
|
+
attr_accessor :i18n, :labels
|
8
|
+
|
9
|
+
def initialize(options)
|
10
|
+
@default = options[:default]
|
11
|
+
@i18n = ::IsoDoc::I18n.new(options[:lang] || "en",
|
12
|
+
options[:script] || "Latn")
|
13
|
+
@labels = options[:labels]
|
14
|
+
@models = {}
|
15
|
+
model_names.each { |k| @models[k] = create(k) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def model_names
|
19
|
+
%i[default ogc]
|
20
|
+
end
|
21
|
+
|
22
|
+
# all roles that can be assigned to an example to make it a reqt,
|
23
|
+
# across all models (because the model may not be an attribute but
|
24
|
+
# embedded in the definition list). Mapped to obligation
|
25
|
+
# TODO may need to make it conditional on model
|
26
|
+
def requirement_roles
|
27
|
+
{
|
28
|
+
recommendation: "recommendation",
|
29
|
+
requirement: "requirement",
|
30
|
+
permission: "permission",
|
31
|
+
requirements_class: "requirement",
|
32
|
+
conformance_test: "requirement",
|
33
|
+
conformance_class: "requirement",
|
34
|
+
abstract_test: "requirement",
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def create(type)
|
39
|
+
case type
|
40
|
+
when :modspec, :ogc
|
41
|
+
Metanorma::Requirements::Modspec.new(parent: self)
|
42
|
+
else Metanorma::Requirements::Default.new(parent: self)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def model(type)
|
47
|
+
@models[type&.to_sym] || @models[@default]
|
48
|
+
end
|
49
|
+
|
50
|
+
REQRECPER = "//requirement | //recommendation | //permission".freeze
|
51
|
+
|
52
|
+
# all cleanup steps by all possible models are included here, and each model
|
53
|
+
# can skip a given step. This class iterates through the entire document,
|
54
|
+
# and picks the model for each requirement; then that model's method is
|
55
|
+
# applied to that particular requirement instance
|
56
|
+
def requirement_cleanup(xmldoc)
|
57
|
+
requirement_metadata_cleanup(xmldoc)
|
58
|
+
requirement_type_cleanup(xmldoc)
|
59
|
+
requirement_inherit_cleanup(xmldoc)
|
60
|
+
requirement_descriptions_cleanup(xmldoc)
|
61
|
+
requirement_identifier_cleanup(xmldoc)
|
62
|
+
end
|
63
|
+
|
64
|
+
def requirement_type_cleanup(xmldoc)
|
65
|
+
xmldoc.xpath(REQRECPER).each do |r|
|
66
|
+
model(r["model"]).requirement_type_cleanup(r)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def requirement_metadata_cleanup(xmldoc)
|
71
|
+
xmldoc.xpath(REQRECPER).each do |r|
|
72
|
+
model(r["model"]).requirement_metadata_cleanup(r)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def requirement_inherit_cleanup(xmldoc)
|
77
|
+
xmldoc.xpath(REQRECPER).each do |r|
|
78
|
+
model(r["model"]).requirement_inherit_cleanup(r)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def requirement_descriptions_cleanup(xmldoc)
|
83
|
+
xmldoc.xpath(REQRECPER).each do |r|
|
84
|
+
model(r["model"]).requirement_descriptions_cleanup(r)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def requirement_identifier_cleanup(xmldoc)
|
89
|
+
xmldoc.xpath(REQRECPER).each do |r|
|
90
|
+
model(r["model"]).requirement_identifier_cleanup(r)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require "metanorma/requirements/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "mn-requirements"
|
7
|
+
spec.version = Metanorma::Requirements::VERSION
|
8
|
+
spec.authors = ["Ribose Inc."]
|
9
|
+
spec.email = ["open.source@ribose.com"]
|
10
|
+
|
11
|
+
spec.summary = "Requirements processing and rendering according to different models"
|
12
|
+
spec.description = <<~DESCRIPTION
|
13
|
+
Requirements processing and rendering according to different models
|
14
|
+
DESCRIPTION
|
15
|
+
|
16
|
+
spec.homepage = "https://github.com/metanorma/mn-requirements"
|
17
|
+
spec.license = "BSD-2-Clause"
|
18
|
+
|
19
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
20
|
+
f.match(%r{^(test|spec|features)/})
|
21
|
+
end
|
22
|
+
spec.bindir = "exe"
|
23
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
24
|
+
spec.require_paths = ["lib"]
|
25
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
|
26
|
+
|
27
|
+
spec.add_dependency "isodoc-i18n", "~> 1.0.0"
|
28
|
+
spec.add_dependency "metanorma-utils", "~> 1.3.0"
|
29
|
+
|
30
|
+
spec.add_development_dependency "debug"
|
31
|
+
spec.add_development_dependency "equivalent-xml", "~> 0.6"
|
32
|
+
spec.add_development_dependency "guard", "~> 2.14"
|
33
|
+
spec.add_development_dependency "guard-rspec", "~> 4.7"
|
34
|
+
spec.add_development_dependency "isodoc", "~> 2"
|
35
|
+
spec.add_development_dependency "metanorma-standoc", "~> 2"
|
36
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
37
|
+
spec.add_development_dependency "rspec", "~> 3.6"
|
38
|
+
spec.add_development_dependency "rubocop", "~> 1.5.2"
|
39
|
+
spec.add_development_dependency "sassc", "2.4.0"
|
40
|
+
spec.add_development_dependency "simplecov", "~> 0.15"
|
41
|
+
spec.add_development_dependency "timecop", "~> 0.9"
|
42
|
+
end
|