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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 78b6c79e8a4be4c2bd9c2b5a824550c47ccc02b3
4
- data.tar.gz: a96047a216f7a553d6de6f9f48fca64c4d7790b6
3
+ metadata.gz: a3544c56a29c66abcc6c6ba3fb11ddc204b3198a
4
+ data.tar.gz: aeb8c4b728b729c3d88e428297dc5f13f5871ecf
5
5
  SHA512:
6
- metadata.gz: 94f0ece8702ec1534ccec2c13670f937c0ed41a389164bcf36e36ac9fc40cafc29777ec5b2fe167798161dc998d1d0a756f0c35e2530706d63fd36a545ae14a6
7
- data.tar.gz: ea980d45095e57bcda650ff70bf4a1b87628b441ce48bdba7a63ee46bb885dacf6eac1f67b1c7b78feddb143be870acb3555c9cf973d7f4732c4be8f6f2e0c0e
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
- Advanced example: including some graphs to your reports
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
- # we can't add the entire xml document since multiple xml declarations are invalid
86
- # so we add only the svg part
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] at this point one of "y", "r", "g", "b" (yellow, red, green, blue) is supported
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
- replace_text_with_elements(el, i, "span", {"class" => color}, positions)
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] at this point one of "y", "r", "g", "b" (yellow, red, green, blue) is supported
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
- replace_text_with_elements(el, i, "span", {"class" => color}, positions)
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
- col = row.add_element("td", td_attrs)
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"
@@ -1,5 +1,5 @@
1
1
  module XhtmlReportGenerator
2
- VERSION = '3.0.3'
2
+ VERSION = '3.1.0'
3
3
  end
4
4
 
5
5
  # puts XhtmlReportGenerator::VERSION
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.3
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-01 00:00:00.000000000 Z
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.6.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: