data-table 1.0.0 → 2.0.3
Sign up to get free protection for your applications and to get access to all the features.
- 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: []
|