xhtml_report_generator 3.0.3 → 3.1.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/README.md +67 -13
- data/lib/xhtml_report_generator/custom.rb +197 -18
- data/lib/xhtml_report_generator/version.rb +1 -1
- metadata +3 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a3544c56a29c66abcc6c6ba3fb11ddc204b3198a
|
4
|
+
data.tar.gz: aeb8c4b728b729c3d88e428297dc5f13f5871ecf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 102f3476494f50f9d96d5a735fe33ae4714d74a70f422b47c0930c613b891d724b23e5ec59624a9361cc21f3e2982bf626bea5c15d772470fc1421760802aa58
|
7
|
+
data.tar.gz: f926db10047c34ddd95eaeb1a9eb7c0f0378cbfec795bc3999e4d8ce28478f813dd4284c7a4e41e97e8e15eb926aa3998eab84c0c3a15cbd96c6b9d2e12080aa
|
data/README.md
CHANGED
@@ -48,9 +48,73 @@ By default "custom.rb" is loaded through instance eval, see
|
|
48
48
|
[XhtmlReportGenerator/Generator](http://www.rubydoc.info/gems/xhtml_report_generator/XhtmlReportGenerator/Generator)
|
49
49
|
for the documentation of available methods.
|
50
50
|
|
51
|
+
Advanced example1: custom tables including pictures or links
|
52
|
+
----------------------------------
|
53
|
+
[Code] (../master/test/test.rb#L166-L233)
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
require 'xhtml_report_generator'
|
57
|
+
gen1 = XhtmlReportGenerator::Generator.new
|
58
|
+
gen1.create_layout("Custom Table")
|
59
|
+
gen1.heading("h1") {"custom styling"}
|
60
|
+
table_data = [
|
61
|
+
(0..9).collect{|i| i},
|
62
|
+
(10..19).collect{|i| i},
|
63
|
+
(20..29).collect{|i| i},
|
64
|
+
(30..39).collect{|i| i},
|
65
|
+
]
|
66
|
+
table_opts = {
|
67
|
+
:headers => 3,
|
68
|
+
:data_is_xhtml => false,
|
69
|
+
:special => [
|
70
|
+
{ # highlight all numbers from 0-13 green, only 11-13 should be green since the others are part of heading
|
71
|
+
condition: Proc.new { |e| (0 <= e.to_i) && (e.to_i <= 13) }, # a proc
|
72
|
+
attributes: {"style" => "background: #00FF00;"},
|
73
|
+
},
|
74
|
+
{ # font-color the area if number contains a 3
|
75
|
+
row_index: 2..3,
|
76
|
+
col_index: 3..7,
|
77
|
+
condition: Proc.new { |e| e.to_s.match(/3/) }, # a proc
|
78
|
+
attributes: {"style" => "color: red;"},
|
79
|
+
},
|
80
|
+
{ # red border around row 2-3 col with title 8
|
81
|
+
row_title: /[23]/,
|
82
|
+
col_title: "8",
|
83
|
+
condition: Proc.new { |e| true }, # a proc
|
84
|
+
attributes: {"style" => "border: 2px solid red;"},
|
85
|
+
},
|
86
|
+
{ # black border around cell bottom right
|
87
|
+
row_index: 2,
|
88
|
+
col_index: 9,
|
89
|
+
condition: Proc.new { |e| true }, # a proc
|
90
|
+
attributes: {"style" => "border: 2px solid black;"},
|
91
|
+
},
|
92
|
+
]
|
93
|
+
}
|
94
|
+
gen1.custom_table(table_data, table_opts)
|
95
|
+
gen1.heading("h1") {"Table Layout"}
|
96
|
+
table_opts = {
|
97
|
+
:headers => 0,
|
98
|
+
:data_is_xhtml => true,
|
99
|
+
}
|
51
100
|
|
101
|
+
a = gen1.get_code_html() {" blub\nblab\n\t\tblib"}
|
102
|
+
b = gen1.get_image_html("path/to/test.png", {"width" => "55", "height"=> "20", "alt" => "some_interesting_text"})
|
103
|
+
c = gen1.get_content_html() {" leading-whitespace removed"}
|
104
|
+
d = gen1.get_link_html("https://rubygems.org/gems/xhtml_report_generator/") {"download the gem"}
|
52
105
|
|
53
|
-
|
106
|
+
table_data = [
|
107
|
+
[a, d],
|
108
|
+
[c, b]
|
109
|
+
]
|
110
|
+
gen1.custom_table(table_data, table_opts)
|
111
|
+
|
112
|
+
gen1.write("path/to/CustomTable.xhtml")
|
113
|
+
```
|
114
|
+
[Preview](https://cdn.rawgit.com/lumean/xhtml-report-generator/master/test/CustomTableReference.xhtml)
|
115
|
+
|
116
|
+
|
117
|
+
Advanced example2: including some graphs to your reports
|
54
118
|
----------------------------------
|
55
119
|
Due to the xml nature it is also easy to insert SVG graphs / pictures. Check out the svg-graph gem
|
56
120
|
|
@@ -82,16 +146,8 @@ graph = SVG::Graph::Line.new({
|
|
82
146
|
graph.add_data({:data => data_sales_02, :title => 'Sales2002'})
|
83
147
|
graph.add_data({:data => data_sales_03, :title => 'Sales2003'})
|
84
148
|
|
85
|
-
#
|
86
|
-
|
87
|
-
doc = REXML::Document.new(graph.burn())
|
88
|
-
svg = doc.elements["//svg"]
|
89
|
-
out = ''
|
90
|
-
f = REXML::Formatters::Pretty.new(0)
|
91
|
-
f.compact = true
|
92
|
-
f.write(svg, out)
|
93
|
-
|
94
|
-
gen1.html(out)
|
149
|
+
# add the svg to the report
|
150
|
+
gen1.html(graph.burn_svg_only())
|
95
151
|
gen1.write("graph.xhtml")
|
96
152
|
|
97
153
|
```
|
@@ -131,8 +187,6 @@ As a start you can copy the [custom.rb](../master/lib/xhtml_report_generator/cus
|
|
131
187
|
default naming.
|
132
188
|
|
133
189
|
|
134
|
-
|
135
|
-
|
136
190
|
Changes from version 2.x to 3.x
|
137
191
|
-------------------------------
|
138
192
|
The options for the initialize method "XhtmlReportGenerator::Generator.new" changed.
|
@@ -98,8 +98,33 @@ module Custom
|
|
98
98
|
return out
|
99
99
|
end
|
100
100
|
|
101
|
+
# @param elem [REXML::Element]
|
102
|
+
# @return [String]
|
103
|
+
def element_to_string(elem)
|
104
|
+
f = REXML::Formatters::Transitive.new(0) # use Transitive to preserve source formatting (e.g. <pre> tags)
|
105
|
+
out = ""
|
106
|
+
f.write(elem, out)
|
107
|
+
return out
|
108
|
+
end
|
109
|
+
|
110
|
+
# @see #code
|
111
|
+
# Instead of adding content to the report, this method returns the produced html code as a string.
|
112
|
+
# This can be used to insert code into #custom_table (with the option data_is_xhtml: true)
|
113
|
+
# @return [String] the code including <pre> tags as a string
|
114
|
+
def get_code_html(attrs={}, &block)
|
115
|
+
temp = REXML::Element.new("pre")
|
116
|
+
temp.add_attributes(attrs)
|
117
|
+
raise "Block argument is mandatory" unless block_given?
|
118
|
+
text = encoding_fixer(block.call())
|
119
|
+
temp.add_text(text)
|
120
|
+
element_to_string(temp)
|
121
|
+
end
|
122
|
+
|
101
123
|
# Appends a <pre> node after the @current node
|
102
|
-
# @param attrs [Hash] attributes for the <pre> element
|
124
|
+
# @param attrs [Hash] attributes for the <pre> element. The following classes can be passed as attributes and are predefined with a different
|
125
|
+
# background for your convenience !{"class" => "code0"} (light-blue), !{"class" => "code1"} (red-brown),
|
126
|
+
# !{"class" => "code2"} (light-green), !{"class" => "code3"} (light-yellow). You may also specify your own background
|
127
|
+
# as follows: !{"style" => "background: #FF00FF;"}.
|
103
128
|
# @yieldreturn [String] the text to be added to the <pre> element
|
104
129
|
# @return [REXML::Element] the Element which was just added
|
105
130
|
def code(attrs={}, &block)
|
@@ -113,6 +138,19 @@ module Custom
|
|
113
138
|
return @current
|
114
139
|
end
|
115
140
|
|
141
|
+
# @see #content
|
142
|
+
# Instead of adding content to the report, this method returns the produced html code as a string.
|
143
|
+
# This can be used to insert code into #custom_table (with the option data_is_xhtml: true)
|
144
|
+
# @return [String] the code including <pre> tags as a string
|
145
|
+
def get_content_html(attrs={}, &block)
|
146
|
+
temp = REXML::Element.new("p")
|
147
|
+
temp.add_attributes(attrs)
|
148
|
+
raise "Block argument is mandatory" unless block_given?
|
149
|
+
text = encoding_fixer(block.call())
|
150
|
+
temp.add_text(text)
|
151
|
+
element_to_string(temp)
|
152
|
+
end
|
153
|
+
|
116
154
|
# Appends a <p> node after the @current node
|
117
155
|
# @param attrs [Hash] attributes for the <p> element
|
118
156
|
# @yieldreturn [String] the text to be added to the <p> element
|
@@ -143,6 +181,20 @@ module Custom
|
|
143
181
|
return @current
|
144
182
|
end
|
145
183
|
|
184
|
+
# @see #link
|
185
|
+
# Instead of adding content to the report, this method returns the produced html code as a string.
|
186
|
+
# This can be used to insert code into #custom_table (with the option data_is_xhtml: true)
|
187
|
+
# @return [String] the code including <pre> tags as a string
|
188
|
+
def get_link_html(href, attrs={}, &block)
|
189
|
+
temp = REXML::Element.new("a")
|
190
|
+
attrs.merge!({"href" => href})
|
191
|
+
temp.add_attributes(attrs)
|
192
|
+
raise "Block argument is mandatory" unless block_given?
|
193
|
+
text = encoding_fixer(block.call())
|
194
|
+
temp.add_text(text)
|
195
|
+
element_to_string(temp)
|
196
|
+
end
|
197
|
+
|
146
198
|
# Appends a <a href = > node after the @current nodes
|
147
199
|
# @param href [String] this is the
|
148
200
|
# @param attrs [Hash] attributes for the <a> element
|
@@ -160,6 +212,22 @@ module Custom
|
|
160
212
|
return @current
|
161
213
|
end
|
162
214
|
|
215
|
+
# @see #image
|
216
|
+
# Instead of adding content to the report, this method returns the produced html code as a string.
|
217
|
+
# This can be used to insert code into #custom_table (with the option data_is_xhtml: true)
|
218
|
+
# @return [String] the code including <pre> tags as a string
|
219
|
+
def get_image_html(path, attributes = {})
|
220
|
+
# read image as binary and do a base64 encoding
|
221
|
+
binary_data = Base64.strict_encode64(IO.binread(path))
|
222
|
+
type = File.extname(path).gsub('.', '')
|
223
|
+
# create the element
|
224
|
+
temp = REXML::Element.new("img")
|
225
|
+
# add the picture
|
226
|
+
temp.add_attribute("src","data:image/#{type};base64,#{binary_data}")
|
227
|
+
temp.add_attributes(attributes)
|
228
|
+
element_to_string(temp)
|
229
|
+
end
|
230
|
+
|
163
231
|
# @param path [String] absolute or relative path to the image that should be inserted into the report
|
164
232
|
# @param attributes [Hash] attributes for the <img> element, any valid html attributes can be specified
|
165
233
|
# you may specify attributes such "alt", "height", "width"
|
@@ -187,7 +255,7 @@ module Custom
|
|
187
255
|
# highlight_captures then puts a <span> </span> tag around all captures of the regex
|
188
256
|
# NOTE: nested captures are not supported and don't make sense in this context!!
|
189
257
|
# @param regex [Regexp] a regular expression that will be matched
|
190
|
-
# @param color [String]
|
258
|
+
# @param color [String] either one of "y", "r", "g", "b" (yellow, red, green, blue) or a valid html color code (e.g. "#80BFFF")
|
191
259
|
# @param el [REXML::Element] the Element (scope) which will be searched for pattern matches, by default the last inserted element will be scanned
|
192
260
|
# @return [Fixnum] the number of highlighted captures
|
193
261
|
def highlight_captures(regex, color="y", el = @current)
|
@@ -214,7 +282,14 @@ module Custom
|
|
214
282
|
array
|
215
283
|
}
|
216
284
|
num_matches += positions.length
|
217
|
-
|
285
|
+
if ["y","r","g","b"].include?(color)
|
286
|
+
attr = {"class" => color}
|
287
|
+
elsif color.match(/^#[A-Fa-f0-9]{6}$/)
|
288
|
+
attr = {"style" => "background: #{color};"}
|
289
|
+
else
|
290
|
+
raise "invalid color: #{color}"
|
291
|
+
end
|
292
|
+
replace_text_with_elements(el, i, "span", attr, positions)
|
218
293
|
else
|
219
294
|
# for non-text nodes we recurse into it and finally reattach to our parent to preserve ordering
|
220
295
|
num_matches += highlight_captures(regex, color, i)
|
@@ -231,7 +306,7 @@ module Custom
|
|
231
306
|
# or match newlines explicitly.
|
232
307
|
# highlight then puts a <span> </span> tag around all matches of regex
|
233
308
|
# @param regex [Regexp] a regular expression that will be matched
|
234
|
-
# @param color [String]
|
309
|
+
# @param color [String] either one of "y", "r", "g", "b" (yellow, red, green, blue) or a valid html color code (e.g. "#80BFFF")
|
235
310
|
# @param el [REXML::Element] the Element (scope) which will be searched for pattern matches
|
236
311
|
# @return [Fixnum] the number of highlighted captures
|
237
312
|
def highlight(regex, color="y", el = @current)
|
@@ -254,7 +329,14 @@ module Custom
|
|
254
329
|
Regexp.last_match.end(0)-Regexp.last_match.begin(0)]
|
255
330
|
}
|
256
331
|
num_matches += positions.length
|
257
|
-
|
332
|
+
if ["y","r","g","b"].include?(color)
|
333
|
+
attr = {"class" => color}
|
334
|
+
elsif color.match(/^#[A-Fa-f0-9]{6}$/)
|
335
|
+
attr = {"style" => "background: #{color};"}
|
336
|
+
else
|
337
|
+
raise "invalid color: #{color}"
|
338
|
+
end
|
339
|
+
replace_text_with_elements(el, i, "span", attr, positions)
|
258
340
|
else
|
259
341
|
# for non-text nodes we recurse into it and finally reattach to our parent to preserve ordering
|
260
342
|
# puts "recurse"
|
@@ -272,23 +354,119 @@ module Custom
|
|
272
354
|
# is equivalent to the bitwise AND of the two least significant bits with 1, 2 or 3
|
273
355
|
# @return [REXML::Element] the Element which was just added
|
274
356
|
def table(table_data, headers=0, table_attrs={}, tr_attrs={}, th_attrs={}, td_attrs={})
|
275
|
-
|
357
|
+
opts = {
|
358
|
+
headers: headers,
|
359
|
+
data_is_xhtml: false,
|
360
|
+
table_attrs: table_attrs,
|
361
|
+
th_attrs: th_attrs,
|
362
|
+
tr_attrs: tr_attrs,
|
363
|
+
td_attrs: td_attrs,
|
364
|
+
}
|
365
|
+
custom_table(table_data, opts)
|
366
|
+
end
|
367
|
+
|
368
|
+
# creates a html table from two dimensional array of the form Array [row] [col]
|
369
|
+
# @param table_data [Array<Array>] of the form Array [row] [col] containing all data, the '.to_s' method will be called on each element,
|
370
|
+
# @option opts [Number] :headers either of 0, 1, 2, 3. Where 0 is no headers (<th>) at all, 1 is only the first row,
|
371
|
+
# 2 is only the first column and 3 is both, first row and first column as <th> elements. Every other number
|
372
|
+
# is equivalent to the bitwise AND of the two least significant bits with 1, 2 or 3
|
373
|
+
# @option opts [Boolean] :data_is_xhtml defaults to false, if true table_data is inserted as xhtml without any sanitation or escaping.
|
374
|
+
# This way a table can be used for custom layouts.
|
375
|
+
# @option opts [Hash] :table_attrs html attributes for the <table> tag
|
376
|
+
# @option opts [Hash] :th_attrs html attributes for the <th> tag
|
377
|
+
# @option opts [Hash] :tr_attrs html attributes for the <tr> tag
|
378
|
+
# @option opts [Hash] :td_attrs html attributes for the <td> tag
|
379
|
+
# @option opts [Array<Hash>] :special Array of hashes for custom attributes on specific cells (<td> only) of the table
|
380
|
+
# @example Example of the :special attributes
|
381
|
+
# opts[:special] = [
|
382
|
+
# {
|
383
|
+
# col_title: 'rx_DroppedFrameCount', # string or regexp or nil # if neither title nor index are present, the condition is evaluated for all <td> cells
|
384
|
+
# col_index: 5..7, # Fixnum, Range or nil # index has precedence over title
|
385
|
+
# row_title: 'D_0_BE_iMix', # string or regexp or nil
|
386
|
+
# row_index: 6, # Fixnum, Range or nil
|
387
|
+
# condition: Proc.new { |e| Integer(e) != 0 }, # a proc
|
388
|
+
# attributes: {"style" => "background-color: #DB7093;"},
|
389
|
+
# },
|
390
|
+
# ]
|
391
|
+
# @return [REXML::Element] the Element which was just added
|
392
|
+
def custom_table(table_data, opts = {})
|
393
|
+
defaults = {
|
394
|
+
headers: 0,
|
395
|
+
data_is_xhtml: false,
|
396
|
+
table_attrs: {},
|
397
|
+
th_attrs: {},
|
398
|
+
tr_attrs: {},
|
399
|
+
td_attrs: {},
|
400
|
+
special: [],
|
401
|
+
}
|
402
|
+
o = defaults.merge(opts)
|
403
|
+
|
276
404
|
temp = REXML::Element.new("table")
|
277
|
-
temp.add_attributes(table_attrs)
|
405
|
+
temp.add_attributes(o[:table_attrs])
|
406
|
+
row_titles = table_data.collect{|row| row[0].to_s}
|
407
|
+
col_titles = table_data[0].collect{|title| title.to_s}
|
278
408
|
|
279
|
-
for i in 0..table_data.length-1 do
|
280
|
-
row = temp.add_element("tr", tr_attrs)
|
281
|
-
for j in 0..table_data[i].length-1 do
|
282
|
-
if (i == 0 && (0x1 & headers)==0x1)
|
283
|
-
col = row.add_element("th", th_attrs)
|
284
|
-
elsif (j == 0 && (0x2 & headers)==0x2)
|
285
|
-
col = row.add_element("th", th_attrs)
|
286
|
-
elsif ((i == 0 || j ==0) && (0x3 & headers)==0x3)
|
287
|
-
col = row.add_element("th", th_attrs)
|
409
|
+
for i in 0..table_data.length-1 do # row
|
410
|
+
row = temp.add_element("tr", o[:tr_attrs])
|
411
|
+
for j in 0..table_data[i].length-1 do # column
|
412
|
+
if (i == 0 && (0x1 & o[:headers])==0x1)
|
413
|
+
col = row.add_element("th", o[:th_attrs])
|
414
|
+
elsif (j == 0 && (0x2 & o[:headers])==0x2)
|
415
|
+
col = row.add_element("th", o[:th_attrs])
|
416
|
+
elsif ((i == 0 || j ==0) && (0x3 & o[:headers])==0x3)
|
417
|
+
col = row.add_element("th", o[:th_attrs])
|
288
418
|
else
|
289
|
-
|
419
|
+
_td_attrs = o[:td_attrs].clone
|
420
|
+
|
421
|
+
o[:special].each do |h|
|
422
|
+
# check if the current cell is a candidate for special
|
423
|
+
if !h[:col_index].nil?
|
424
|
+
if h[:col_index].is_a?(Range)
|
425
|
+
next if (!h[:col_index].include?(j)) # skip if not in range
|
426
|
+
elsif h[:col_index].is_a?(Integer)
|
427
|
+
next if (h[:col_index] != j) # skip if not at index
|
428
|
+
end
|
429
|
+
elsif !h[:col_title].nil?
|
430
|
+
next if !col_titles[j].match(h[:col_title])
|
431
|
+
end
|
432
|
+
# check if the current cell is a candidate for special
|
433
|
+
if !h[:row_index].nil?
|
434
|
+
if h[:row_index].is_a?(Range)
|
435
|
+
next if (!h[:row_index].include?(i)) # skip if not in range
|
436
|
+
elsif h[:row_index].is_a?(Integer)
|
437
|
+
next if (h[:row_index] != i) # skip if not at index
|
438
|
+
end
|
439
|
+
elsif !h[:row_title].nil?
|
440
|
+
next if !row_titles[i].match(h[:row_title])
|
441
|
+
end
|
442
|
+
|
443
|
+
# here we are a candidate for special, so we check if we meet the condition
|
444
|
+
if h[:condition].call(table_data[i][j])
|
445
|
+
h[:attributes].each { |attr, val|
|
446
|
+
if !_td_attrs[attr].nil?
|
447
|
+
# assume the existing attribute is a string (other types don't make much sense for html)
|
448
|
+
_td_attrs[attr] << val
|
449
|
+
else
|
450
|
+
# create the attribute if it is not already there
|
451
|
+
_td_attrs[attr] = val
|
452
|
+
end
|
453
|
+
}
|
454
|
+
end
|
455
|
+
end
|
456
|
+
|
457
|
+
col = row.add_element("td", _td_attrs)
|
458
|
+
end
|
459
|
+
if o[:data_is_xhtml]
|
460
|
+
# we need to create a new document with a pseudo root because having multiple nodes at top
|
461
|
+
# level is not valid xml
|
462
|
+
doc = REXML::Document.new("<root>" + table_data[i][j].to_s + "</root>")
|
463
|
+
# then we move all children of root to the actual div middle element and insert after current
|
464
|
+
for elem in doc.root.to_a do
|
465
|
+
col.add_element(elem) # add the td element
|
466
|
+
end
|
467
|
+
else
|
468
|
+
col.add_text(table_data[i][j].to_s)
|
290
469
|
end
|
291
|
-
col.add_text(table_data[i][j].to_s)
|
292
470
|
end
|
293
471
|
end
|
294
472
|
|
@@ -296,6 +474,7 @@ module Custom
|
|
296
474
|
@current = temp
|
297
475
|
return @current
|
298
476
|
end
|
477
|
+
|
299
478
|
|
300
479
|
# Appends a new heading element to body, and sets current to this new heading
|
301
480
|
# @param tag_type [String] specifiy "h1", "h2", "h3" for the heading, defaults to "h1"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: xhtml_report_generator
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0
|
4
|
+
version: 3.1.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: 2016-09-
|
11
|
+
date: 2016-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: "The generator can be used to create html or xhtml files. It comes with
|
14
14
|
many utility functions.\n \nThe javascript to render the table of contents, the
|
@@ -51,10 +51,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
51
51
|
version: '0'
|
52
52
|
requirements: []
|
53
53
|
rubyforge_project:
|
54
|
-
rubygems_version: 2.
|
54
|
+
rubygems_version: 2.4.5.1
|
55
55
|
signing_key:
|
56
56
|
specification_version: 4
|
57
57
|
summary: A simple html or xhtml generator or logger to create human readable reports
|
58
58
|
and logs
|
59
59
|
test_files: []
|
60
|
-
has_rdoc:
|