rubypivot 0.0.1 → 0.0.2
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 +35 -2
- data/examples/html_table.html +61 -0
- data/examples/html_tag.rb +15 -0
- data/examples/simple.rb +11 -7
- data/examples/spread_table.png +0 -0
- data/examples/spread_table.rb +75 -0
- data/lib/rubypivot.rb +3 -2
- data/lib/rubypivot/html_tag.rb +81 -0
- data/lib/rubypivot/pivot.rb +42 -20
- data/lib/rubypivot/pivot_row.rb +0 -5
- data/lib/rubypivot/spread_table.rb +334 -0
- data/lib/rubypivot/table.rb +95 -0
- data/lib/rubypivot/version.rb +1 -1
- metadata +9 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a08da64e0fde946fe250518330143484176d4aae03f14d01d8c0597ec1b9631
|
4
|
+
data.tar.gz: 920c3745a938d8529a56acd051801ac18b9ec76d00ca62bb498e3043d080f446
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
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
|
+

|
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')
|
data/examples/simple.rb
CHANGED
@@ -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])
|
data/lib/rubypivot.rb
CHANGED
@@ -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
|
data/lib/rubypivot/pivot.rb
CHANGED
@@ -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
|
-
#
|
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
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
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 <<
|
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 <<
|
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
|
data/lib/rubypivot/pivot_row.rb
CHANGED
@@ -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
|
data/lib/rubypivot/version.rb
CHANGED
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.
|
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:
|
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
|