xhtml_report_generator 2.1.1 → 2.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +66 -6
- data/lib/xhtml_report_generator.rb +54 -15
- data/lib/xhtml_report_generator/custom.rb +25 -6
- data/lib/xhtml_report_generator/toc.js +5 -5
- data/lib/xhtml_report_generator/version.rb +1 -1
- metadata +13 -11
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3aa1ee1460f949608080993ab68e93eab9298561
|
4
|
+
data.tar.gz: 3ac67dd1dcde669790405a065295abdcc223d36d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d5499730f068d0597fe8ae3f7fba1e1af58257a7b2ae52c11993e1f3ca0062fc095d8f0e6c48919ccb1141602ed3a6d7b5019d2059747f04c14466c2ee0e1553
|
7
|
+
data.tar.gz: f92130285b5d241af4c17a10068c2c18a155a29cfca58ca6024c20da4b0e1c8dc1f80ba87e3d8dba21dacddb077310080b835ad6922e547a5f73e28544190092
|
data/README.md
CHANGED
@@ -1,15 +1,20 @@
|
|
1
|
-
|
1
|
+
xhtml_report_generator
|
2
2
|
======================
|
3
3
|
|
4
|
-
This project was written to provide an easy way to create valid xhtml documents.
|
4
|
+
This project was written to provide an easy way to create valid xhtml or html documents.
|
5
5
|
Usecases are the automatic creation of reports (e.g. program logs) with automatically created table of contents.
|
6
|
-
|
7
|
-
only written to disk on demand. Hence in case of crashes the data might be lost.
|
6
|
+
xhtml_report_generator is not a Logger replacement, since the complete document is always kept in memory and
|
7
|
+
only written to disk on demand. Hence in case of crashes the data might be lost if you didn't write before.
|
8
|
+
|
9
|
+
Ruby version
|
10
|
+
-----
|
11
|
+
This gem was mainly tested with ruby version 2.2.3. Except of the test_encoding_issues unit tests, all other tests are
|
12
|
+
also passing with 1.9.3. Probably there were issues in ruby itself for earlier versions.
|
8
13
|
|
9
14
|
|
10
15
|
Example usage
|
11
16
|
-------------
|
12
|
-
In the following you can find a quick start on how to use
|
17
|
+
In the following you can find a quick start on how to use xhtml_report_generator.
|
13
18
|
Basically the project is built in a way that lets you supply your own methods for everything.
|
14
19
|
By default "custom.rb" is loaded through instance eval, so you can check the corresponding documentation for available methods.
|
15
20
|
|
@@ -21,9 +26,11 @@ Basically starting from version 2 the syntax for each method of custom.rb is uni
|
|
21
26
|
def method({"attribute" => "value", "attribute2" => "value2"}) {contents}
|
22
27
|
|
23
28
|
in addition the method naming convention was changed from camelCase to underscore to comply more with ruby conventions.
|
29
|
+
|
30
|
+
See <a href=http://www.rubydoc.info/gems/xhtml_report_generator/Custom>http://www.rubydoc.info/gems/xhtml_report_generator/Custom</> for the documentation of available methods.
|
24
31
|
|
25
32
|
<pre>
|
26
|
-
require '
|
33
|
+
require 'xhtml_report_generator'
|
27
34
|
|
28
35
|
gen1 = XhtmlReportGenerator::Generator.new
|
29
36
|
gen1.create_layout("Title")
|
@@ -33,9 +40,62 @@ gen1.heading("h3") {"section"}
|
|
33
40
|
gen1.content({"class"=>"bold"}) {"content function: Hallo welt <br /> html test <span class=\"r\" >red span test</span>"}
|
34
41
|
gen1.html("<p class=\"italic\">html function: Hallo welt <br /> html test <span class=\"r\" >red span test</span></p>")
|
35
42
|
gen1.highlight(/Ha.*lt/)
|
43
|
+
gen1.link("https://rubygems.org/gems/xhtml_report_generator/") {"download the gem"}
|
44
|
+
# browser will parse this as html (based on file extension)
|
45
|
+
gen1.write("myreport.html")
|
46
|
+
# browser will parse this as xhtml (based on file extension)
|
47
|
+
gen1.write("myreport.xhtml")
|
48
|
+
</pre>
|
49
|
+
|
50
|
+
Adding some graphs to your reports
|
51
|
+
----------------------------------
|
52
|
+
Due to the xml nature it is also easy to insert SVG graphs / pictures. Check out the svg-graph gem
|
53
|
+
|
54
|
+
<pre>
|
55
|
+
require 'xhtml_report_generator'
|
56
|
+
require 'SVG/Graph/Line'
|
57
|
+
require 'REXML/document'
|
58
|
+
|
59
|
+
gen1 = XhtmlReportGenerator::Generator.new
|
60
|
+
gen1.create_layout("Graph example")
|
61
|
+
gen1.heading("h1") {"my graph"}
|
62
|
+
|
63
|
+
x_axis = %w(Jan Feb Mar);
|
64
|
+
data_sales_02 = [12, 45, 21]
|
65
|
+
data_sales_03 = [15, 30, 40]
|
66
|
+
|
67
|
+
graph = SVG::Graph::Line.new({
|
68
|
+
:height => 300,
|
69
|
+
:width => 500,
|
70
|
+
:show_graph_title => true,
|
71
|
+
:graph_title => 'Graph Title',
|
72
|
+
:show_x_title => true,
|
73
|
+
:x_title => 'Month',
|
74
|
+
:show_y_title => true,
|
75
|
+
#:y_title_text_direction => :bt,
|
76
|
+
:y_title => 'cash',
|
77
|
+
:fields => x_axis})
|
78
|
+
|
79
|
+
graph.add_data({:data => data_sales_02, :title => 'Sales2002'})
|
80
|
+
graph.add_data({:data => data_sales_03, :title => 'Sales2003'})
|
81
|
+
|
82
|
+
# we can't add the entire xml document since multiple xml declarations are invalid
|
83
|
+
# so we add only
|
84
|
+
doc = REXML::Document.new(graph.burn())
|
85
|
+
svg = doc.elements["//svg"]
|
86
|
+
out = ''
|
87
|
+
f = REXML::Formatters::Pretty.new(0)
|
88
|
+
f.compact = true
|
89
|
+
f.write(svg, out)
|
90
|
+
|
91
|
+
gen1.html(out)
|
92
|
+
gen1.write("graph.xhtml")
|
36
93
|
|
37
94
|
</pre>
|
38
95
|
|
96
|
+
|
97
|
+
|
98
|
+
|
39
99
|
Changes from version 1.x to 2.x
|
40
100
|
-------------------------------
|
41
101
|
To ease with migration here is a list with the changed function names, please also check the new synopsis
|
@@ -1,8 +1,6 @@
|
|
1
|
-
#
|
2
|
-
# logfile.section
|
3
|
-
# logfil.content("mein resultat")
|
4
|
-
# logfile.markup(regexstart, regex end, :yellow)
|
1
|
+
# encoding: utf-8
|
5
2
|
require 'rexml/document'
|
3
|
+
require 'rexml/formatters/transitive'
|
6
4
|
|
7
5
|
module XhtmlReportGenerator
|
8
6
|
|
@@ -29,36 +27,73 @@ module XhtmlReportGenerator
|
|
29
27
|
}
|
30
28
|
# either use the default files provided with the gem, or those provided by the caller
|
31
29
|
symbols = symbols.merge(opts)
|
30
|
+
custom_rb_path = symbols[:custom_rb]
|
32
31
|
for key in symbols.keys do
|
33
32
|
# read the contents into the symbols hash
|
34
33
|
symbols[key] = File.read(symbols[key])
|
35
34
|
end
|
36
35
|
# load the custom module and extend it, use instance_eval otherwise the module will affect
|
37
36
|
# all existing Generator classes
|
38
|
-
instance_eval
|
37
|
+
instance_eval(symbols[:custom_rb], custom_rb_path)
|
39
38
|
|
40
39
|
@document = Generator.create_xhtml_document("Title")
|
41
40
|
head = @document.elements["//head"]
|
41
|
+
|
42
|
+
head.add_element("meta", {"charset" => "utf-8"})
|
43
|
+
|
42
44
|
# insert the custom css, and javascript files
|
43
45
|
style = head.add_element("style", {"type" => "text/css"})
|
44
|
-
|
45
|
-
style.add_text(REXML::CData.new("\n"+symbols[:css].gsub(/\n/, "")+"\n"))
|
46
|
+
cdata(symbols[:css], style)
|
46
47
|
|
47
48
|
style = head.add_element("style", {"type" => "text/css", "media"=>"print"})
|
48
|
-
|
49
|
+
cdata(symbols[:css_print], style)
|
49
50
|
|
50
51
|
script = head.add_element("script", {"type" => "text/javascript"})
|
51
|
-
|
52
|
+
cdata(symbols[:jquery], script)
|
52
53
|
|
53
54
|
script = head.add_element("script", {"type" => "text/javascript"})
|
54
|
-
|
55
|
+
cdata(symbols[:toc], script)
|
56
|
+
end
|
57
|
+
|
58
|
+
# Surrounds CData tag with c-style comments to remain compatible with normal html.
|
59
|
+
# For plain xhtml documents this is not needed.
|
60
|
+
# Example /*<![CDATA[*/\n ...content ... \n/*]]>*/
|
61
|
+
# @param str [String] the string to be enclosed in cdata
|
62
|
+
# @param parent_element [REXML::Element] the element to which cdata should be added
|
63
|
+
# @return [String] CDATA enclosed in c-style comments /**/
|
64
|
+
def cdata(str, parent_element)
|
65
|
+
f = REXML::Formatters::Transitive.new(0) # use Transitive to preserve source formatting
|
66
|
+
# somehow there is a problem with CDATA, any text added after will automatically go into the CDATA
|
67
|
+
# so we have do add a dummy node after the CDATA and then add the text.
|
68
|
+
parent_element.add_text("/*")
|
69
|
+
parent_element.add(REXML::CData.new("*/\n"+str+"\n/*"))
|
70
|
+
parent_element.add(REXML::Comment.new("dummy comment to make c-style comments for cdata work"))
|
71
|
+
parent_element.add_text("*/")
|
72
|
+
end
|
73
|
+
|
74
|
+
# Check if the give string is a valid UTF-8 byte sequence. If it is not valid UTF-8, then
|
75
|
+
# all invalid bytes are replaced by "\u2e2e" (\xe2\xb8\xae) ('REVERSED QUESTION MARK') because the default
|
76
|
+
# replacement character "\uFFFD" ('QUESTION MARK IN DIAMOND BOX') is two slots wide and might
|
77
|
+
# destroy mono spaced formatting
|
78
|
+
# @param str [String] of any encoding
|
79
|
+
# @return [String] UTF-8 encoded valid string
|
80
|
+
def encoding_fixer(str)
|
81
|
+
#if !str.force_encoding('UTF-8').valid_encoding?
|
82
|
+
# str.encode!('UTF-8', 'ISO-8859-1', {:invalid => :replace, :undef => :replace, :xml => :text})
|
83
|
+
#end
|
84
|
+
tmp = str.force_encoding('UTF-8').encode('UTF-8',{:invalid => :replace, :undef => :replace, :replace => "\u2e2e"})
|
85
|
+
# replace all special control chars as well but keep newline and whitespace "\u2e2e"
|
86
|
+
tmp.force_encoding('binary').gsub!(/[\x00-\x07\x0C-\x1F]|\xef\xbf\xbe|\xef\xbf\xbf/n, "\xe2\xb8\xae".force_encoding('binary'))
|
87
|
+
return tmp.force_encoding('UTF-8')
|
55
88
|
end
|
56
89
|
|
57
90
|
# Creates a minimal valid xhtml document including header title and body elements
|
58
91
|
# @param title [String] Title in the header section
|
59
92
|
def self.create_xhtml_document(title)
|
60
|
-
|
61
|
-
|
93
|
+
# don't use version 1.1 - firefox has not yet a parser vor xml 1.1
|
94
|
+
# https://bugzilla.mozilla.org/show_bug.cgi?id=233154
|
95
|
+
header = '<?xml version="1.0" encoding="UTF-8"?>'
|
96
|
+
header << '<!DOCTYPE html>'
|
62
97
|
|
63
98
|
doc = REXML::Document.new(header)
|
64
99
|
html = doc.add_element("html", {"xmlns" => "http://www.w3.org/1999/xhtml"})
|
@@ -71,14 +106,18 @@ module XhtmlReportGenerator
|
|
71
106
|
end
|
72
107
|
|
73
108
|
# returns the string representation of the xml document
|
74
|
-
# @param indent [Number] indent for child elements. defaults to 0.
|
109
|
+
# @param indent [Number] indent for child elements. defaults to 0.
|
110
|
+
# Note: if you change the indet this might destroy formatting of <pre> sections
|
111
|
+
# @return [String] formatted xml document
|
75
112
|
def to_s(indent = 0)
|
76
113
|
output = ""
|
77
114
|
# note : transitive is needed to preserve newlines in <pre> tags
|
78
115
|
# note2: the hash options syntax is supported only from ruby version >= 2.0.0 we need the old style
|
79
116
|
# for compatibility with 1.9.3
|
80
|
-
|
81
|
-
|
117
|
+
# @document.write({:output=>output, :indent=>indent, :transitive=>true})
|
118
|
+
# change to Formatters since document.write is deprecated
|
119
|
+
f = REXML::Formatters::Transitive.new(indent)
|
120
|
+
f.write(@document, output)
|
82
121
|
return output
|
83
122
|
end
|
84
123
|
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: utf-8
|
1
2
|
require 'base64'
|
2
3
|
# The module name doesn't matter, just make sure at the end to 'extend' it
|
3
4
|
# because it will be 'eval'ed by the initialize method of the XhtmlReportGenerator::Generator class.
|
@@ -28,7 +29,7 @@ module Custom
|
|
28
29
|
div.add_text("Quick Links")
|
29
30
|
div.add_element("br");div.add_element("br")
|
30
31
|
end
|
31
|
-
|
32
|
+
|
32
33
|
@div_middle = @body.add_element("div", {"class" => "middle"})
|
33
34
|
@layout = true
|
34
35
|
end
|
@@ -85,7 +86,7 @@ module Custom
|
|
85
86
|
@div_middle.insert_after(@current, temp)
|
86
87
|
@current = temp
|
87
88
|
raise "Block argument is mandatory" unless block_given?
|
88
|
-
text = block.call()
|
89
|
+
text = encoding_fixer(block.call())
|
89
90
|
@current.add_text(text)
|
90
91
|
return @current
|
91
92
|
end
|
@@ -100,7 +101,7 @@ module Custom
|
|
100
101
|
@div_middle.insert_after(@current, temp)
|
101
102
|
@current = temp
|
102
103
|
raise "Block argument is mandatory" unless block_given?
|
103
|
-
text = block.call()
|
104
|
+
text = encoding_fixer(block.call())
|
104
105
|
@current.add_text(text)
|
105
106
|
return @current
|
106
107
|
end
|
@@ -109,7 +110,8 @@ module Custom
|
|
109
110
|
# @param text [String] valid xhtml code which is included into the document
|
110
111
|
# @return [REXML::Element] the Element which was just added
|
111
112
|
def html(text)
|
112
|
-
# we need to create a new document with a pseudo root
|
113
|
+
# we need to create a new document with a pseudo root becaus having multiple nodes at top
|
114
|
+
# level is not valid xml
|
113
115
|
doc = REXML::Document.new("<root>"+text+"</root>")
|
114
116
|
# then we move all children of root to the actual div middle element and insert after current
|
115
117
|
for i in doc.root.to_a do
|
@@ -119,6 +121,23 @@ module Custom
|
|
119
121
|
return @current
|
120
122
|
end
|
121
123
|
|
124
|
+
# Appends a <a href = > node after the @current nodes
|
125
|
+
# @param href [String] this is the
|
126
|
+
# @param attrs [Hash] attributes for the <a> element
|
127
|
+
# @yieldreturn [String] the text to be added to the <a> element
|
128
|
+
# @return [REXML::Element] the Element which was just added
|
129
|
+
def link(href, attrs={}, &block)
|
130
|
+
temp = REXML::Element.new("a")
|
131
|
+
attrs.merge!({"href" => href})
|
132
|
+
temp.add_attributes(attrs)
|
133
|
+
@div_middle.insert_after(@current, temp)
|
134
|
+
@current = temp
|
135
|
+
raise "Block argument is mandatory" unless block_given?
|
136
|
+
text = encoding_fixer(block.call())
|
137
|
+
@current.add_text(text)
|
138
|
+
return @current
|
139
|
+
end
|
140
|
+
|
122
141
|
# @param path [String] absolute or relative path to the image that should be inserted into the report
|
123
142
|
# @param attrs [Hash] attributes for the <img> element, any valid html attributes can be specified
|
124
143
|
# you may specify attributes such "alt", "height", "width"
|
@@ -270,7 +289,7 @@ module Custom
|
|
270
289
|
@div_middle.insert_after(@current, temp)
|
271
290
|
@current = temp
|
272
291
|
raise "Block argument is mandatory" unless block_given?
|
273
|
-
text = block.call()
|
292
|
+
text = encoding_fixer(block.call())
|
274
293
|
@current.text = text
|
275
294
|
return @current
|
276
295
|
end
|
@@ -297,7 +316,7 @@ module Custom
|
|
297
316
|
|
298
317
|
@current = temp
|
299
318
|
raise "Block argument is mandatory" unless block_given?
|
300
|
-
text = block.call()
|
319
|
+
text = encoding_fixer(block.call())
|
301
320
|
@current.text = text
|
302
321
|
return @current
|
303
322
|
end
|
@@ -1,5 +1,5 @@
|
|
1
|
-
$(document).ready(function(){$("td").each(function(b){b=$(this);null!==b.html().match(/^passed$/)?b.attr("style","background-color:#19D119;"):null!==b.html().match(/^failed$/)?b.attr("style","background-color:#FF4719;"):null!==b.html().match(/^check$/)&&b.attr("style","background-color:#FFFF00;")});$("[class=rtoconly],[class=bothtoc]").each(function(b){var a=$(this),c=a.attr("class");a.attr("id",c+b);$("#rtoc").append("<a href='#"+c+b+"'>"+a.html()+"</a> <br />\n")});h3index=h2index=h1index=0
|
2
|
-
$(this);void 0==a.attr("id")&&a.attr("id","title"+b);if("
|
3
|
-
0;if("
|
4
|
-
e=!1;else c=" - ",e=!0;b.html(c);if("0"==a[2]){var d=new RegExp("div_fold_"+a[1]+"_[1-9]\\d*_0");$("div").filter(function(){return this.id.match(d)}).each(function(){$(this).children('a[id^="a_fold"]').html(c);$(this).toggle(e)});d=new RegExp("div_fold_"+a[1]+"_\\d+_[1-9]\\d*");$("div").filter(function(){return this.id.match(d)}).each(function(){$(this).children('a[id^="a_fold"]').html(c)
|
5
|
-
$(this).toggle(e)}))})});
|
1
|
+
$(document).ready(function(){$("td").each(function(b){b=$(this);null!==b.html().match(/^passed$/i)?b.attr("style","background-color:#19D119;"):null!==b.html().match(/^failed$/i)?b.attr("style","background-color:#FF4719;"):null!==b.html().match(/^check$/i)&&b.attr("style","background-color:#FFFF00;")});$("[class=rtoconly],[class=bothtoc]").each(function(b){var a=$(this),c=a.attr("class");a.attr("id",c+b);$("#rtoc").append("<a href='#"+c+b+"'>"+a.html()+"</a> <br />\n")});h3index=h2index=h1index=0;
|
2
|
+
$("h1, h2, h3, a.h2, a.h1").each(function(b){var a=$(this);void 0==a.attr("id")&&a.attr("id","title"+b);if("H1"==a.prop("tagName").toUpperCase())h1index+=1,h3index=h2index=0,a.prepend(h1index+" "),$("#ltoc").append("<br />\n"),lasth1="#"+a.attr("id"),lasth1cont=a.html();else if("H2"==a.prop("tagName").toUpperCase())h2index+=1,h3index=0,a.prepend(h1index+"."+h2index+" "),lasth2="#"+a.attr("id"),lasth2cont=a.html();else if("H3"==a.prop("tagName").toUpperCase())h3index+=1,a.prepend(h1index+"."+h2index+
|
3
|
+
"."+h3index+" ");else{if("H1"==a.attr("class").toUpperCase())return a.attr("href",lasth1),a.html(lasth1cont),0;if("H2"==a.attr("class").toUpperCase())return a.attr("href",lasth2),a.html(lasth2cont),0}if("undefined"!=typeof a.attr("class")&&"RTOCONLY"==a.attr("class").toUpperCase())return 0;$("#ltoc").append("<div id='div_fold_"+h1index+"_"+h2index+"_"+h3index+"'><a id='a_fold_"+h1index+"_"+h2index+"_"+h3index+"' style='cursor:pointer'> - </a> <a id='link"+b+"' href='#"+a.attr("id")+"' >"+
|
4
|
+
a.html()+"</a> <br /> </div>\n");return 0});$('a[id^="a_fold"]').click(function(){var b=$(this),a=b.attr("id").match(/(\d+)_(\d+)_(\d+)/);if(b.html().match(/-/))var c=" + ",e=!1;else c=" - ",e=!0;b.html(c);if("0"==a[2]){var d=new RegExp("div_fold_"+a[1]+"_[1-9]\\d*_0");$("div").filter(function(){return this.id.match(d)}).each(function(){$(this).children('a[id^="a_fold"]').html(c);$(this).toggle(e)});d=new RegExp("div_fold_"+a[1]+"_\\d+_[1-9]\\d*");$("div").filter(function(){return this.id.match(d)}).each(function(){$(this).children('a[id^="a_fold"]').html(c);
|
5
|
+
$(this).toggle(e)})}else"0"==a[3]&&(d=new RegExp("div_fold_"+a[1]+"_"+a[2]+"_[1-9]\\d*"),$("div").filter(function(){return this.id.match(d)}).each(function(){$(this).children('a[id^="a_fold"]').html(c);$(this).toggle(e)}))})});
|
metadata
CHANGED
@@ -1,25 +1,27 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xhtml_report_generator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 2.
|
4
|
+
version: 2.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Manuel Widmer
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-01-29 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description: "The generator can be used to create xhtml files. It comes with
|
14
|
-
|
13
|
+
description: "The generator can be used to create html or xhtml files. It comes with
|
14
|
+
many utility functions.\n== Example usage\n gen1 = XhtmlReportGenerator::Generator.new\n
|
15
15
|
\ gen1.create_layout(\"Title\")\n gen1.heading(\"h1\", {\"class\" => \"bothtoc\"})
|
16
16
|
{\"titel\"}\n gen1.heading(\"h2\") {\"subtitel\"}\n gen1.heading(\"h3\") {\"section\"}\n
|
17
|
-
\ gen1.content({\"class\"=>\"bold\"}) {\"content function: Hallo welt
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
\ gen1.content({\"class\"=>\"bold\"}) {\"content function: Hallo welt <br /> html
|
18
|
+
test <span class=\"r\" >red span test</span>\"}\n gen1.html(\"<p class=\"italic\">html
|
19
|
+
function: Hallo welt <br /> html test <span class=\"r\" >red span test<span></p>\")\n
|
20
|
+
\ gen1.highlight(/Ha.*lt/)\n \nThe javascript to render the table of contents,
|
21
|
+
the custom generator functions and style sheet all can be\nsupplied by your own,
|
22
|
+
if necessary. By default there are methods to insert tables, links, paragraphs,
|
23
|
+
preformatted text\nand arbitrary xhtml code. Due to the xml nature it is also easy
|
24
|
+
to insert SVG graphs / pictures.\n\n"
|
23
25
|
email: m-widmer@gmx.ch
|
24
26
|
executables: []
|
25
27
|
extensions: []
|
@@ -57,5 +59,5 @@ rubyforge_project:
|
|
57
59
|
rubygems_version: 2.4.5.1
|
58
60
|
signing_key:
|
59
61
|
specification_version: 4
|
60
|
-
summary: A simple
|
62
|
+
summary: A simple html or xhtml generator to create human readable support
|
61
63
|
test_files: []
|