Skip to content

Commit

Permalink
#70 print html
Browse files Browse the repository at this point in the history
  • Loading branch information
yegor256 committed Jul 2, 2024
1 parent ea5fedb commit 645f5f0
Show file tree
Hide file tree
Showing 4 changed files with 276 additions and 7 deletions.
222 changes: 222 additions & 0 deletions assets/index.xsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
MIT License
Copyright (c) 2024 Zerocracy
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
-->
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:output method="xml" doctype-system="about:legacy-compat" encoding="UTF-8" indent="yes"/>
<xsl:param name="name"/>
<xsl:param name="date"/>
<xsl:param name="version"/>
<xsl:param name="columns"/>
<xsl:template name="javascript">
<xsl:param name="url"/>
<script type="text/javascript" src="{$url}">
<xsl:text> </xsl:text>
</script>
</xsl:template>
<xsl:template match="/">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<title>
<xsl:value-of select="$name"/>
</title>
<meta charset="UTF-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<link rel="icon" href="https://www.zerocracy.com/svg/logo.svg" type="image/svg"/>
<link href="https://cdn.jsdelivr.net/gh/yegor256/tacit@gh-pages/tacit-css.min.css" rel="stylesheet"/>
<link href="https://cdn.jsdelivr.net/gh/yegor256/drops@gh-pages/drops.min.css" rel="stylesheet"/>
<xsl:call-template name="javascript">
<xsl:with-param name="url">https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js</xsl:with-param>
</xsl:call-template>
<xsl:call-template name="javascript">
<xsl:with-param name="url">https://cdnjs.cloudflare.com/ajax/libs/jquery.tablesorter/2.31.3/js/jquery.tablesorter.min.js</xsl:with-param>
</xsl:call-template>
<style>
* { font-family: monospace; }
section { width: 100%; }
article { border: none; }
header img { width: 3em; height: 3em; }
.sorter { cursor: pointer; }
</style>
<script type="text/javascript">
$(function() {
$("#facts").tablesorter();
});
</script>
</head>
<body>
<section>
<header>
<p>
<a href="https://www.zerocracy.com">
<img src="https://www.zerocracy.com/svg/logo.svg" alt="Zerocracy"/>
</a>
</p>
</header>
<article>
<xsl:apply-templates select="fb"/>
</article>
<footer class="smaller">
<p>
<xsl:text>The page was generated by the </xsl:text>
<a href="https://github.com/yegor256/judges">
<xsl:text>judges</xsl:text>
</a>
<xsl:text> tool (</xsl:text>
<xsl:value-of select="$version"/>
<xsl:text>) on </xsl:text>
<xsl:value-of select="$date"/>
<xsl:text>.</xsl:text>
<br/>
<a href="https://github.com/yegor256/factbase">
<xsl:text>Factbase</xsl:text>
</a>
<xsl:text>: </xsl:text>
<xsl:value-of select="count(fb/f)"/>
<xsl:text> fact</xsl:text>
<xsl:if test="count(fb/f) != 1">
<xsl:text>s</xsl:text>
</xsl:if>
<xsl:text>, </xsl:text>
<xsl:value-of select="fb/@size"/>
<xsl:text> bytes, version </xsl:text>
<xsl:value-of select="fb/@version"/>
<xsl:text>.</xsl:text>
</p>
</footer>
</section>
</body>
</html>
</xsl:template>
<xsl:template match="fb">
<table id="facts">
<thead>
<tr>
<xsl:call-template name="th">
<xsl:with-param name="cols" select="$columns"/>
</xsl:call-template>
</th>
</thead>
<tbody>
<xsl:apply-templates select="f"/>
</tbody>
</table>
</xsl:template>
<xsl:template match="f">
<tr>
<xsl:call-template name="td">
<xsl:with-param name="cols" select="$columns"/>
<xsl:with-param name="f" select="."/>
</xsl:call-template>
</tr>
</xsl:template>
<xsl:template name="th">
<xsl:param name="cols"/>
<xsl:choose>
<xsl:when test="string-length($cols) &gt; 0">
<th class="sorter">
<xsl:value-of select="substring-before(concat($cols, ','), ',')"/>
</th>
<xsl:call-template name="th">
<xsl:with-param name="cols" select="substring-after($cols, ',')"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<th>
<xsl:text>&nbsp;</xsl:text>
</th>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="td">
<xsl:param name="cols"/>
<xsl:param name="f"/>
<xsl:choose>
<xsl:when test="string-length($cols) &gt; 0">
<td>
<xsl:variable name="c" select="substring-before(concat($cols, ','), ',')"/>
<xsl:call-template name="value">
<xsl:with-param name="v" select="$f/*[name()=$c]"/>
</xsl:call-template>
</td>
<xsl:call-template name="td">
<xsl:with-param name="cols" select="substring-after($cols, ',')"/>
<xsl:with-param name="f" select="$f"/>
</xsl:call-template>
</xsl:when>
<xsl:otherwise>
<td>
<xsl:for-each select="$f/*">
<xsl:if test="string-length(substring-before(concat(',', $columns, ','), concat(name(), ','))) = 0">
<xsl:if test="position() &gt; 1">
<xsl:text>, </xsl:text>
</xsl:if>
<xsl:value-of select="name()"/>
<xsl:text>:</xsl:text>
<xsl:call-template name="value">
<xsl:with-param name="v" select="."/>
</xsl:call-template>
</xsl:if>
</xsl:for-each>
</td>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
<xsl:template name="value">
<xsl:param name="v"/>
<xsl:choose>
<xsl:when test="$v/v">
<xsl:text>[</xsl:text>
<xsl:for-each select="$v/v">
<xsl:if test="position() &gt; 1">
<xsl:text>, </xsl:text>
</xsl:if>
<xsl:call-template name="value">
<xsl:with-param name="v" select="."/>
</xsl:call-template>
</xsl:for-each>
<xsl:text>]</xsl:text>
</xsl:when>
<xsl:otherwise>
<span>
<xsl:attribute name="style">
<xsl:text>color:</xsl:text>
<xsl:choose>
<xsl:when test="$v/@t = 'S'">
<xsl:text>#196F3D</xsl:text>
</xsl:when>
<xsl:when test="$v/@t = 'T'">
<xsl:text>#2471A3</xsl:text>
</xsl:when>
<xsl:when test="$v/@t = 'I'">
<xsl:text>#212F3C</xsl:text>
</xsl:when>
</xsl:choose>
</xsl:attribute>
<xsl:value-of select="$v"/>
</span>
</xsl:otherwise>
</xsl:choose>
</xsl:template>
</xsl:stylesheet>
2 changes: 2 additions & 0 deletions bin/judges
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,8 @@ class App
c.switch([:auto], default_value: false)
c.desc 'Only the facts that match the expression are printed'
c.flag([:query], default_value: '(always)')
c.desc 'Comma separated list of columns to show in HTML output (no spaces!)'
c.flag([:columns], default_value: 'when,what')
c.desc 'Print even if target file already exists and is older than the factbase'
c.switch([:force], default_value: false)
c.action do |global, options, args|
Expand Down
30 changes: 27 additions & 3 deletions lib/judges/commands/print.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

require 'time'
require 'fileutils'
require 'factbase'
require 'nokogiri'
require_relative '../../judges'
require_relative '../../judges/impex'
require_relative '../../judges/elapsed'
Expand All @@ -41,10 +43,11 @@ def run(opts, args)
fb = Judges::Impex.new(@loog, f).import
fb.query("(not #{opts['query']})").delete! unless opts['query'].nil?
o = args[1]
fmt = opts['format']&.downcase
if o.nil?
raise 'Either provide output file name or use --auto' unless opts[:auto]
raise 'Either provide output file name or use --auto' unless opts['auto']
o = File.join(File.dirname(f), File.basename(f).gsub(/\.[^.]*$/, ''))
o = "#{o}.#{opts[:format]}"
o = "#{o}.#{fmt}"
end
FileUtils.mkdir_p(File.dirname(o))
if !opts['force'] && File.exist?(o)
Expand All @@ -56,7 +59,7 @@ def run(opts, args)
end
elapsed(@loog) do
output =
case opts[:format].downcase
case fmt
when 'yaml'
require 'factbase/to_yaml'
Factbase::ToYAML.new(fb).yaml
Expand All @@ -66,9 +69,30 @@ def run(opts, args)
when 'xml'
require 'factbase/to_xml'
Factbase::ToXML.new(fb).xml
when 'html'
to_html(opts, fb)
else
raise "Unknown format '#{fmt}'"
end
File.binwrite(o, output)
throw :"Factbase printed to #{o.to_rel} (#{File.size(o)} bytes)"
end
end

private

def to_html(opts, fb)
xslt = Nokogiri::XSLT(File.read(File.join(__dir__, '../../../assets/index.xsl')))
require 'factbase/to_xml'
xml = Factbase::ToXML.new(fb).xml
xslt.transform(
Nokogiri::XML(xml),
Nokogiri::XSLT.quote_params(
'name' => 'print',
'date' => Time.now.utc.iso8601,
'columns' => opts['columns'] || 'when,what',
'version' => Judges::VERSION
)
)
end
end
29 changes: 25 additions & 4 deletions test/commands/test_print.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
require 'loog'
require 'factbase'
require 'yaml'
require 'securerandom'
require_relative '../../lib/judges'
require_relative '../../lib/judges/commands/print'

Expand All @@ -38,21 +39,41 @@ def test_simple_print
fb = Factbase.new
fb.insert
File.binwrite(f, fb.export)
Judges::Print.new(Loog::NULL).run({ format: 'yaml', auto: true }, [f])
Judges::Print.new(Loog::NULL).run({ 'format' => 'yaml', 'auto' => true }, [f])
y = File.join(d, 'base.yaml')
assert(File.exist?(y))
assert_equal(1, YAML.load_file(y).size)
end
end

def test_print_to_html
fb = Factbase.new
10.times do
f = fb.insert
f.what = SecureRandom.hex(10)
f.when = Time.now
f.details = 'hey, друг'
f.ticket = 42
f.ticket = 55
end
Dir.mktmpdir do |d|
f = File.join(d, 'base.fb')
File.binwrite(f, fb.export)
Judges::Print.new(Loog::NULL).run(
{ 'format' => 'html', 'columns' => 'what,when,ticket' },
[f, File.join(__dir__, '../../temp/base.html')]
)
end
end

def test_print_all_formats
%w[yaml html xml json].each do |fmt|
Dir.mktmpdir do |d|
f = File.join(d, 'base.fb')
fb = Factbase.new
fb.insert
File.binwrite(f, fb.export)
Judges::Print.new(Loog::NULL).run({ format: fmt, auto: true }, [f])
Judges::Print.new(Loog::NULL).run({ 'format' => fmt, 'auto' => true }, [f])
y = File.join(d, "base.#{fmt}")
assert(File.exist?(y))
end
Expand All @@ -65,11 +86,11 @@ def test_print_twice
fb = Factbase.new
fb.insert
File.binwrite(f, fb.export)
Judges::Print.new(Loog::NULL).run({ format: 'yaml', auto: true }, [f])
Judges::Print.new(Loog::NULL).run({ 'format' => 'yaml', 'auto' => true }, [f])
y = File.join(d, 'base.yaml')
assert(File.exist?(y))
mtime = File.mtime(y)
Judges::Print.new(Loog::NULL).run({ format: 'yaml', auto: true }, [f])
Judges::Print.new(Loog::NULL).run({ 'format' => 'yaml', 'auto' => true }, [f])
assert_equal(mtime, File.mtime(y))
end
end
Expand Down

0 comments on commit 645f5f0

Please sign in to comment.