relaton-cli 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,15 @@
1
+ # This project follows the Ribose OSS style guide.
2
+ # https://github.com/riboseinc/oss-guides
3
+ # All project-specific additions and overrides should be specified in this file.
4
+
5
+ inherit_from:
6
+ # Thoughtbot's style guide from: https://github.com/thoughtbot/guides
7
+ - ".rubocop.tb.yml"
8
+ # Overrides from Ribose
9
+ - ".rubocop.ribose.yml"
10
+ AllCops:
11
+ DisplayCopNames: false
12
+ StyleGuideCopsOnly: false
13
+ TargetRubyVersion: 2.5
14
+ Rails:
15
+ Enabled: true
@@ -0,0 +1,14 @@
1
+ dist: trusty
2
+ sudo: false
3
+ language: ruby
4
+ rvm:
5
+ - 2.5
6
+ - 2.4
7
+ - 2.3
8
+ - ruby-head
9
+ before_install:
10
+ - gem install bundler -v 1.16.1
11
+ - unset _JAVA_OPTIONS
12
+ matrix:
13
+ allow_failures:
14
+ - rvm: ruby-head
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ Encoding.default_external = Encoding::UTF_8
2
+ Encoding.default_internal = Encoding::UTF_8
3
+
4
+ source "https://rubygems.org"
5
+
6
+ # Specify your gem's dependencies in gemspec
7
+ gemspec
8
+
data/LICENSE ADDED
@@ -0,0 +1,25 @@
1
+ BSD 2-Clause License
2
+
3
+ Copyright (c) 2018, Ribose
4
+ All rights reserved.
5
+
6
+ Redistribution and use in source and binary forms, with or without
7
+ modification, are permitted provided that the following conditions are met:
8
+
9
+ * Redistributions of source code must retain the above copyright notice, this
10
+ list of conditions and the following disclaimer.
11
+
12
+ * Redistributions in binary form must reproduce the above copyright notice,
13
+ this list of conditions and the following disclaimer in the documentation
14
+ and/or other materials provided with the distribution.
15
+
16
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17
+ AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19
+ DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
20
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
22
+ SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
23
+ CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
@@ -0,0 +1,9 @@
1
+ = Relaton CLI (relaton-cli): Relaton Command-line Interface
2
+
3
+ image:https://img.shields.io/gem/v/relaton-cli.svg["Gem Version", link="https://rubygems.org/gems/relaton-cli"]
4
+ image:https://img.shields.io/travis/riboseinc/relaton-cli/master.svg["Build Status", link="https://travis-ci.org/riboseinc/relaton-cli"]
5
+ image:https://codeclimate.com/github/riboseinc/relaton-cli/badges/gpa.svg["Code Climate", link="https://codeclimate.com/github/riboseinc/relaton-cli"]
6
+
7
+ Documentation in development.
8
+
9
+ Please refer to https://github.com/riboseinc/relaton.
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env ruby
2
+ # Invoke as: ./relaton-doc FILENAME STYLESHEET RELATON-ROOT
3
+ require "nokogiri"
4
+ require "relaton/cli"
5
+
6
+ unless ARGV.size == 3
7
+ warn "Invoke as: ./relaton-doc <relaton-xml> <stylesheet> <output-root>"
8
+ exit
9
+ end
10
+
11
+ filename = ARGV[0]
12
+ stylesheet = ARGV[1]
13
+ relaton_root = ARGV[2] || "relaton"
14
+
15
+ file = File.read(ARGV[0], encoding: "utf-8")
16
+
17
+ xml_to_html = Relaton::Cli::XmlToHtmlRenderer.new
18
+
19
+ File.open(filename.sub(/\.xml$/, ".html"), "w:UTF-8") do |f|
20
+ f.write(xml_to_html.render(file, stylesheet, relaton_root))
21
+ end
22
+
@@ -0,0 +1,83 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "yaml"
4
+ require "optparse"
5
+ # require "metanorma"
6
+ # require "byebug"
7
+ require "fileutils"
8
+
9
+
10
+ options = {}
11
+
12
+ opt_parser = OptionParser.new do |opts|
13
+ opts.banner += " <file>"
14
+ opts.on(
15
+ '-t',
16
+ '--type TYPE',
17
+ "Type of standard to generate"
18
+ ) { |v| options[:type] = v.to_sym }
19
+
20
+ opts.on(
21
+ '-r',
22
+ '--require LIBRARY',
23
+ 'Require LIBRARY prior to execution'
24
+ ) { |v|
25
+ options[:require] ||= []
26
+ options[:require] << v
27
+ }
28
+
29
+ opts.on(
30
+ '-R OUTDIR',
31
+ '--relaton-outdir OUTDIR',
32
+ "Create Relaton XML per item, within this output directory (e.g. {outdir}/{docid}.xml)"
33
+ ) { |v| options[:outdir] = v }
34
+
35
+ opts.on_tail("-h", "--help", "Show this message") do
36
+ puts opts
37
+ exit
38
+ end
39
+ end
40
+
41
+ opt_parser.parse!(ARGV)
42
+ options[:filename] = ARGV.pop
43
+
44
+ if options[:require]
45
+ options[:require].each do |r|
46
+ require r
47
+ end
48
+ end
49
+
50
+ index_input = YAML.load_file(options[:filename])
51
+ index_collection = Bibcollection.new(index_input["root"])
52
+
53
+ # TODO real lookup of namespaces and root elements
54
+
55
+ # Write out index.html
56
+ outfilename = options[:filename].sub(/\.[^.]+$/, ".xml")
57
+ File.open(outfilename, "w") do |f|
58
+ f.write index_collection.to_xml
59
+ end
60
+
61
+ outdir = options[:outdir] || "relaton"
62
+
63
+ FileUtils.mkdir_p(outdir)
64
+ # Write out relaton/#{id}.xml
65
+ index_collection.items_flattened.each do |item|
66
+ filename = File.join(outdir, "#{item.docid_code}.xml")
67
+
68
+ File.open(filename, "w:UTF-8") do |f|
69
+ f.write(item.to_xml)
70
+ end
71
+
72
+ end
73
+
74
+ # cmd = "#{File.dirname(__FILE__)}/relaton-doc #{outfilename} #{ manifest["htmlstylesheet"] || '""' } #{ options[:relaton] || '""' }"
75
+ # system cmd
76
+
77
+ #processor = registry.find_processor(options[:type].to_sym)
78
+ #ext = :html
79
+ #file_extension = "html" || processor.output_formats[ext]
80
+ #outfilename = options[:filename].sub(/\.[^.]+$/, ".#{file_extension}")
81
+ #isodoc_options = { suppressheadingnumbers: true }
82
+ #isodoc_options[:htmlstylesheet] = manifest["htmlstylesheet"] if manifest["htmlstylesheet"]
83
+ #processor.output(out, outfilename, ext, isodoc_options)
@@ -0,0 +1 @@
1
+ label: label
@@ -0,0 +1,77 @@
1
+ module Relaton
2
+ class Bibcollection
3
+ ATTRIBS = %i[
4
+ title
5
+ items
6
+ doctype
7
+ ]
8
+
9
+ attr_accessor *ATTRIBS
10
+
11
+ def initialize(options)
12
+ self.items = []
13
+ ATTRIBS.each do |k|
14
+ method = "#{k}="
15
+ value = options[k.to_s]
16
+ # puts "K #{method}"
17
+ # puts value.inspect
18
+
19
+ self.send("#{k}=", options[k.to_s])
20
+ # puts "SET! to #{self.send(k).inspect}"
21
+ end
22
+
23
+ # puts items.inspect
24
+ self.items = self.items.inject([]) do |acc,item|
25
+ acc << if item.is_a?(Bibcollection) || item.is_a?(Bibdata)
26
+ item
27
+ else
28
+ # puts "item.inspect #{item.inspect}"
29
+ new_bib_item_class(item)
30
+ end
31
+ end
32
+
33
+ self
34
+ # byebug
35
+ end
36
+
37
+ def new_bib_item_class(options)
38
+ if options["items"]
39
+ Bibcollection.new(options)
40
+ else
41
+ Bibdata.new(options)
42
+ end
43
+ end
44
+
45
+ def items_flattened
46
+
47
+ items.inject([]) do |acc,item|
48
+ if item.is_a? Bibcollection
49
+ acc << item.items_flattened
50
+ else
51
+ acc << item
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ def to_xml
58
+ collection_type = if doctype
59
+ "type=\"#{doctype}\""
60
+ else
61
+ 'xmlns="http://riboseinc.com/isoxml"'
62
+ end
63
+
64
+ ret = "<relaton-collection #{collection_type}>"
65
+ ret += "<title>#{title}</title>" if title
66
+ unless items.empty?
67
+ items.each do |item|
68
+ ret += "<relation type='partOf'>"
69
+ ret += item.to_xml
70
+ ret += "</relation>\n"
71
+ end
72
+ end
73
+ ret += "</relaton-collection>\n"
74
+ end
75
+
76
+ end
77
+ end
@@ -0,0 +1,40 @@
1
+ module Relaton
2
+ class Bibdata
3
+ attr_accessor *%i[
4
+ docid
5
+ doctype
6
+ title
7
+ stage
8
+ relation
9
+ uri
10
+ revdate
11
+ abstract
12
+ technical_committee
13
+ ]
14
+
15
+ def initialize(options)
16
+ options.each_pair do |k,v|
17
+ send("#{k.to_s}=", v)
18
+ end
19
+ end
20
+
21
+ def docid_code
22
+ docid.downcase.gsub(/[\s\/]/, "-") || ""
23
+ end
24
+
25
+ def to_xml
26
+ datetype = stage.casecmp("published") == 0 ? "published" : "updated"
27
+
28
+ ret = "<bibdata type='#{doctype}'>\n"
29
+ ret += "<title>#{title}</title>\n"
30
+ ret += "<uri>#{uri}</uri>\n"
31
+ ret += "<docidentifier>#{docid}</docidentifier>\n"
32
+ ret += "<date type='#{datetype}'><on>#{revdate}</on></date>\n" if revdate
33
+ ret += "<abstract>#{abstract}</abstract>\n" if abstract
34
+ ret += "<status>#{stage}</status>\n" if stage
35
+ ret += "<technical-committee>#{technical_committee}</technical-committee>\n" if technical_committee
36
+ ret += "</bibdata>\n"
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,4 @@
1
+ require "relaton"
2
+ require_relative "bibcollection.rb"
3
+ require_relative "bibdata"
4
+ require_relative "cli/xml_to_html_renderer"
@@ -0,0 +1,5 @@
1
+ module Relaton
2
+ module Cli
3
+ VERSION = "0.1.0".freeze
4
+ end
5
+ end
@@ -0,0 +1,279 @@
1
+
2
+ module Relaton::Cli
3
+ class XmlToHtmlRenderer
4
+
5
+ # require "byebug"; byebug
6
+ def ns(xpath)
7
+ xpath.gsub(%r{/([a-zA-z])}, "/xmlns:\\1").
8
+ gsub(%r{::([a-zA-z])}, "::xmlns:\\1").
9
+ gsub(%r{\[([a-zA-z][a-z0-9A-Z@/]* ?=)}, "[xmlns:\\1").
10
+ gsub(%r{\[([a-zA-z][a-z0-9A-Z@/]*\])}, "[xmlns:\\1")
11
+ end
12
+
13
+ NOKOHEAD = <<~HERE.freeze
14
+ <!DOCTYPE html SYSTEM "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
15
+ <html xmlns="http://www.w3.org/1999/xhtml">
16
+ <head></head>
17
+ <body></body>
18
+ </html>
19
+ HERE
20
+
21
+ # block for processing XML document fragments as XHTML,
22
+ # to allow for HTMLentities
23
+ def noko(&block)
24
+ doc = ::Nokogiri::XML.parse(NOKOHEAD)
25
+ fragment = doc.fragment("")
26
+ ::Nokogiri::XML::Builder.with fragment, &block
27
+ fragment.to_xml(encoding: "US-ASCII").lines.map do |l|
28
+ l.gsub(/\s*\n/, "")
29
+ end
30
+ end
31
+
32
+ def script_cdata(result)
33
+ result.gsub(%r{<script>\s*<!\[CDATA\[}m, "<script>").
34
+ gsub(%r{\]\]>\s*</script>}, "</script>").
35
+ gsub(%r{<!\[CDATA\[\s*<script>}m, "<script>").
36
+ gsub(%r{</script>\s*\]\]>}, "</script>")
37
+ end
38
+
39
+ def render(file_content, css_path, relaton_root)
40
+ doc = Nokogiri::XML(file_content)
41
+ stylesheet = File.read(css_path)
42
+
43
+ result = noko do |xml|
44
+ xml.html do |html|
45
+ define_head(
46
+ html,
47
+ stylesheet,
48
+ doc&.at(ns("./relaton-collection/title"))&.text || "Untitled"
49
+ )
50
+ make_body html, doc, relaton_root
51
+ end
52
+ end.join("\n")
53
+
54
+ script_cdata(result)
55
+ end
56
+
57
+ def define_head(html, stylesheet, title)
58
+ html.head do |head|
59
+ head.title { |t| t << title }
60
+ head.style do |style|
61
+ style.comment "\n#{stylesheet}\n"
62
+ end
63
+ head.meta **{ "http-equiv": "Content-Type", content: "text/html", charset: "utf-8" }
64
+
65
+ [
66
+ "https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js",
67
+ "https://cdn.rawgit.com/jgallen23/toc/0.3.2/dist/toc.min.js"
68
+ ].each do |url|
69
+ head.script **{ src: url, type: "text/javascript" } do |s|
70
+ s.text nil
71
+ end
72
+ end
73
+
74
+ [
75
+ {
76
+ href: "https://fonts.googleapis.com/css?family=Open+Sans:300,300i,400,400i,600,600i|Space+Mono:400,700|Overpass:300,300i,600,900|Ek+Mukta:200",
77
+ rel: "stylesheet",
78
+ type: "text/css"
79
+ },
80
+ {
81
+ href: "https://use.fontawesome.com/releases/v5.0.8/css/solid.css",
82
+ integrity: "sha384-v2Tw72dyUXeU3y4aM2Y0tBJQkGfplr39mxZqlTBDUZAb9BGoC40+rdFCG0m10lXk",
83
+ crossorigin: "anonymous"
84
+ },
85
+ {
86
+ href: "https://use.fontawesome.com/releases/v5.0.8/css/fontawesome.css",
87
+ integrity: "sha384-q3jl8XQu1OpdLgGFvNRnPdj5VIlCvgsDQTQB6owSOHWlAurxul7f+JpUOVdAiJ5P",
88
+ crossorigin: "anonymous"
89
+ }
90
+
91
+ ].each do |attribs|
92
+ head.link **attribs
93
+ end
94
+
95
+ end
96
+ end
97
+
98
+ def make_body(html, xml, relaton_root)
99
+ # require "byebug"; byebug
100
+ body_attr = { lang: "EN-US", link: "blue", vlink: "#954F72" }
101
+ html.body **body_attr do |body|
102
+ make_body1(body, xml)
103
+ make_body2(body, xml)
104
+ make_body3(body, xml, relaton_root)
105
+ scripts(body)
106
+ end
107
+ end
108
+
109
+ def make_body1(body, docxml)
110
+ body.div **{ class: "title-section" } do |div1|
111
+ div1 << <<~END
112
+ <header>
113
+ <div class="coverpage">
114
+ <div class="wrapper-top">
115
+ <div class="coverpage-doc-identity">
116
+ <div class="coverpage-title">
117
+ <span class="title-first">#{docxml&.at(ns("./relaton-collection/title"))&.text}</span>
118
+ </div>
119
+ </div>
120
+ </div>
121
+ <div>
122
+ </header>
123
+ END
124
+ end
125
+ end
126
+
127
+ def make_body2(body, docxml)
128
+ body.div **{ class: "prefatory-section" } do |div2|
129
+ div2.p { |p| p << "&nbsp;" } # placeholder
130
+ end
131
+ end
132
+
133
+ def make_body3(body, docxml, relaton_root)
134
+ # require "byebug"; byebug
135
+ body.main **{ class: "main-section" } do |div3|
136
+ docxml.xpath(ns("./relaton-collection/relation")).each do |x|
137
+ iterate(div3, x.at(ns("./bibdata | ./relaton-collection")), 2, relaton_root)
138
+ end
139
+ end
140
+
141
+ body.div **{ class: "copyright" } do |div1|
142
+ div1 << <<~END
143
+ <p class="year">
144
+ © The Calendaring and Scheduling Consortium, Inc.
145
+ </p>
146
+ <p class="message">
147
+ All rights reserved. Unless otherwise specified, no part of this publication may be reproduced or utilized otherwise in any form or by any means, electronic or mechanical, including photocopying, or posting on the internet or an intranet, without prior written permission. Permission can be requested from the address below.
148
+ </p>
149
+ END
150
+ end
151
+
152
+ end
153
+
154
+ def script
155
+ <<~"END"
156
+ <script>
157
+ $(document).ready(function() {
158
+ $('[id^=toc]').each(function () {
159
+ var currentToc = $(this);
160
+ var url = window.location.href;
161
+ currentToc.wrap("<a href='" + url + "#" + currentToc.attr("id") + "' <\/a>");
162
+ });
163
+ });
164
+ anchors.options = { placement: 'left' };
165
+ anchors.add('h1, h2, h3, h4');
166
+ </script>
167
+ END
168
+ end
169
+
170
+ def scripts(body)
171
+ body.parent.add_child script
172
+ end
173
+
174
+ EXTENSION_TYPES = [
175
+ {
176
+ text: "HTML",
177
+ extension: ".html"
178
+ },
179
+ {
180
+ text: "PDF",
181
+ extension: ".pdf"
182
+ },
183
+ {
184
+ text: "Word",
185
+ extension: ".doc"
186
+ },
187
+ {
188
+ text: "XML",
189
+ extension: ".xml"
190
+ }
191
+ ]
192
+
193
+ def uri_for_extension(uri, extension)
194
+ uri.sub(/\.[^.]+$/, extension.to_s)
195
+ end
196
+
197
+ def iterate(d0, bib, depth, relaton_root)
198
+ # require "byebug"; byebug
199
+ uri = bib.at(ns("./uri"))&.text
200
+ id = bib.at(ns("./docidentifier"))&.text
201
+ id_code = id.downcase.gsub(/[\s\/]/, "-") unless id.nil?
202
+ title = bib.at(ns("./title"))&.text
203
+
204
+ d0.div **{ class: bib.name == "bibdata" ? "document" : "doc-section" } do |d|
205
+ d.div **{ class: "doc-line" } do |d1|
206
+
207
+ d1.div **{ class: "doc-identifier" } do |d2|
208
+ d2.send "h#{depth}" do |h|
209
+ if uri
210
+ h.a **{ href: uri_for_extension(uri, :html) } do |a|
211
+ a << id
212
+ end
213
+ else
214
+ h << id
215
+ end
216
+ end
217
+ end
218
+
219
+ d1.div **{ class: "doc-type-wrap" } do |d2|
220
+ d2.div bib.at(ns("./@type"))&.text, **{ class: "doc-type #{bib.at(ns("./@type"))&.text&.downcase}" }
221
+ end
222
+ end
223
+
224
+ d.div **{ class: "doc-title" } do |d1|
225
+ d1.send "h#{depth+1}" do |h|
226
+
227
+ if uri
228
+ h.a **{ href: uri_for_extension(uri, :html) } do |a|
229
+ a << title
230
+ end
231
+ else
232
+ h << title
233
+ end
234
+ end
235
+ end
236
+
237
+ d.div **{ class: "doc-info #{bib.at(ns("./status"))&.text&.downcase}" } do |d1|
238
+ d1.div bib.at(ns("./status"))&.text, **{ class: "doc-stage #{bib.at(ns("./status"))&.text&.downcase}" }
239
+ d1.div **{ class: "doc-dates" } do |d2|
240
+ if bib.at(ns("./date[@type = 'published']/on"))
241
+ d2.div bib.at(ns("./date[@type = 'published']/on"))&.text, **{ class: "doc-published" }
242
+ end
243
+ if bib.at(ns("./date[@type = 'updated']/on"))
244
+ d2.div bib.at(ns("./date[@type = 'updated']/on"))&.text, **{ class: "doc-updated" }
245
+ end
246
+ end
247
+ end
248
+
249
+ if id
250
+ d.div **{ class: "doc-bib" } do |d1|
251
+ d1.div **{ class: "doc-bib-relaton" } do |d2|
252
+ d2.a **{ href: URI.escape("#{relaton_root}/id/#{id_code}.xml") } do |a|
253
+ a << "Relaton XML"
254
+ end
255
+ end
256
+ end
257
+ end
258
+
259
+ if uri
260
+ d.div **{ class: "doc-access" } do |d1|
261
+
262
+ EXTENSION_TYPES.each do |attribs|
263
+ d1.div **{ class: "doc-access-button-#{attribs[:extension]}" } do |d2|
264
+ d2.a **{ href: uri_for_extension(uri, attribs[:extension]) } do |a|
265
+ a << attribs[:text]
266
+ end
267
+ end
268
+ end
269
+ end
270
+ end
271
+
272
+ bib.xpath(ns("./relation")).each do |x|
273
+ iterate(d, x.at(ns("./bibdata | ./relaton-collection")), depth + 1, relaton_root)
274
+ end
275
+ end
276
+ end
277
+
278
+ end
279
+ end