rubypivot 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2976ecf2bef2b1fcc45d0044cbf553ab7b07e0ee8ecd48aca334f13836399016
4
- data.tar.gz: 92c6cf0c117a8dbbff68b55a72c760bae6bc62992b1ca7744e9bfe35ebc731eb
3
+ metadata.gz: 9a08da64e0fde946fe250518330143484176d4aae03f14d01d8c0597ec1b9631
4
+ data.tar.gz: 920c3745a938d8529a56acd051801ac18b9ec76d00ca62bb498e3043d080f446
5
5
  SHA512:
6
- metadata.gz: 9575159c5670c6831c9777e98bede1a93e19347f3b9ae6d3f9a848450d78029dc3810323857de554d98d0fa5a72edfd5930173fc049c73b8ee0b28a00b9c4e7c
7
- data.tar.gz: a9a32709eec385451e3bd1398e412cc874e7020daf02ad25ca85ed4905606c15ee300e4884dc8a9be906b0da8188d6ec5bb498cd2c3d180b6e90ad22cd5fd4f7
6
+ metadata.gz: 2b27cc0df34f5bdf1c08e44633f2f85204ebf78ee139bd56162841f7dba07f063976bdf1b31635a1f2315aab782f9d848fd710b7895c915516e2fc52460d475a
7
+ data.tar.gz: 904336637ab697e188b75165b56f32a318b5036aec8d141f8db8bd84d0648f1d169ecc990670f561af9b60c26c9102314780fc7a27dda51ddfdc24b958618c9c
data/README.md CHANGED
@@ -10,22 +10,55 @@ Suggestions and pull requests are welcome.
10
10
  ```
11
11
  gem install rubypivot
12
12
  ```
13
- Not yet ready, please clone the repository for the moment.
13
+
14
14
  ## Usage
15
15
 
16
+ Pivot class transform database record type array into pivot table Hash or Array
17
+
16
18
  ```ruby
17
19
  require "rubypivot"
18
20
  pivot = Rubypivot::Pivot.new(source_data, :month, :name, :value, data_type: :integer)
19
- pivot.build.each do |line|
21
+ pivot_array = pivot.build
22
+ pivot_array.each do |line|
20
23
  p line
21
24
  end
22
25
  ```
26
+
27
+ SpreadTable class makes a Hash table from pivot hash, which makes it easy to build HTML table with totals, layouts, colors.
28
+
29
+ ```ruby
30
+ require "rubypivot"
31
+ pivot = Rubypivot::Pivot.new(source_data, :month, :name, :value, data_type: :integer)
32
+ pivot_hash = pivot.build_hash
33
+ spread = Rubypivot::SpreadTable.new(pivot_hash, data_type: :integer)
34
+ spread.rows.each do |row|
35
+ puts row.to_s
36
+ end
37
+ ```
38
+
39
+ ```ruby
40
+ require "rubypivot"
41
+ spread = Rubypivot::SpreadTable.new(DATA_SOURCE, data_type: :integer)
42
+ puts spread.to_html(class: "table table-striped", line_end: :cr)
43
+ ```
44
+ ![SpreadTable](./examples/spread_table.png)
45
+
23
46
  See sample scripts in examples folder.
24
47
 
25
48
  Supported data aggregation is only SUM for numeric values.
26
49
 
27
50
  Total calculation supported.
28
51
 
52
+
53
+ ## To do-s
54
+
55
+ - Bootstrap Grid output
56
+
57
+ ## History
58
+
59
+ - Ver. 0.0.2 : 2021-01-01 New class SpreadTable supports to_s, to_html (HTML table), to_grid (Bootstrap grid)
60
+ - Ver. 0.0.1 : 2020-12-28 First release. Making pivot array
61
+
29
62
  ## License
30
63
 
31
64
  The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,61 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="utf-8">
5
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
6
+ <meta name="viewport" content="initial-scale=1.0, user-scalable=no" />
7
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
8
+ <title>RubyPivot Example</title>
9
+ <style>
10
+ .container {
11
+ max-width: 960px;
12
+ }
13
+ .title-header {
14
+ max-width: 700px;
15
+ }
16
+ tr.header {
17
+ background-color: #eeeeff;
18
+ text-align: right;
19
+ font-weight: bold;
20
+ }
21
+ tr.data-row {
22
+ text-align: right;
23
+ }
24
+ tr.data-girl {
25
+ background-color: #ffeeee;
26
+ text-align: right;
27
+ }
28
+ td.header-title {
29
+ text-align: left;
30
+ }
31
+ td.data-title {
32
+ background-color: #ffffee;
33
+ text-align: left;
34
+ }
35
+ td.data {
36
+ color:#3333aa;
37
+ }
38
+ td.zero {
39
+ color: #aaaaaa;
40
+ }
41
+ td.negative {
42
+ color:#aa3333;
43
+ }
44
+ </style>
45
+ </head>
46
+ <body>
47
+ <div class="container">
48
+
49
+ <div class="title_header" name="title-header" data-bs-target="target">HTML Table Creator for pivot array</div>
50
+
51
+ <table class="table table-striped">
52
+ <tr class="header"><td class="header-title"></td><td>A</td><td>B</td><td>C</td></tr>
53
+ <tr class="data-girl"><td class="data-title">Wendy</td><td>1</td><td>2</td><td class="negative">-3</td></tr>
54
+ <tr class="data-row"><td class="data-title">John</td><td>4</td><td class="zero">0</td><td>6</td></tr>
55
+ <tr class="data-row"><td class="data-title">David</td><td class="negative">-2</td><td>3</td><td class="zero">0</td></tr>
56
+ <tr class="header"><td class="header-title">Total</td><td>3</td><td>5</td><td>3</td></tr>
57
+ </table>
58
+
59
+ </div>
60
+ </body>
61
+ </html>
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+ #
4
+ # Make HTML table from spread sheet array
5
+ #
6
+ APP_ROOT = File.dirname(__FILE__)
7
+ $LOAD_PATH << "#{APP_ROOT}/../lib"
8
+ require "rubypivot/html_tag"
9
+
10
+ div = Rubypivot::HtmlTag.new('div', class: 'section section-info', name: 'title-header')
11
+ div.add_key('data-bs-target', 'target')
12
+ puts div.build { "HTML Tag generator" }
13
+ puts div.build(body: "HTML Tag generator")
14
+
15
+ tr = Rubypivot::HtmlTag.new('tr', class: 'header')
@@ -18,18 +18,22 @@ source_data = [
18
18
 
19
19
  pivot = Rubypivot::Pivot.new(source_data, :month, :name, :value, data_type: :integer)
20
20
  pivot.column_titles = ['Jan', 'Feb', 'Mar']
21
+ pivot.options[:row_total] = 'Total'
21
22
  # pivot.options[:header] = true
22
23
  # pivot.options[:row_header] = false
23
- # pivot.column_titles.sort!
24
- # p pivot.column_titles
25
24
  # p pivot.row_titles
25
+ # p pivot.header_row('')
26
+ # p pivot.column_titles.sort! # sorting columns
27
+ p pivot.column_titles
28
+ puts "------------"
29
+ pivot.build_data.sort.each do |title, line|
30
+ puts "#{title}: #{line.join(", ")}"
31
+ end
32
+
33
+ puts
34
+ puts "------------"
26
35
  pivot.build.each do |line|
27
36
  p line
28
37
  end
29
38
  puts "------------"
30
39
  p pivot.total_row('Total')
31
-
32
- #["", "Jan", "Feb", "Mar"]
33
- #["David", 3, nil, nil]
34
- #["John", 123, 23, nil]
35
- #["Wendy", 33, 100, nil]
Binary file
@@ -0,0 +1,75 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+ #
4
+ # Create spread sheet array, having row (tr) and cell (td) attributes
5
+ # to help HTML table
6
+ #
7
+ APP_ROOT = File.dirname(__FILE__)
8
+ $LOAD_PATH << "#{APP_ROOT}/../lib"
9
+ require "rubypivot"
10
+ # require "pry"
11
+ # binding.pry
12
+
13
+ DATA_SOURCE = [
14
+ ["", "A", "B", "C"],
15
+ ["Wendy", 1, 2, -3],
16
+ ["John", 4, 0, 6],
17
+ ["David", -2, 3, 0],
18
+ ["Total", 3, 5, 3],
19
+ ]
20
+ # Callback function to control cell class
21
+ # data is cell data, line_data is an array of the line cells
22
+ def cell_class_callback(data, line_title)
23
+ if data.to_i < 0
24
+ "negative"
25
+ elsif data.to_i == 0
26
+ "zero"
27
+ else
28
+ nil
29
+ end
30
+ end
31
+
32
+ def cell_callback(data, line_title)
33
+ if line_title == 'Total'
34
+ "<td class=\"my-total\">#{"%03d" % data}</td>"
35
+ else
36
+ "<td class=\"my-cell\">#{"%03d" % data}</td>"
37
+ end
38
+ end
39
+
40
+ # spread = DATA_SOURCE.to_spread( # Alternate method to create new
41
+ spread = Rubypivot::SpreadTable.new(DATA_SOURCE,
42
+ header: :first, # Consider the first row as header row
43
+ title: :first, # Consider the first column as row header
44
+ total: :last, # Consider the last row as total row
45
+ data_type: :integer, # or :float, :string, nil
46
+ header_line_class: 'header', # tr class for header and total row(s), grid size for Bootstrap
47
+ header_title_class: 'header-title', # td class for title of header row
48
+ data_line_class: 'data-row', # tr class for data row(s), grid size for Bootstrap
49
+ data_title_class: 'data-title', # td class for title of data row
50
+ data_format: '%02d', # format for data cells
51
+ )
52
+ # spread.each_header_line {|row| row.set_line_class("header") }
53
+ spread.line(1).set_line_class("data-girl") # Set TR class
54
+ spread.set_cell_class(method(:cell_class_callback))
55
+ # spread.each_data_line {|line| line.set_cell_class(method(:cell_class_callback)) } # Same effects like above line
56
+ # spread.set_cell_class('data-class')
57
+ spread.get_row(:last).set_cell_callback(method(:cell_callback))
58
+ # spread.set_cell_callback(method(:cell_callback))
59
+
60
+ puts "--- Created array ------------"
61
+ spread.rows.each do |row|
62
+ puts row.to_s
63
+ end
64
+ puts "--- Some calculated attrib ---"
65
+ puts "Total width: #{spread.total_width}"
66
+ puts "Total height: #{spread.total_height}"
67
+ puts "Data width: #{spread.data_width}"
68
+ puts "Data height: #{spread.data_height}"
69
+
70
+ puts "--- HTML table ---------------"
71
+ puts spread.to_html(class: "table table-striped", line_end: :cr)
72
+
73
+ puts "--- Bootstrap grid -----------"
74
+ spread.set_line_class("md")
75
+ puts spread.to_grid(:bootstrap, [2, 1, 1, 1, 1])
@@ -1,9 +1,10 @@
1
1
  # require "rubypivot/version"
2
+ require "rubypivot/version"
2
3
  require "rubypivot/pivot"
3
4
  require "rubypivot/pivot_row"
5
+ require "rubypivot/spread_table"
6
+ require "rubypivot/html_tag"
4
7
 
5
8
  module Rubypivot
6
- VERSION = "0.0.1" # Pre-release
7
-
8
9
  class PivotError < StandardError; end
9
10
  end
@@ -0,0 +1,81 @@
1
+ module Rubypivot
2
+ class HtmlTag
3
+ def initialize(tag_name, options = {})
4
+ @tag_name = tag_name
5
+ @options = options
6
+ @class_strings = []
7
+ end
8
+
9
+ def add_key(key, value)
10
+ @options[key] = value
11
+ self
12
+ end
13
+
14
+ def sanitize(value)
15
+ return nil unless value
16
+ array = value.split(/ +/)
17
+ array.uniq!
18
+ array.join(" ")
19
+ end
20
+
21
+ def add_class(class_string)
22
+ if class_string
23
+ class_string.to_s.split(/ +/).each do |str|
24
+ next if str.nil? || @class_strings.include?(str)
25
+ @class_strings << str
26
+ end
27
+ end
28
+ self
29
+ end
30
+
31
+ def build_class
32
+ return '' if @class_strings.empty?
33
+ " class=\"#{@class_strings.join(' ')}\""
34
+ end
35
+
36
+ def open
37
+ add_class(@options[:class])
38
+ res = "<#{@tag_name}"
39
+ res << build_class
40
+ @options.each do |key, value|
41
+ next if value.nil?
42
+ case key
43
+ when :class
44
+ # class was handled first
45
+ else
46
+ res << " #{key}=\"#{value}\""
47
+ end
48
+ end
49
+ res << ">"
50
+ res
51
+ end
52
+
53
+ def close
54
+ "</#{@tag_name}>"
55
+ end
56
+
57
+ def build(options = {})
58
+ res = open
59
+ # res << "\n" unless options[:compact]
60
+ if block_given?
61
+ res << yield.to_s
62
+ # res << "\n" unless options[:compact]
63
+ res << close
64
+ # res << "\n" unless options[:compact]
65
+ elsif options[:body]
66
+ res << options[:body]
67
+ # res << "\n" unless options[:compact]
68
+ res << close
69
+ # res << "\n" unless options[:compact]
70
+ end
71
+ res
72
+ end
73
+ alias :to_html :build
74
+
75
+ def self.to_html(tag_name, options = {})
76
+ instance = self.new(tag_name, body, options = {})
77
+ instance.build(body)
78
+ end
79
+
80
+ end
81
+ end
@@ -1,5 +1,8 @@
1
1
  module Rubypivot
2
-
2
+ #
3
+ # 2020-12-28 First
4
+ # 2020-12-30 Build an hash instead array
5
+ #
3
6
  class Pivot
4
7
  DEFAULT_OPTIONS = {
5
8
  data_type: :integer, # :integer, :float or :string
@@ -9,7 +12,7 @@ module Rubypivot
9
12
  row_header: true,
10
13
  # column_lookup: HashName
11
14
  # row_lookup: HashName
12
- # :row_total: 'Title for total column'
15
+ # row_total: 'Title for total column, if nil row total not calculated'
13
16
  }.freeze
14
17
 
15
18
  attr_accessor :options
@@ -50,6 +53,10 @@ module Rubypivot
50
53
  @row_titles || make_row_list
51
54
  end
52
55
 
56
+ def column_head(row_title)
57
+ @options[:row_lookup] ? @options[:row_lookup][row_title] : row_title
58
+ end
59
+
53
60
  def make_column_list
54
61
  @column_titles = [] unless @column_titles
55
62
  @source_data.each do |each_line|
@@ -84,35 +91,50 @@ module Rubypivot
84
91
  self
85
92
  end
86
93
 
87
- def column_header
88
- res = []
89
- res << '' if @options[:row_header]
90
- if @options[:column_lookup]
91
- @column_titles.each do |column|
92
- res << @options[:column_lookup][column]
93
- end
94
- else
95
- res += @column_titles
94
+ # return an pivot hash. data rows only
95
+ def build_data
96
+ parse_data unless @rows_parsed
97
+ res = {}
98
+ @row_titles.each do |row_title|
99
+ row = @rows_parsed.get_row(row_title)
100
+ title = column_head(row_title)
101
+ res[title] = row.to_a(column_titles)
96
102
  end
97
- res << @options[:row_total] if @options[:row_total]
98
103
  res
99
104
  end
105
+ alias :build_hash :build_data
100
106
 
101
- # return an pivot array
107
+ # return an pivot array with titles
102
108
  def build
103
109
  parse_data unless @rows_parsed
104
110
  res = []
105
- res << column_header if @options[:header]
111
+ res << header_row if @options[:header]
106
112
  @row_titles.each do |row_title|
107
113
  row = @rows_parsed.get_row(row_title)
108
114
  data_array = []
109
- data_array << @rows_parsed.header(row_title) if @options[:row_header]
115
+ data_array << column_head(row_title) if @options[:row_header]
110
116
  data_array += row.to_a(column_titles)
111
117
  data_array << row.total(column_titles) if @options[:row_total]
112
118
  res << data_array
113
119
  end
114
120
  res
115
121
  end
122
+ alias :build_array :build
123
+
124
+ # Make an array
125
+ def header_row(title = nil)
126
+ res = []
127
+ res << "#{title}" if @options[:row_header]
128
+ if @options[:column_lookup]
129
+ @column_titles.each do |column|
130
+ res << @options[:column_lookup][column]
131
+ end
132
+ else
133
+ res += @column_titles
134
+ end
135
+ res << @options[:row_total] if @options[:row_total]
136
+ res
137
+ end
116
138
 
117
139
  def total_row(title = nil)
118
140
  parse_data unless @rows_parsed
@@ -122,10 +144,6 @@ module Rubypivot
122
144
  res += @rows_parsed.total(@column_titles, @options[:row_total])
123
145
  end
124
146
 
125
- def self.build(data, column_name, row_name, data_name, options = {})
126
- obj = self.new(data, column_name, row_name, data_name, options)
127
- obj.build
128
- end
129
147
  # get column title or row title
130
148
  def self.get_title(name, line)
131
149
  if line.is_a?(Hash)
@@ -144,6 +162,10 @@ module Rubypivot
144
162
  line.send(name)
145
163
  end
146
164
  end
147
-
165
+
166
+ def self.build(data, column_name, row_name, data_name, options = {})
167
+ obj = self.new(data, column_name, row_name, data_name, options)
168
+ obj.build
169
+ end
148
170
  end
149
171
  end
@@ -4,7 +4,6 @@ module Rubypivot
4
4
  def initialize(options = {})
5
5
  @options = options
6
6
  @data_type = options[:data_type]
7
- @lookup = options[:row_lookup]
8
7
  @rows = {}
9
8
  end
10
9
 
@@ -13,10 +12,6 @@ module Rubypivot
13
12
  end
14
13
  alias :add_row :get_row
15
14
 
16
- def header(row_title)
17
- @lookup ? @lookup[row_title] : row_title
18
- end
19
-
20
15
  def total(column_titles = [], show_grand_total = false)
21
16
  return ['Total', 'row', 'can', 'not', 'create', "type :#{@data_type}"] unless [:integer, :float].include?(@data_type)
22
17
  grand_total = @data_type == :float ? 0.0 : 0
@@ -0,0 +1,334 @@
1
+ # Create spread sheet array of row objects (SpreadTableLine) from array
2
+ # having row (tr) and cell (td) attributes
3
+ # to help HTML table
4
+ #
5
+ # 2020-12-31
6
+ #
7
+
8
+ class Array
9
+ def to_spread(options = {})
10
+ spread_array = SpreadTable.new(self, options)
11
+ end
12
+ end
13
+
14
+ module Rubypivot
15
+ class SpreadTableError < StandardError; end
16
+
17
+ class SpreadTableLine
18
+ LINE_TYPES = {
19
+ header: 'Header',
20
+ data: 'Data ',
21
+ total: 'Total ',
22
+ }.freeze
23
+
24
+ attr_reader :line_type, :options
25
+ attr_accessor :line_data, :title, :attribut
26
+ def initialize(line_type, line_data = [], options = {})
27
+ @options = options
28
+ @attribute = {}
29
+ @attribs = []
30
+ set_line_type(line_type, @options)
31
+ # Desparate to have an array, convert it if not
32
+ if line_data.is_a?(Array)
33
+ @line_data = line_data
34
+ elsif line_data.is_a?(Hash)
35
+ @line_data = []
36
+ line_data.each do |key, value|
37
+ @line_data << value
38
+ end
39
+ elsif line_data.is_a?(String)
40
+ @line_data = line_data.split(/[ \t]+/)
41
+ else
42
+ @line_data = [line_data]
43
+ end
44
+ if options[:title]
45
+ if options[:title] == :first
46
+ @title = @line_data.shift
47
+ else
48
+ @title = options[:title].to_s
49
+ end
50
+ end
51
+ end
52
+
53
+ def set_line_type(line_type, options = {})
54
+ @line_type = line_type
55
+ @line_type = :data unless LINE_TYPES[@line_type] # at least vaid must be set
56
+ if @line_type == :data
57
+ @attribute[:line_class] = options[:data_line_class] if options[:data_line_class]
58
+ @attribute[:title_class] = options[:data_title_class] if options[:data_title_class]
59
+ else
60
+ @attribute[:line_class] = options[:header_line_class] if options[:header_line_class]
61
+ @attribute[:title_class] = options[:header_title_class] if options[:header_title_class]
62
+ end
63
+ if @line_type == :header
64
+ #
65
+ else
66
+ @attribute[:data_format] = options[:data_format] if options[:data_format]
67
+ end
68
+ end
69
+
70
+ def data_width
71
+ @line_data.size
72
+ end
73
+
74
+ def set_line_class(klass)
75
+ @attribute[:line_class] = klass
76
+ end
77
+
78
+ def set_td_class(klass)
79
+ @attribute[:cell_class] = klass
80
+ end
81
+
82
+ def set_title_class(klass)
83
+ @attribute[:cell_class] = klass
84
+ end
85
+
86
+ def set_cell_class(callback)
87
+ return unless callback
88
+ if callback.is_a?(Method)
89
+ @line_data.each_with_index do |cell_data, idx|
90
+ klass = callback.call(cell_data, @title)
91
+ @attribs[idx] = klass if klass
92
+ end
93
+ else
94
+ @line_data.each_with_index do |cell_data, idx|
95
+ @attribs[idx] = callback.to_s
96
+ end
97
+ end
98
+ end
99
+
100
+ def set_cell_callback(callback)
101
+ return if callback.nil? || !callback.is_a?(Method)
102
+ if @line_type == :header
103
+ @attribute[:cell_callback] = callback
104
+ else
105
+ @attribute[:cell_callback] = callback
106
+ end
107
+ end
108
+
109
+ def line_data_formatted
110
+ return @line_data if @line_type == :header || @attribute[:data_format].nil?
111
+ res = []
112
+ @line_data.each do |data|
113
+ res << @attribute[:data_format] % data
114
+ end
115
+ res
116
+ end
117
+
118
+ def to_s
119
+ res = ""
120
+ res << "#{LINE_TYPES[@line_type]}: "
121
+ res << "#{@title}: " if @title
122
+ res << line_data_formatted.join(', ')
123
+ res << " : Line Class: #{@attribute[:line_class]}"
124
+ res << " : Cell Class: #{@attribute[:cell_class]}"
125
+ res << " : Title Class: #{@attribute[:title_class]}"
126
+ res
127
+ end
128
+
129
+ def to_html(options = {})
130
+ tr = Rubypivot::HtmlTag.new('tr', class: @attribute[:line_class])
131
+ td_str = ""
132
+ if @title
133
+ td = Rubypivot::HtmlTag.new('td', class: @attribute[:title_class] || @attribute[:cell_class])
134
+ td_str << td.build{ @title }
135
+ end
136
+ line_data_formatted.each_with_index do |cell_data, idx|
137
+ if @attribute[:cell_callback]
138
+ td_str << @attribute[:cell_callback].call(cell_data, @title)
139
+ else
140
+ td = Rubypivot::HtmlTag.new('td', class: @attribs[idx])
141
+ td_str << td.build{ cell_data }
142
+ end
143
+ end
144
+ res = tr.build { td_str }
145
+ res << "\n" if options[:line_end] == :cr
146
+ res
147
+ end
148
+
149
+ def make_col_class(options = {})
150
+ klass = "col"
151
+ klass << "-#{@attribute[:line_class]}" if @attribute[:line_class]
152
+ klass << "-#{options[:width]}" if options[:width]
153
+ # klass << " #{options[:klass]}" if options[:klass]
154
+ klass
155
+ end
156
+
157
+ def to_grid(framework = :bootstrap, widths = [], options = {})
158
+ res = "<div class=\"row\">"
159
+ if @title
160
+ div = Rubypivot::HtmlTag.new('div', class: make_col_class(width: widths[0])) # klass: @attribute[:title_class]
161
+ res << div.build{ @title }
162
+ end
163
+ line_data_formatted.each_with_index do |cell_data, idx|
164
+ div = Rubypivot::HtmlTag.new('div', class: make_col_class(width: widths[idx + 1])) # klass: @attribute[:title_class]
165
+ res << div.build{ cell_data }
166
+ end
167
+ res << "</div>/n"
168
+ res
169
+ end
170
+ end
171
+
172
+ class SpreadTable
173
+ attr_reader :data_width, :data_height, :total_width
174
+ attr_accessor :options, :rows
175
+ def initialize(data_source, options = {})
176
+ @rows = []
177
+ @options = {}
178
+ @options_for_line = {}
179
+ options.each do |k, v|
180
+ if [:title, :data_line_class, :header_line_class, :data_title_class, :header_title_class, :data_format].include?(k)
181
+ @options_for_line[k] = v
182
+ else
183
+ @options[k] = v
184
+ end
185
+ end
186
+ @attribs = []
187
+
188
+ if data_source.is_a? Array
189
+ data_source.each do |line|
190
+ @rows << SpreadTableLine.new(:data, line, @options_for_line)
191
+ end
192
+ elsif data_source.is_a? Hash
193
+ data_source.each do |title, values|
194
+ @rows << SpreadTableLine.new(:data, values, title: title)
195
+ end
196
+ else
197
+ @rows << SpreadTableLine.new(:data, line, line_options)
198
+ end
199
+ set_line_type(:header, @options[:header], false)
200
+ set_line_type(:total, @options[:total], false)
201
+ calc_data_size
202
+ end
203
+
204
+ def set_line_type(line_type, position = nil, recalc = true)
205
+ return if line_type.nil? || position.nil?
206
+ return unless SpreadTableLine::LINE_TYPES[line_type]
207
+ case position
208
+ when :first, :top
209
+ @rows.first.set_line_type(line_type, @options_for_line)
210
+ when :last, :bottom
211
+ @rows.last.set_line_type(line_type, @options_for_line)
212
+ else
213
+ row = get_row(position)
214
+ if row
215
+ @rows[pos].set_line_type(line_type, @options_for_line)
216
+ end
217
+ end
218
+ calc_data_size if recalc
219
+ end
220
+
221
+ def add_line(line_type = :data, line = [], line_options = {})
222
+ @rows << SpreadTableLine.new(line_type, line, line_options)
223
+ calc_data_size
224
+ end
225
+
226
+ def get_row(position)
227
+ if position.is_a?(Symbol)
228
+ if position == :last
229
+ @rows.last
230
+ else
231
+ @rows.first
232
+ end
233
+ else
234
+ pos = position.to_i
235
+ @rows[pos] if pos >= 0 && pos < @rows.size - 1
236
+ end
237
+ end
238
+ alias :line :get_row
239
+
240
+ def total_height
241
+ @rows.size
242
+ end
243
+
244
+ def calc_data_size
245
+ @total_width = 0
246
+ @data_width = 0
247
+ @data_height = 0
248
+ @rows.each do |row|
249
+ w = row.data_width
250
+ @total_width = w if @total_width < w
251
+ next if row.line_type != :data
252
+ @data_width = w if @data_width < w
253
+ @data_height += 1
254
+ end
255
+ @total_width += 1 # Including title column
256
+ self
257
+ end
258
+
259
+ def set_cell_class(callback)
260
+ return unless callback
261
+ each_data_line do |line|
262
+ line.set_cell_class(callback)
263
+ end
264
+ end
265
+
266
+ def set_line_class(klass)
267
+ return unless klass
268
+ each do |line|
269
+ line.set_line_class(klass)
270
+ end
271
+ end
272
+
273
+ def set_cell_callback(callback)
274
+ return if callback.nil? || !callback.is_a?(Method)
275
+ each_non_header_line do |line|
276
+ line.set_cell_callback(callback)
277
+ end
278
+ end
279
+
280
+ def each
281
+ @rows.each do |row|
282
+ yield row
283
+ end
284
+ end
285
+
286
+ def each_data_line
287
+ @rows.each do |row|
288
+ next if row.line_type != :data
289
+ yield row
290
+ end
291
+ end
292
+
293
+ def each_header_line
294
+ @rows.each do |row|
295
+ next if row.line_type == :data
296
+ yield row
297
+ end
298
+ end
299
+
300
+ def each_non_header_line
301
+ @rows.each do |row|
302
+ next if row.line_type == :header
303
+ yield row
304
+ end
305
+ end
306
+
307
+ def to_s
308
+ @rows.each do |row|
309
+ puts row.to_s
310
+ end
311
+ end
312
+
313
+ def to_html(options = {})
314
+ line_end = options.delete(:line_end)
315
+ res = HtmlTag.new('table', options).open
316
+ res << "\n" if line_end == :cr
317
+ @rows.each do |row|
318
+ res << row.to_html(line_end: line_end)
319
+ end
320
+ res << "</table>\n"
321
+ res
322
+ end
323
+ # Bootstrap grid
324
+ def to_grid(framework = :bootstrap, widths = [], options = {})
325
+ res = ""
326
+ @rows.each do |row|
327
+ res << row.to_grid(framework, widths)
328
+ end
329
+ res
330
+ end
331
+
332
+ end
333
+
334
+ end
@@ -0,0 +1,95 @@
1
+ module Rubypivot
2
+
3
+ # Create HTML table from array
4
+ # having row (tr) and cell (td) attribute control
5
+ # rubypivot main gem does not include this, maybe won't be using
6
+ #
7
+ class TableBuilder
8
+ attr_accessor :options
9
+ attr_reader :x_size, :y_size
10
+ def initialize(data_array, options = {})
11
+ raise StandardError, "Data source must be an two dimension array" if !data_array.is_a?(Array) || !data_array.first.is_a?(Array)
12
+ @options = options
13
+ @data_array = data_array
14
+ @attributes = []
15
+ @data_array.each do |line|
16
+ @attributes << [options[:tr_class]]
17
+ end
18
+ @x_size = @data_array.first.size
19
+ @y_size = @data_array.size
20
+ end
21
+
22
+ def build(options = {})
23
+ tr_classes(options[:tr_class]) if options[:tr_class]
24
+ res = open
25
+ @data_array.each_with_index do |line, y|
26
+ tr = HtmlTag.new("tr", class: @attributes[y][0])
27
+ res << tr.open
28
+ line.each_with_index do |td_data, x|
29
+ res << HtmlTag.new("td", class: @attributes[y][x + 1]).build{ td_data }
30
+ end
31
+ res << tr.close + "\n"
32
+ end
33
+ # res << "\n"
34
+ res << close
35
+ res
36
+ end
37
+
38
+ def open
39
+ res = HtmlTag.new('table', @options).open
40
+ res << "\n"
41
+ res
42
+ end
43
+
44
+ def close(options = {})
45
+ "</table>\n"
46
+ end
47
+
48
+ def range_check(y)
49
+ if y.is_a?(Symbol)
50
+ if y == :bottom
51
+ return @y_size - 1
52
+ else
53
+ return 0
54
+ end
55
+ end
56
+ raise StandardError, "Class set out of range: #{y} > #{@y_size}" if y > @y_size
57
+ y
58
+ end
59
+
60
+ def tr_class(klass, y)
61
+ return unless klass
62
+ y = range_check(y)
63
+ @attributes[y][0] = klass
64
+ end
65
+
66
+ def tr_classes(klass)
67
+ return unless klass
68
+ 0.upto(@attributes.size - 1) do |y|
69
+ @attributes[y][0] = klass
70
+ end
71
+ end
72
+
73
+ def set_class(klass, y, x)
74
+ return unless klass
75
+ y = range_check(y)
76
+ raise StandardError, "Class set out of range: #{} > #{@x_size}" if x >= @x_size
77
+ @attributes[y][x + 1] = klass
78
+ end
79
+
80
+ def row_attributes(klass, y)
81
+ return unless klass
82
+ y = range_check(y)
83
+ 1.upto(@x_size) do |pos|
84
+ @attributes[y][pos] = klass
85
+ end
86
+ end
87
+
88
+ def column_attributes(klass, x)
89
+ return unless klass
90
+ 0.upto(@attributes.size - 1) do |y|
91
+ @attributes[y][x + 1] = klass
92
+ end
93
+ end
94
+ end
95
+ end
@@ -1,3 +1,3 @@
1
1
  module Rubypivot
2
- VERSION = "0.0.1" # Pre-release
2
+ VERSION = "0.0.2"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubypivot
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - hiro utsumi
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-28 00:00:00.000000000 Z
11
+ date: 2021-01-01 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Transforming a dataset or array of hashes into a spreadsheet-style array
14
14
  email:
@@ -26,12 +26,19 @@ files:
26
26
  - bin/console
27
27
  - bin/setup
28
28
  - examples/active_record_like.rb
29
+ - examples/html_table.html
30
+ - examples/html_tag.rb
29
31
  - examples/lookup.rb
30
32
  - examples/simple.rb
33
+ - examples/spread_table.png
34
+ - examples/spread_table.rb
31
35
  - lib/rubypivot.rb
36
+ - lib/rubypivot/html_tag.rb
32
37
  - lib/rubypivot/pivot.rb
33
38
  - lib/rubypivot/pivot_column.rb
34
39
  - lib/rubypivot/pivot_row.rb
40
+ - lib/rubypivot/spread_table.rb
41
+ - lib/rubypivot/table.rb
35
42
  - lib/rubypivot/version.rb
36
43
  - rubypivot.gemspec
37
44
  homepage: https://github.com/gambldia/rubypivot