data-table 1.0.0 → 2.0.3
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 +7 -0
- data/.gitignore +4 -0
- data/.rspec +2 -0
- data/.travis.yml +5 -0
- data/Guardfile +48 -0
- data/LICENSE +21 -0
- data/README.md +135 -0
- data/Rakefile +5 -0
- data/assigments_table.html +56 -0
- data/data-table.gemspec +17 -12
- data/examples/all_features.rb +161 -0
- data/lib/data-table.rb +62 -3
- data/lib/data-table/column.rb +75 -0
- data/lib/data-table/enum.rb +57 -0
- data/lib/data-table/table.rb +458 -0
- data/lib/data-table/version.rb +2 -2
- data/rebuild_gem.rb +11 -0
- data/spec/column_spec.rb +41 -0
- data/spec/data_table_spec.rb +22 -0
- data/spec/enum_spec.rb +36 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/table_spec.rb +281 -0
- metadata +89 -18
- data/lib/data-table/data_table.rb +0 -385
- data/lib/data-table/data_table_column.rb +0 -60
data/lib/data-table/version.rb
CHANGED
@@ -1,3 +1,3 @@
|
|
1
|
-
|
2
|
-
VERSION =
|
1
|
+
module DataTable
|
2
|
+
VERSION = '2.0.3'.freeze
|
3
3
|
end
|
data/rebuild_gem.rb
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
puts "Removing old gem file"
|
2
|
+
`rm data-table*.gem`
|
3
|
+
|
4
|
+
puts "Building new data-table gem"
|
5
|
+
`gem build ./data-table.gemspec`
|
6
|
+
|
7
|
+
puts "Uninstalling old data-table gem"
|
8
|
+
`gem uninstall -a data-table`
|
9
|
+
|
10
|
+
puts "Installing new build of data-table gem"
|
11
|
+
`gem install ./data-table*.gem`
|
data/spec/column_spec.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DataTable::Column do
|
4
|
+
it "should store the name" do
|
5
|
+
column = DataTable::Column.new(:thing)
|
6
|
+
expect(column.name).to eq(:thing)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should add the column name as a css class" do
|
10
|
+
column = DataTable::Column.new(:thing)
|
11
|
+
expect(column.css_class_names).to include('thing')
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should render a td tag" do
|
15
|
+
column = DataTable::Column.new(:thing)
|
16
|
+
expect(column.render_cell("Data")).to eq(%(<td class='thing text' >Data</td>))
|
17
|
+
end
|
18
|
+
|
19
|
+
it "should render the column header" do
|
20
|
+
column = DataTable::Column.new(:thing, 'Thing')
|
21
|
+
expect(column.render_column_header).to eq(%(<th class='thing ' >Thing</th>))
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should add custom attributes to the td tag" do
|
25
|
+
options = {
|
26
|
+
attributes: {
|
27
|
+
'data-type' => 'text',
|
28
|
+
'data-id' => 1
|
29
|
+
}
|
30
|
+
}
|
31
|
+
column = DataTable::Column.new(:thing, 'Thing', options)
|
32
|
+
expect(column.custom_attributes).to eq("data-type='text' data-id='1'")
|
33
|
+
expect(column.render_cell('Data')).to include("data-type='text'")
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should use the block for rendering" do
|
37
|
+
square = lambda { |v| v.to_i ** 2 }
|
38
|
+
column = DataTable::Column.new(:amount, 'Amount', &square)
|
39
|
+
expect(column.render_cell(5, amount: 5)).to eq(%(<td class='amount numeric' >25</td>))
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DataTable do
|
4
|
+
let(:collection) {
|
5
|
+
[
|
6
|
+
{:name => 'Luke Skywalker', :class => 'Jedi Knight'},
|
7
|
+
{:name => 'Emporer Palpatine', :class => 'Sith Lord'},
|
8
|
+
{:name => 'Mithrander', :class => 'Wizard'},
|
9
|
+
{:name => 'Aragorn', :class => 'Ranger'}
|
10
|
+
]
|
11
|
+
}
|
12
|
+
|
13
|
+
|
14
|
+
it "should render the collection" do
|
15
|
+
html = DataTable.render(collection) do |t|
|
16
|
+
t.column :name, 'Name'
|
17
|
+
t.column :class, 'Class'
|
18
|
+
end
|
19
|
+
|
20
|
+
expect(html).to eq(%{<table id='' class='data_table ' cellspacing='0' cellpadding='0'><caption></caption><thead><tr><th class='name ' >Name</th><th class='class ' >Class</th></tr></thead><tbody><tr class='row_0 ' ><td class='name text' >Luke Skywalker</td><td class='class text' >Jedi Knight</td></tr><tr class='row_1 alt ' ><td class='name text' >Emporer Palpatine</td><td class='class text' >Sith Lord</td></tr><tr class='row_2 ' ><td class='name text' >Mithrander</td><td class='class text' >Wizard</td></tr><tr class='row_3 alt ' ><td class='name text' >Aragorn</td><td class='class text' >Ranger</td></tr></tbody></table>})
|
21
|
+
end
|
22
|
+
end
|
data/spec/enum_spec.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Enumerable do
|
4
|
+
context "with a non-empty collection of hashes" do
|
5
|
+
let(:collection) {
|
6
|
+
[
|
7
|
+
{name: 'Luke Skywalker', class: 'Jedi Knight', world: 'Star Wars', power_level: 50},
|
8
|
+
{name: 'Emporer Palpatine', class: 'Sith Lord', world: 'Star Wars', power_level: 95},
|
9
|
+
{name: 'Mithrander', class: 'Wizard', world: 'Middle Earth', power_level: 9001},
|
10
|
+
{name: 'Aragorn', class: 'Ranger', world: 'Middle Earth', power_level: 80}
|
11
|
+
]
|
12
|
+
}
|
13
|
+
|
14
|
+
let(:groupings) { [:class] }
|
15
|
+
|
16
|
+
it "should transform a collection into nested hash based on and array of groups" do
|
17
|
+
expect(
|
18
|
+
collection.group_by_recursive(groupings)
|
19
|
+
).to eq(
|
20
|
+
{
|
21
|
+
"Jedi Knight"=>[{:name=>"Luke Skywalker", :class=>"Jedi Knight", :world=>"Star Wars", :power_level=>50}],
|
22
|
+
"Sith Lord"=>[{:name=>"Emporer Palpatine", :class=>"Sith Lord", :world=>"Star Wars", :power_level=>95}],
|
23
|
+
"Wizard"=>[{:name=>"Mithrander", :class=>"Wizard", :world=>"Middle Earth", :power_level=>9001}],
|
24
|
+
"Ranger"=>[{:name=>"Aragorn", :class=>"Ranger", :world=>"Middle Earth", :power_level=>80}]
|
25
|
+
}
|
26
|
+
)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should traverse a nested hash" do
|
30
|
+
grouped_collection = collection.group_by_recursive(groupings)
|
31
|
+
ungrouped = []
|
32
|
+
grouped_collection.each_pair_recursive { |_k, v| ungrouped.concat(v) }
|
33
|
+
expect(ungrouped).to eq(collection)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'data-table'
|
2
|
+
require 'pry'
|
3
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
4
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
5
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
6
|
+
# loaded once.
|
7
|
+
#
|
8
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
9
|
+
RSpec.configure do |config|
|
10
|
+
config.run_all_when_everything_filtered = true
|
11
|
+
config.filter_run :focus
|
12
|
+
|
13
|
+
# Run specs in random order to surface order dependencies. If you find an
|
14
|
+
# order dependency and want to debug it, you can fix the order by providing
|
15
|
+
# the seed, which is printed after each run.
|
16
|
+
# --seed 1234
|
17
|
+
config.order = 'random'
|
18
|
+
end
|
data/spec/table_spec.rb
ADDED
@@ -0,0 +1,281 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe DataTable::Table do
|
4
|
+
context "with a non-empty collection of hashes" do
|
5
|
+
let(:collection) {
|
6
|
+
[
|
7
|
+
{:name => 'Luke Skywalker', :class => 'Jedi Knight', :world => 'Star Wars', :power_level => 50},
|
8
|
+
{:name => 'Emporer Palpatine', :class => 'Sith Lord', :world => 'Star Wars', :power_level => 95},
|
9
|
+
{:name => 'Mithrander', :class => 'Wizard', :world => 'Middle Earth', :power_level => 9001},
|
10
|
+
{:name => 'Aragorn', :class => 'Ranger', :world => 'Middle Earth', :power_level => 80}
|
11
|
+
]
|
12
|
+
}
|
13
|
+
|
14
|
+
let(:data_table) {DataTable::Table.new(collection)}
|
15
|
+
|
16
|
+
it "should add a column do @columns" do
|
17
|
+
data_table.column(:name, 'Name')
|
18
|
+
expect(data_table.columns).to_not be_empty
|
19
|
+
expect(data_table.columns.first.class).to be(DataTable::Column)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should render the collection" do
|
23
|
+
data_table.column(:name, 'Name')
|
24
|
+
data_table.column(:class, 'Class')
|
25
|
+
expect(data_table.render).to \
|
26
|
+
eq(%{<table id='' class='data_table ' cellspacing='0' cellpadding='0'><caption></caption><thead><tr><th class='name ' >Name</th><th class='class ' >Class</th></tr></thead><tbody><tr class='row_0 ' ><td class='name text' >Luke Skywalker</td><td class='class text' >Jedi Knight</td></tr><tr class='row_1 alt ' ><td class='name text' >Emporer Palpatine</td><td class='class text' >Sith Lord</td></tr><tr class='row_2 ' ><td class='name text' >Mithrander</td><td class='class text' >Wizard</td></tr><tr class='row_3 alt ' ><td class='name text' >Aragorn</td><td class='class text' >Ranger</td></tr></tbody></table>})
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should group the records" do
|
30
|
+
grouping_column = :world
|
31
|
+
|
32
|
+
data_table.group_by grouping_column, level: 0
|
33
|
+
data_table.column(:name, 'Name')
|
34
|
+
data_table.column(:class, 'Class')
|
35
|
+
expect(data_table.grouped_data).to be true
|
36
|
+
data_table.prepare_data
|
37
|
+
expect(data_table.collection).to eq(collection.group_by {|g| g[grouping_column]})
|
38
|
+
expect(data_table.render).to eq(%{<table id='' class='data_table ' cellspacing='0' cellpadding='0'><caption></caption><thead><tr><th class='name ' >Name</th><th class='class ' >Class</th></tr></thead><tbody class='star_wars'><tr class='group_header level_0'><th colspan='2'>Star Wars</th></tr><tr class='row_0 ' ><td class='name text' >Luke Skywalker</td><td class='class text' >Jedi Knight</td></tr><tr class='row_1 alt ' ><td class='name text' >Emporer Palpatine</td><td class='class text' >Sith Lord</td></tr></tbody><tbody class='middle_earth'><tr class='group_header level_0'><th colspan='2'>Middle Earth</th></tr><tr class='row_0 ' ><td class='name text' >Mithrander</td><td class='class text' >Wizard</td></tr><tr class='row_1 alt ' ><td class='name text' >Aragorn</td><td class='class text' >Ranger</td></tr></tbody></table>})
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should do totaling" do
|
42
|
+
data_table.column :power_level
|
43
|
+
data_table.total :power_level, :sum, 0
|
44
|
+
data_table.calculate_totals!
|
45
|
+
expect(data_table.total_calculations).to eq([{:power_level=>9226.0}])
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should do custom formatting for the total" do
|
49
|
+
data_table.column :power_level
|
50
|
+
data_table.total :power_level, :avg, 0 do |average|
|
51
|
+
"#{average / 100.0}%"
|
52
|
+
end
|
53
|
+
data_table.calculate_totals!
|
54
|
+
expect(data_table.total_calculations).to eq([{:power_level=>"23.065%"}])
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should do custom totalling" do
|
58
|
+
data_table.column :power_level
|
59
|
+
data_table.total :power_level do |collection|
|
60
|
+
collection.inject(0) { |sum, c| sum + c[:power_level] }
|
61
|
+
end
|
62
|
+
data_table.calculate_totals!
|
63
|
+
expect(data_table.total_calculations).to eq([{:power_level=>9226}])
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should do sub-totaling" do
|
67
|
+
data_table.group_by :world, level: 0
|
68
|
+
data_table.column :power_level
|
69
|
+
data_table.subtotal :power_level, :sum, 0
|
70
|
+
|
71
|
+
data_table.prepare_data
|
72
|
+
expect(data_table.subtotal_calculations).to eq({["Star Wars"]=>[{:power_level=>{:sum=>145.0}}], ["Middle Earth"]=>[{:power_level=>{:sum=>9081.0}}]})
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should do sub-totaling starting with indexes > 0" do
|
76
|
+
data_table.group_by :world, level: 0
|
77
|
+
data_table.column :power_level
|
78
|
+
data_table.subtotal :power_level, :sum, 1
|
79
|
+
|
80
|
+
data_table.prepare_data
|
81
|
+
|
82
|
+
expect(data_table.subtotal_calculations).to eq({
|
83
|
+
["Star Wars"] => [{}, {:power_level => {:sum => 145.0}}],
|
84
|
+
["Middle Earth"] => [{}, {:power_level => {:sum => 9081.0}}]
|
85
|
+
})
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should not render empty sub-total aggregate rows" do
|
89
|
+
data_table.group_by :world, level: 0
|
90
|
+
data_table.column :power_level
|
91
|
+
data_table.subtotal :power_level, nil, 1 do |_records, _column, path|
|
92
|
+
path
|
93
|
+
end
|
94
|
+
|
95
|
+
data_table.prepare_data
|
96
|
+
subtotal_calculations = data_table.subtotal_calculations
|
97
|
+
|
98
|
+
# this is convoluted because it's hard to assert a nested structure that includes procs
|
99
|
+
# [
|
100
|
+
# ["Middle Earth"] => [{}, {:power_level=>{#<Proc:0x03dbead8@table_spec.rb:78>=>"Middle Earth"}}],
|
101
|
+
# ["Star Wars"] => [{}, {:power_level=>{#<Proc:0x03dbead8@table_spec.rb:78>=>"Star Wars"}}]
|
102
|
+
# ]
|
103
|
+
expect(subtotal_calculations.keys).to eq([["Star Wars"], ["Middle Earth"]])
|
104
|
+
expect(subtotal_calculations.values.flatten.map(&:keys)).to eq([[], [:power_level], [], [:power_level]])
|
105
|
+
subtotal_calculations.values.flatten.map(&:values).flatten.map(&:keys).each do |k|
|
106
|
+
expect(k).to be_a(Array)
|
107
|
+
expect(k.length).to eq(1)
|
108
|
+
expect(k[0]).to be_a(Proc)
|
109
|
+
end
|
110
|
+
expect(subtotal_calculations.values.flatten.map(&:values).flatten.map(&:values)).to eq([["Star Wars"], ["Middle Earth"]])
|
111
|
+
|
112
|
+
# note how the rows are index_1, and there is no index_0 row
|
113
|
+
expect(data_table.render).to \
|
114
|
+
eq(%{<table id='' class='data_table ' cellspacing='0' cellpadding='0'><caption></caption><thead><tr><th class='power_level ' ></th></tr></thead><tbody class='star_wars'><tr class='group_header level_0'><th colspan='1'>Star Wars</th></tr><tr class='row_0 ' ><td class='power_level numeric' >50</td></tr><tr class='row_1 alt ' ><td class='power_level numeric' >95</td></tr><tr class='subtotal index_1 first'><td class='power_level numeric' >Star Wars</td></tr></tbody><tbody class='middle_earth'><tr class='group_header level_0'><th colspan='1'>Middle Earth</th></tr><tr class='row_0 ' ><td class='power_level numeric' >9001</td></tr><tr class='row_1 alt ' ><td class='power_level numeric' >80</td></tr><tr class='subtotal index_1 first'><td class='power_level numeric' >Middle Earth</td></tr></tbody></table>})
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should render a custom header" do
|
118
|
+
data_table.custom_header do
|
119
|
+
th 'Two Columns', :colspan => 2
|
120
|
+
th 'One Column', :colspan => 1
|
121
|
+
end
|
122
|
+
expect(data_table.render_custom_table_header).to eq(%{<tr class='custom-header'><th class="" colspan="2" style="">Two Columns</th><th class="" colspan="1" style="">One Column</th></tr>})
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
context "with an empty collection" do
|
127
|
+
let(:collection) {Array.new}
|
128
|
+
let(:data_table) {DataTable::Table.new(collection)}
|
129
|
+
|
130
|
+
it "should render a table with the 'no records' message" do
|
131
|
+
expect(data_table.render).to \
|
132
|
+
eq(%{<table id='' class='data_table ' cellspacing='0' cellpadding='0'><caption></caption><thead><tr></tr></thead><tr><td class='empty_data_table' colspan='0'>No records found</td></tr></table>})
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should render a custom empty text notice" do
|
136
|
+
text = "Nothing to see here"
|
137
|
+
data_table.empty_text = text
|
138
|
+
expect(data_table.render).to \
|
139
|
+
eq(%{<table id='' class='data_table ' cellspacing='0' cellpadding='0'><caption></caption><thead><tr></tr></thead><tr><td class='empty_data_table' colspan='0'>#{text}</td></tr></table>})
|
140
|
+
end
|
141
|
+
|
142
|
+
it "avoids dividing by zero" do
|
143
|
+
data_table.column :power_level
|
144
|
+
data_table.total :power_level, :avg, 0
|
145
|
+
data_table.calculate_totals!
|
146
|
+
expect(data_table.total_calculations).to eq([{:power_level=>0}])
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
context 'with a more complicated setup' do
|
151
|
+
it 'renders okay' do
|
152
|
+
raw_results = [
|
153
|
+
{ 'class' => 'Basketball', 'grade_level' => '9', 'points' => 50 },
|
154
|
+
{ 'class' => 'Basketball', 'grade_level' => '9', 'points' => 51 },
|
155
|
+
{ 'class' => 'Basketball', 'grade_level' => '10', 'points' => 52 },
|
156
|
+
{ 'class' => 'Basketball', 'grade_level' => '10', 'points' => 53 },
|
157
|
+
{ 'class' => 'Basketball', 'grade_level' => '10', 'points' => 54 },
|
158
|
+
{ 'class' => 'Basketball', 'grade_level' => '12', 'points' => 55 }
|
159
|
+
]
|
160
|
+
|
161
|
+
fields = [{
|
162
|
+
field_name: 'class',
|
163
|
+
display_description: 'Class',
|
164
|
+
column_width: 1.23,
|
165
|
+
data_type: 2
|
166
|
+
}, {
|
167
|
+
field_name: 'grade_level',
|
168
|
+
display_description: 'Grade Level',
|
169
|
+
column_width: 2.34,
|
170
|
+
data_type: 2
|
171
|
+
}, {
|
172
|
+
field_name: 'points',
|
173
|
+
display_description: 'Points',
|
174
|
+
column_width: 3.45,
|
175
|
+
data_type: 4
|
176
|
+
}]
|
177
|
+
|
178
|
+
column_groups = {}
|
179
|
+
|
180
|
+
subtotal_headers = [
|
181
|
+
{ field_name: 'class' },
|
182
|
+
{ field_name: 'grade_level' }
|
183
|
+
]
|
184
|
+
|
185
|
+
subtotal_aggregates = {
|
186
|
+
sum: [],
|
187
|
+
avg: [{
|
188
|
+
field_name: 'points',
|
189
|
+
data_type: 4
|
190
|
+
}],
|
191
|
+
min: [],
|
192
|
+
max: []
|
193
|
+
}
|
194
|
+
|
195
|
+
total_aggregates = {
|
196
|
+
sum: [],
|
197
|
+
avg: [],
|
198
|
+
min: [],
|
199
|
+
max: []
|
200
|
+
}
|
201
|
+
|
202
|
+
has_aggregates = true
|
203
|
+
|
204
|
+
raw_results.each_with_index do |record, index|
|
205
|
+
record[:___data_table_index___] = index
|
206
|
+
end
|
207
|
+
|
208
|
+
html = DataTable.render(raw_results) do |t|
|
209
|
+
if has_aggregates
|
210
|
+
t.column :__subtotal_header__, ' ', width: '30px' do |_v|
|
211
|
+
' '
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# COLUMN GROUPS
|
216
|
+
if column_groups.any?
|
217
|
+
t.custom_header do
|
218
|
+
th '', colspan: 1, css: 'column-group', style: 'width: 30px;' unless subtotal_headers.empty?
|
219
|
+
|
220
|
+
column_groups.each do |_column_group_index, column_group|
|
221
|
+
th column_group[:description], colspan: column_group[:column_count], css: 'column-group', style: "width: #{column_group_width}in;"
|
222
|
+
end
|
223
|
+
|
224
|
+
# spacer column
|
225
|
+
th '', colspan: 1, css: 'column-group'
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# COLUMNS
|
230
|
+
fields.each do |field|
|
231
|
+
t.column field[:field_name], field[:display_description], css_class: "data-type-#{field[:data_type]}", width: field[:column_width] do |_v, record|
|
232
|
+
record[field[:field_name]]
|
233
|
+
end
|
234
|
+
end
|
235
|
+
|
236
|
+
# SUBTOTAL HEADERS
|
237
|
+
subtotal_headers.each_with_index do |subtotal_header, index|
|
238
|
+
t.group_by subtotal_header[:field_name], level: index
|
239
|
+
end
|
240
|
+
|
241
|
+
# SUBTOTAL AGGREGATES
|
242
|
+
unless subtotal_headers.empty?
|
243
|
+
subtotal_aggregates.each_with_index do |(aggregate_function, columns), index|
|
244
|
+
next if columns.empty?
|
245
|
+
|
246
|
+
t.subtotal :__subtotal_header__, nil, index do |_records, _column, path|
|
247
|
+
"#{path}: #{aggregate_function.to_s.upcase}"
|
248
|
+
end
|
249
|
+
|
250
|
+
columns.each do |column|
|
251
|
+
t.subtotal column[:field_name], aggregate_function, index do |value|
|
252
|
+
value
|
253
|
+
end
|
254
|
+
end
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
# TOTAL AGGREGATES
|
259
|
+
total_aggregates.each_with_index do |(aggregate_function, columns), index|
|
260
|
+
next if columns.empty?
|
261
|
+
|
262
|
+
t.total :__subtotal_header__, nil, index do |_records|
|
263
|
+
aggregate_function.to_s.upcase
|
264
|
+
end
|
265
|
+
|
266
|
+
columns.each do |column|
|
267
|
+
t.total column[:field_name], aggregate_function, index do |value|
|
268
|
+
value
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# spacer column
|
274
|
+
t.column :_empty_space, ''
|
275
|
+
end
|
276
|
+
|
277
|
+
expected_html = %(<table id='' class='data_table ' cellspacing='0' cellpadding='0'><caption></caption><thead><tr><th class='__subtotal_header__ ' style='width: 30px'> </th><th class='points data-type-4' style='width: 3.45'>Points</th><th class='_empty_space ' ></th></tr></thead><tbody class='basketball'><tr class='group_header level_0'><th colspan='3'>Basketball</th></tr><tr class='group_header level_1'><th colspan='3'>9</th></tr><tr class='row_0 ' ><td class='__subtotal_header__ nilclass' > </td><td class='points numeric data-type-4' >50</td><td class='_empty_space nilclass' ></td></tr><tr class='row_1 alt ' ><td class='__subtotal_header__ nilclass' > </td><td class='points numeric data-type-4' >51</td><td class='_empty_space nilclass' ></td></tr><tr class='subtotal index_1 first'><td class='__subtotal_header__ nilclass' >9: AVG</td><td class='points numeric data-type-4' >50.5</td><td class='_empty_space nilclass' ></td></tr><tr class='group_header level_1'><th colspan='3'>10</th></tr><tr class='row_0 ' ><td class='__subtotal_header__ nilclass' > </td><td class='points numeric data-type-4' >52</td><td class='_empty_space nilclass' ></td></tr><tr class='row_1 alt ' ><td class='__subtotal_header__ nilclass' > </td><td class='points numeric data-type-4' >53</td><td class='_empty_space nilclass' ></td></tr><tr class='row_2 ' ><td class='__subtotal_header__ nilclass' > </td><td class='points numeric data-type-4' >54</td><td class='_empty_space nilclass' ></td></tr><tr class='subtotal index_1 first'><td class='__subtotal_header__ nilclass' >10: AVG</td><td class='points numeric data-type-4' >53.0</td><td class='_empty_space nilclass' ></td></tr><tr class='group_header level_1'><th colspan='3'>12</th></tr><tr class='row_0 ' ><td class='__subtotal_header__ nilclass' > </td><td class='points numeric data-type-4' >55</td><td class='_empty_space nilclass' ></td></tr><tr class='subtotal index_1 first'><td class='__subtotal_header__ nilclass' >12: AVG</td><td class='points numeric data-type-4' >55.0</td><td class='_empty_space nilclass' ></td></tr><tr class='parent_subtotal index_1 basketball'><td class='__subtotal_header__ nilclass' >Basketball: AVG</td><td class='points numeric data-type-4' >52.5</td><td class='_empty_space nilclass' ></td></tr></tbody></table>)
|
278
|
+
expect(html).to eq(expected_html)
|
279
|
+
end
|
280
|
+
end
|
281
|
+
end
|
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: data-table
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
5
|
-
prerelease:
|
4
|
+
version: 2.0.3
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Steve Erickson
|
@@ -10,46 +9,118 @@ authors:
|
|
10
9
|
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date:
|
14
|
-
dependencies:
|
15
|
-
|
16
|
-
|
12
|
+
date: 2020-06-26 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rake
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - "~>"
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '12'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - "~>"
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '12'
|
28
|
+
- !ruby/object:Gem::Dependency
|
29
|
+
name: rspec
|
30
|
+
requirement: !ruby/object:Gem::Requirement
|
31
|
+
requirements:
|
32
|
+
- - "~>"
|
33
|
+
- !ruby/object:Gem::Version
|
34
|
+
version: '3'
|
35
|
+
type: :development
|
36
|
+
prerelease: false
|
37
|
+
version_requirements: !ruby/object:Gem::Requirement
|
38
|
+
requirements:
|
39
|
+
- - "~>"
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
version: '3'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: guard
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - "~>"
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '2'
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - "~>"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: '2'
|
56
|
+
- !ruby/object:Gem::Dependency
|
57
|
+
name: guard-rspec
|
58
|
+
requirement: !ruby/object:Gem::Requirement
|
59
|
+
requirements:
|
60
|
+
- - "~>"
|
61
|
+
- !ruby/object:Gem::Version
|
62
|
+
version: '4'
|
63
|
+
type: :development
|
64
|
+
prerelease: false
|
65
|
+
version_requirements: !ruby/object:Gem::Requirement
|
66
|
+
requirements:
|
67
|
+
- - "~>"
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '4'
|
70
|
+
description: |-
|
71
|
+
data-table is a simple gem that provides a DSL for
|
72
|
+
turning an array of hashes or ActiveRecord objects into an
|
73
|
+
HTML table.
|
17
74
|
email:
|
18
75
|
- sixfeetover@gmail.com
|
19
76
|
executables: []
|
20
77
|
extensions: []
|
21
78
|
extra_rdoc_files: []
|
22
79
|
files:
|
23
|
-
- .gitignore
|
80
|
+
- ".gitignore"
|
81
|
+
- ".rspec"
|
82
|
+
- ".travis.yml"
|
24
83
|
- Gemfile
|
84
|
+
- Guardfile
|
85
|
+
- LICENSE
|
86
|
+
- README.md
|
25
87
|
- Rakefile
|
88
|
+
- assigments_table.html
|
26
89
|
- data-table.gemspec
|
90
|
+
- examples/all_features.rb
|
27
91
|
- lib/data-table.rb
|
28
|
-
- lib/data-table/
|
29
|
-
- lib/data-table/
|
92
|
+
- lib/data-table/column.rb
|
93
|
+
- lib/data-table/enum.rb
|
94
|
+
- lib/data-table/table.rb
|
30
95
|
- lib/data-table/version.rb
|
31
|
-
|
32
|
-
|
96
|
+
- rebuild_gem.rb
|
97
|
+
- spec/column_spec.rb
|
98
|
+
- spec/data_table_spec.rb
|
99
|
+
- spec/enum_spec.rb
|
100
|
+
- spec/spec_helper.rb
|
101
|
+
- spec/table_spec.rb
|
102
|
+
homepage: https://github.com/veracross/data-table
|
103
|
+
licenses:
|
104
|
+
- MIT
|
105
|
+
metadata: {}
|
33
106
|
post_install_message:
|
34
107
|
rdoc_options: []
|
35
108
|
require_paths:
|
36
109
|
- lib
|
37
110
|
required_ruby_version: !ruby/object:Gem::Requirement
|
38
|
-
none: false
|
39
111
|
requirements:
|
40
|
-
- -
|
112
|
+
- - ">="
|
41
113
|
- !ruby/object:Gem::Version
|
42
114
|
version: '0'
|
43
115
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
44
|
-
none: false
|
45
116
|
requirements:
|
46
|
-
- -
|
117
|
+
- - ">="
|
47
118
|
- !ruby/object:Gem::Version
|
48
119
|
version: '0'
|
49
120
|
requirements: []
|
50
|
-
rubyforge_project:
|
51
|
-
rubygems_version:
|
121
|
+
rubyforge_project:
|
122
|
+
rubygems_version: 2.7.6
|
52
123
|
signing_key:
|
53
|
-
specification_version:
|
124
|
+
specification_version: 4
|
54
125
|
summary: Turn arrays of hashes or models in to an HTML table.
|
55
126
|
test_files: []
|