dining-table 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +9 -0
- data/CHANGELOG.md +9 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +2 -0
- data/README.md +32 -5
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/dining-table.gemspec +16 -4
- data/lib/dining-table/columns/actions_column.rb +1 -1
- data/lib/dining-table/columns/column.rb +4 -3
- data/lib/dining-table/config.rb +0 -2
- data/lib/dining-table/presenters/csv_presenter.rb +2 -0
- data/lib/dining-table/presenters/html_presenter.rb +3 -3
- data/lib/dining-table/presenters/presenter.rb +5 -0
- data/lib/dining-table/presenters/spreadsheet_presenter.rb +1 -1
- data/lib/dining-table/table.rb +2 -0
- data/spec/collection.rb +23 -0
- data/spec/csv_table_spec.rb +54 -0
- data/spec/date_column.rb +12 -0
- data/spec/html_table_spec.rb +212 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/tables/car_table.rb +8 -0
- data/spec/tables/car_table_with_actions.rb +10 -0
- data/spec/tables/car_table_with_footer.rb +7 -0
- data/spec/tables/car_table_with_header.rb +7 -0
- data/spec/tables/car_table_with_options.rb +10 -0
- metadata +14 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5f2535c2cc619915268041c0bc43d5534172327c
|
4
|
+
data.tar.gz: d09637a314c28ba0928e3a25dd27a90aa0f7f184
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 980abf830c3bc889c0e4b1bb31927338ad07b1f75eb4c6f83b05f97d6cac6ef791774b1c3e22b0dfb39c11bff38285b0a53eb04b0b782ee807c0c39e16ea47b0
|
7
|
+
data.tar.gz: 4a19092fd8d14c30b3823f789debb18ad15443d2001434b099271e2c27432f778e184e9352506da3262918c0112288f81be90eee9b64a67f6aa0e5f72d3edd43
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -30,6 +30,7 @@ GEM
|
|
30
30
|
semver
|
31
31
|
jwt (1.5.1)
|
32
32
|
mini_portile2 (2.0.0)
|
33
|
+
minitest (5.9.0)
|
33
34
|
multi_json (1.12.0)
|
34
35
|
multi_xml (0.5.5)
|
35
36
|
multipart-post (2.0.0)
|
@@ -54,6 +55,7 @@ PLATFORMS
|
|
54
55
|
DEPENDENCIES
|
55
56
|
bundler (~> 1.0)
|
56
57
|
juwelier (~> 2.1.0)
|
58
|
+
minitest
|
57
59
|
|
58
60
|
BUNDLED WITH
|
59
61
|
1.12.3
|
data/README.md
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# dining-table
|
2
|
+
[](https://travis-ci.org/mvdamme/dining-table)
|
2
3
|
|
3
4
|
dining-table was inspired by the (now unfortunately unmaintained) [table_cloth](https://github.com/bobbytables/table_cloth) gem.
|
4
5
|
This gem is definitely not a drop-in replacement for [table-cloth](https://github.com/bobbytables/table_cloth), it aims to be less dependent on Rails
|
@@ -224,15 +225,15 @@ the following in a Rails controller action, for instance:
|
|
224
225
|
```ruby
|
225
226
|
def export_csv
|
226
227
|
collection = Car.order(:brand)
|
227
|
-
csv = CarTable.new( collection, nil, :
|
228
|
-
send_data( csv, :
|
228
|
+
csv = CarTable.new( collection, nil, presenter: DiningTable::Presenters::CSVPresenter.new ).render
|
229
|
+
send_data( csv, filename: 'export.csv', content_type: "text/csv" )
|
229
230
|
end
|
230
231
|
```
|
231
232
|
|
232
233
|
The CSV Presenter uses the CSV class from the Ruby standard library. Options passed in through the :csv key will be passed on to `CSV.new`:
|
233
234
|
|
234
235
|
```ruby
|
235
|
-
csv = CarTable.new( collection, nil, :
|
236
|
+
csv = CarTable.new( collection, nil, presenter: DiningTable::Presenters::CSVPresenter.new( csv: { col_sep: ';' } ) ).render
|
236
237
|
```
|
237
238
|
|
238
239
|
CSV options can also be set as defaults, see [Configuration](#configuration)
|
@@ -278,7 +279,33 @@ In order to use the Excel presenter, pass it in as a presenter and provide an ax
|
|
278
279
|
```ruby
|
279
280
|
collection = Car.order(:brand)
|
280
281
|
# sheet is the axlsx worksheet in which the table will be rendered
|
281
|
-
CarTable.new( collection, nil, :
|
282
|
+
CarTable.new( collection, nil, presenter: DiningTable::Presenters::ExcelPresenter.new( sheet ) ).render
|
283
|
+
```
|
284
|
+
|
285
|
+
## Custom column classes
|
286
|
+
|
287
|
+
You can write your own column classes and use them for specific columns. For instance, the following column class will format a date using `I18n`:
|
288
|
+
|
289
|
+
```ruby
|
290
|
+
class DateColumn < DiningTable::Columns::Column
|
291
|
+
def value(object)
|
292
|
+
val = super
|
293
|
+
I18n.l( val ) if val
|
294
|
+
end
|
295
|
+
end
|
296
|
+
```
|
297
|
+
|
298
|
+
A column class has to be derived from `DiningTable::Columns::Column` and implement the `value` method. The object passed in is the object in the
|
299
|
+
collection for which the current line is being rendered. If you don't have too many custom column classes, an easy place to put the code is in an initializer
|
300
|
+
(e.g. `config/initializers/dining-table.rb`).
|
301
|
+
|
302
|
+
You can use custom column classes as follows:
|
303
|
+
|
304
|
+
```ruby
|
305
|
+
class CarTable < DiningTable::Table
|
306
|
+
def define
|
307
|
+
column :fabrication_date, class: DateColumn
|
308
|
+
end
|
282
309
|
```
|
283
310
|
|
284
311
|
## Configuration <a name="configuration"></a>
|
@@ -289,7 +316,7 @@ You can set default options for the different presenters in an initializer (e.g.
|
|
289
316
|
DiningTable.configure do |config|
|
290
317
|
config.html_presenter.default_options = { class: 'table table-bordered table-hover',
|
291
318
|
wrap: { tag: :div, class: 'table-responsive' } }
|
292
|
-
config.csv_presenter.default_options = { :
|
319
|
+
config.csv_presenter.default_options = { csv: { col_sep: ';' } }
|
293
320
|
end
|
294
321
|
```
|
295
322
|
|
data/Rakefile
CHANGED
@@ -27,8 +27,8 @@ Juwelier::RubygemsDotOrgTasks.new
|
|
27
27
|
|
28
28
|
require 'rake/testtask'
|
29
29
|
Rake::TestTask.new(:test) do |test|
|
30
|
-
test.libs << 'lib' << '
|
31
|
-
test.pattern = '
|
30
|
+
test.libs << 'lib' << 'spec'
|
31
|
+
test.pattern = 'spec/**/*_spec.rb'
|
32
32
|
test.verbose = true
|
33
33
|
end
|
34
34
|
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/dining-table.gemspec
CHANGED
@@ -2,16 +2,16 @@
|
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
3
|
# Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
|
-
# stub: dining-table 0.
|
5
|
+
# stub: dining-table 0.2.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "dining-table"
|
9
|
-
s.version = "0.
|
9
|
+
s.version = "0.2.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib"]
|
13
13
|
s.authors = ["Micha\u{eb}l Van Damme"]
|
14
|
-
s.date = "2016-
|
14
|
+
s.date = "2016-09-03"
|
15
15
|
s.description = "Easily output tabular data, be it in HTML, CSV or XLSX. Create clean table classes instead of messing with views to create nice tables."
|
16
16
|
s.email = "michael.vandamme@vub.ac.be"
|
17
17
|
s.extra_rdoc_files = [
|
@@ -19,6 +19,8 @@ Gem::Specification.new do |s|
|
|
19
19
|
"README.md"
|
20
20
|
]
|
21
21
|
s.files = [
|
22
|
+
".travis.yml",
|
23
|
+
"CHANGELOG.md",
|
22
24
|
"Gemfile",
|
23
25
|
"Gemfile.lock",
|
24
26
|
"LICENSE",
|
@@ -35,7 +37,17 @@ Gem::Specification.new do |s|
|
|
35
37
|
"lib/dining-table/presenters/html_presenter.rb",
|
36
38
|
"lib/dining-table/presenters/presenter.rb",
|
37
39
|
"lib/dining-table/presenters/spreadsheet_presenter.rb",
|
38
|
-
"lib/dining-table/table.rb"
|
40
|
+
"lib/dining-table/table.rb",
|
41
|
+
"spec/collection.rb",
|
42
|
+
"spec/csv_table_spec.rb",
|
43
|
+
"spec/date_column.rb",
|
44
|
+
"spec/html_table_spec.rb",
|
45
|
+
"spec/spec_helper.rb",
|
46
|
+
"spec/tables/car_table.rb",
|
47
|
+
"spec/tables/car_table_with_actions.rb",
|
48
|
+
"spec/tables/car_table_with_footer.rb",
|
49
|
+
"spec/tables/car_table_with_header.rb",
|
50
|
+
"spec/tables/car_table_with_options.rb"
|
39
51
|
]
|
40
52
|
s.homepage = "http://github.com/mvdamme/dining-table"
|
41
53
|
s.licenses = ["MIT"]
|
@@ -17,7 +17,7 @@ module DiningTable
|
|
17
17
|
|
18
18
|
def action(&block)
|
19
19
|
action_value = yield(@current_object)
|
20
|
-
@incremental_value += action_value.
|
20
|
+
@incremental_value += action_value.to_s if action_value && action_value.respond_to?(:to_s)
|
21
21
|
end
|
22
22
|
|
23
23
|
# offer methods normally available on Table that could be used by the action blocks
|
@@ -17,7 +17,7 @@ module DiningTable
|
|
17
17
|
if block
|
18
18
|
block.call(object, self)
|
19
19
|
else
|
20
|
-
object.send(name)
|
20
|
+
object.send(name) if object.respond_to?(name)
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
@@ -25,7 +25,7 @@ module DiningTable
|
|
25
25
|
@header ||= begin
|
26
26
|
label = determine_label(:header)
|
27
27
|
return label if label
|
28
|
-
object_class = table.collection.first.
|
28
|
+
object_class = table.collection.first.class if table.collection.first
|
29
29
|
object_class.human_attribute_name( name ) if object_class && object_class.respond_to?( :human_attribute_name )
|
30
30
|
end
|
31
31
|
end
|
@@ -44,7 +44,8 @@ module DiningTable
|
|
44
44
|
if options[ name ]
|
45
45
|
label_ = options[ name ]
|
46
46
|
label_ = label_.call if label_.respond_to?(:call)
|
47
|
-
|
47
|
+
label_ = label_.to_s if label_.respond_to?(:to_s)
|
48
|
+
return label_
|
48
49
|
end
|
49
50
|
end
|
50
51
|
|
data/lib/dining-table/config.rb
CHANGED
@@ -57,7 +57,7 @@ module DiningTable
|
|
57
57
|
|
58
58
|
def render_footer
|
59
59
|
footers = columns.each.map(&:footer)
|
60
|
-
if footers.map
|
60
|
+
if footers.map { |s| blank?(s) }.uniq != [ true ]
|
61
61
|
add_tag(:start, :tfoot)
|
62
62
|
add_tag(:start, :tr)
|
63
63
|
columns.each_with_index do |column, index|
|
@@ -70,7 +70,7 @@ module DiningTable
|
|
70
70
|
end
|
71
71
|
|
72
72
|
def output
|
73
|
-
@output.html_safe
|
73
|
+
@output.respond_to?(:html_safe) ? @output.html_safe : @output
|
74
74
|
end
|
75
75
|
|
76
76
|
private
|
@@ -87,7 +87,7 @@ module DiningTable
|
|
87
87
|
end
|
88
88
|
|
89
89
|
def start_tag(tag, options = {})
|
90
|
-
"<#{ tag.to_s }
|
90
|
+
"<#{ tag.to_s }#{ options_string(options) }>"
|
91
91
|
end
|
92
92
|
|
93
93
|
def end_tag(tag, options = {})
|
data/lib/dining-table/table.rb
CHANGED
@@ -21,10 +21,12 @@ module DiningTable
|
|
21
21
|
def render
|
22
22
|
presenter.start_table
|
23
23
|
presenter.render_header unless no_header
|
24
|
+
presenter.start_body
|
24
25
|
collection.each_with_index do |object, index_|
|
25
26
|
self.index = index_
|
26
27
|
presenter.render_row( object )
|
27
28
|
end
|
29
|
+
presenter.end_body
|
28
30
|
presenter.render_footer
|
29
31
|
presenter.end_table
|
30
32
|
presenter.output
|
data/spec/collection.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
class Car < Struct.new(:brand, :type, :number_of_doors, :stock, :launch_date)
|
2
|
+
|
3
|
+
COLLECTION = [
|
4
|
+
[ 'Audi', 'A4', 3, 100, Date.new(2016, 1, 1) ],
|
5
|
+
[ 'Citroën', 'C4 Picasso', 5, 150, Date.new(2016, 2, 1) ],
|
6
|
+
[ 'Ferrari', 'F12', 3, 2, Date.new(2016, 3, 1) ]
|
7
|
+
]
|
8
|
+
|
9
|
+
def self.collection
|
10
|
+
COLLECTION.map do |data|
|
11
|
+
self.new( *data )
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
class CarWithHumanAttributeName < Car
|
18
|
+
|
19
|
+
def self.human_attribute_name(attribute)
|
20
|
+
attribute.to_s.gsub('_', ' ').capitalize
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'collection'
|
3
|
+
require 'rexml/document'
|
4
|
+
Dir["#{File.dirname(__FILE__)}/tables/**/*.rb"].each {|file| require file }
|
5
|
+
|
6
|
+
|
7
|
+
describe 'CSVTableSpec' do
|
8
|
+
before do
|
9
|
+
@cars = CarWithHumanAttributeName.collection
|
10
|
+
@presenter = DiningTable::Presenters::CSVPresenter.new
|
11
|
+
end
|
12
|
+
|
13
|
+
it "correctly renders a basic table" do
|
14
|
+
csv = CarTable.new(@cars, nil, :presenter => @presenter).render
|
15
|
+
data = CSV.parse(csv)
|
16
|
+
check_data( data )
|
17
|
+
end
|
18
|
+
|
19
|
+
it "respects csv options" do
|
20
|
+
@presenter = DiningTable::Presenters::CSVPresenter.new( :csv => { :col_sep => ';' } )
|
21
|
+
csv = CarTable.new(@cars, nil, :presenter => @presenter).render
|
22
|
+
data = CSV.parse(csv)
|
23
|
+
data.first.length.must_equal 1 # wrongly parsed due to using comma as a separator
|
24
|
+
data = CSV.parse(csv, :col_sep => ';')
|
25
|
+
check_data( data )
|
26
|
+
end
|
27
|
+
|
28
|
+
it "respects global csv options" do
|
29
|
+
DiningTable.configure do |config|
|
30
|
+
config.csv_presenter.default_options = { :csv => { :col_sep => ';' } }
|
31
|
+
end
|
32
|
+
@presenter = DiningTable::Presenters::CSVPresenter.new
|
33
|
+
csv = CarTable.new(@cars, nil, :presenter => @presenter).render
|
34
|
+
data = CSV.parse(csv)
|
35
|
+
data.first.length.must_equal 1 # wrongly parsed due to using comma as a separator
|
36
|
+
data = CSV.parse(csv, :col_sep => ';')
|
37
|
+
check_data( data )
|
38
|
+
# reset configuration for other specs
|
39
|
+
DiningTable.configure do |config|
|
40
|
+
config.csv_presenter.default_options = { }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def check_data( data )
|
45
|
+
data.first.must_equal ["Brand", "Type", "Number of doors", "Stock"]
|
46
|
+
@cars.each_with_index do |car, index|
|
47
|
+
line = [ :brand, :type, :number_of_doors, :stock ].map do |item|
|
48
|
+
car.send(item).to_s
|
49
|
+
end
|
50
|
+
data[index + 1].must_equal line
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/spec/date_column.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# note that this custom class is specific for testing (value contains minitest assertions)
|
2
|
+
class DateColumn < DiningTable::Columns::Column
|
3
|
+
def value(object)
|
4
|
+
val = super
|
5
|
+
val.class.name.must_equal 'Date' # make sure an actual object is returned, not a string representation
|
6
|
+
self.class.localize( val ) if val
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.localize(date)
|
10
|
+
"#{ date.day }/#{ date.month }/#{ date.year }"
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,212 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'collection'
|
3
|
+
require 'date_column'
|
4
|
+
require 'rexml/document'
|
5
|
+
Dir["#{File.dirname(__FILE__)}/tables/**/*.rb"].each {|file| require file }
|
6
|
+
|
7
|
+
describe 'HTMLTableSpec' do
|
8
|
+
before do
|
9
|
+
@cars = Car.collection
|
10
|
+
@view_context = ViewContext.new
|
11
|
+
end
|
12
|
+
|
13
|
+
it "correctly renders a basic table's body" do
|
14
|
+
html = CarTable.new(@cars, nil).render
|
15
|
+
doc = document( html )
|
16
|
+
@cars.each_with_index do |car, index|
|
17
|
+
[ :brand, :number_of_doors, :stock ].each_with_index do |column, col_index|
|
18
|
+
xpath = "/table/tbody/tr[#{ index + 1 }]/td[#{ col_index + 1 }]"
|
19
|
+
check_not_empty(doc.elements, xpath)
|
20
|
+
doc.elements.each(xpath) do |element|
|
21
|
+
element.text.must_equal car.send( column ).to_s
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
it "correctly renders a table's header when no explicit headers are defined" do
|
28
|
+
@cars = CarWithHumanAttributeName.collection
|
29
|
+
html = CarTable.new(@cars, nil).render
|
30
|
+
doc = document( html )
|
31
|
+
[ 'Brand', 'Number of doors', 'Stock' ].each_with_index do |header, col_index|
|
32
|
+
xpath = "/table/thead/tr[1]/th[#{ col_index + 1 }]"
|
33
|
+
check_not_empty(doc.elements, xpath)
|
34
|
+
doc.elements.each(xpath) do |element|
|
35
|
+
element.text.must_equal header
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
it "correctly renders a table's header when explicit headers are defined" do
|
41
|
+
@cars = CarWithHumanAttributeName.collection
|
42
|
+
html = CarTableWithHeader.new(@cars, @view_context).render
|
43
|
+
doc = document( html )
|
44
|
+
[ 'The brand', 'The number of doors' ].each_with_index do |header, col_index|
|
45
|
+
xpath = "/table/thead/tr[1]/th[#{ col_index + 1 }]"
|
46
|
+
check_not_empty(doc.elements, xpath)
|
47
|
+
doc.elements.each(xpath) do |element|
|
48
|
+
element.text.must_equal header
|
49
|
+
end
|
50
|
+
end
|
51
|
+
# last header has link
|
52
|
+
doc.elements.each("/table/thead/tr[1]/th[3]/a") do |element|
|
53
|
+
element.text.must_equal 'Stock'
|
54
|
+
element.attributes.get_attribute('href').value.must_equal "http://www.google.com"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it "correctly renders a table's footer when footers are defined" do
|
59
|
+
@cars = CarWithHumanAttributeName.collection
|
60
|
+
html = CarTableWithFooter.new(@cars, @view_context).render
|
61
|
+
doc = document( html )
|
62
|
+
[ nil, 'Total' ].each_with_index do |footer, col_index|
|
63
|
+
xpath = "/table/tfoot/tr[1]/td[#{ col_index + 1 }]"
|
64
|
+
check_not_empty(doc.elements, xpath)
|
65
|
+
doc.elements.each(xpath) do |element|
|
66
|
+
element.text.must_equal footer
|
67
|
+
end
|
68
|
+
end
|
69
|
+
# last footer has link
|
70
|
+
doc.elements.each("/table/tfoot/tr[1]/th[3]/a") do |element|
|
71
|
+
element.text.must_equal "Total: #{ 150 }"
|
72
|
+
element.attributes.get_attribute('href').value.must_equal "#"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
it "correctly renders a table with column options and column blocks" do
|
77
|
+
html = CarTableWithOptions.new(@cars, nil).render
|
78
|
+
doc = document( html )
|
79
|
+
@cars.each_with_index do |car, index|
|
80
|
+
[ :brand, :stock ].each_with_index do |column, col_index|
|
81
|
+
xpath = "/table/tbody/tr[#{ index + 1 }]/td[#{ col_index + 1 }]"
|
82
|
+
check_not_empty(doc.elements, xpath)
|
83
|
+
doc.elements.each(xpath) do |element|
|
84
|
+
element.text.must_equal car.send( column ).to_s.upcase
|
85
|
+
class_ = col_index == 0 ? 'center' : 'left'
|
86
|
+
element.attributes.get_attribute('class').value.must_equal class_
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
# also check header
|
91
|
+
[ 1, 2 ].each do |index|
|
92
|
+
doc.elements.each("/table/thead/tr[1]/th[#{ index }]") do |element|
|
93
|
+
class_ = index == 1 ? 'center' : 'left'
|
94
|
+
element.attributes.get_attribute('class').value.must_equal class_
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
it "correctly renders a table with actions" do
|
100
|
+
html = CarTableWithActions.new(@cars, @view_context).render
|
101
|
+
doc = document( html )
|
102
|
+
@cars.each_with_index do |car, index|
|
103
|
+
xpath = "/table/tbody/tr[#{ index + 1 }]/td[3]/a"
|
104
|
+
check_not_empty(doc.elements, xpath)
|
105
|
+
doc.elements.each_with_index(xpath) do |element, idx|
|
106
|
+
text = (idx == 0 ? 'Show' : 'Edit')
|
107
|
+
link = (idx == 0 ? '#show' : '#edit')
|
108
|
+
element.text.must_equal text
|
109
|
+
element.attributes.get_attribute('href').value.must_equal link
|
110
|
+
end
|
111
|
+
# check options
|
112
|
+
xpath = "/table/tbody/tr[#{ index + 1 }]/td[3]"
|
113
|
+
doc.elements.each(xpath) do |element|
|
114
|
+
element.attributes.get_attribute('class').value.must_equal 'left'
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
it "respects custom columns when specified" do
|
120
|
+
# first without custom column
|
121
|
+
html = CarTableWithOptions.new(@cars, nil, :with_normal_launch_date => true).render
|
122
|
+
doc = document( html )
|
123
|
+
@cars.each_with_index do |car, index|
|
124
|
+
xpath = "/table/tbody/tr[#{ index + 1 }]/td[3]"
|
125
|
+
check_not_empty(doc.elements, xpath)
|
126
|
+
doc.elements.each(xpath) do |element|
|
127
|
+
element.text.must_equal car.launch_date.to_s
|
128
|
+
end
|
129
|
+
end
|
130
|
+
# now with custom date column
|
131
|
+
html = CarTableWithOptions.new(@cars, nil, :with_date_column_launch_date => true).render
|
132
|
+
doc = document( html )
|
133
|
+
@cars.each_with_index do |car, index|
|
134
|
+
xpath = "/table/tbody/tr[#{ index + 1 }]/td[3]"
|
135
|
+
check_not_empty(doc.elements, xpath)
|
136
|
+
doc.elements.each(xpath) do |element|
|
137
|
+
element.text.must_equal DateColumn.localize( car.launch_date )
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
it "respects presenter options" do
|
143
|
+
html = CarTable.new(@cars, nil,
|
144
|
+
:presenter => DiningTable::Presenters::HTMLPresenter.new( :class => 'table table-bordered' ) ).render
|
145
|
+
doc = document( html )
|
146
|
+
doc.elements.first.attributes.get_attribute('class').value.must_equal 'table table-bordered'
|
147
|
+
end
|
148
|
+
|
149
|
+
it "correctly wraps the table" do
|
150
|
+
html = CarTable.new(@cars, nil,
|
151
|
+
:presenter => DiningTable::Presenters::HTMLPresenter.new( :wrap => { :tag => :div, :class => 'table-responsive' } ) ).render
|
152
|
+
doc = REXML::Document.new( html )
|
153
|
+
doc.elements.first.name.must_equal 'div'
|
154
|
+
doc.elements.first.attributes.get_attribute('class').value.must_equal 'table-responsive'
|
155
|
+
end
|
156
|
+
|
157
|
+
it "respects global html options" do
|
158
|
+
DiningTable.configure do |config|
|
159
|
+
config.html_presenter.default_options = { :class => 'table-hover',
|
160
|
+
:wrap => { :tag => :div, :class => 'table-responsive' } }
|
161
|
+
end
|
162
|
+
html = CarTable.new(@cars, nil).render
|
163
|
+
doc = REXML::Document.new( html )
|
164
|
+
doc.elements.first.name.must_equal 'div'
|
165
|
+
doc.elements.first.attributes.get_attribute('class').value.must_equal 'table-responsive'
|
166
|
+
table = doc.elements.first.elements.first
|
167
|
+
table.attributes.get_attribute('class').value.must_equal 'table-hover'
|
168
|
+
# reset configuration for other specs
|
169
|
+
DiningTable.configure do |config|
|
170
|
+
config.html_presenter.default_options = { }
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
def document( html )
|
175
|
+
doc = REXML::Document.new( html )
|
176
|
+
check_table_structure( doc )
|
177
|
+
doc
|
178
|
+
end
|
179
|
+
|
180
|
+
def check_table_structure( document )
|
181
|
+
document.elements.size.must_equal 1
|
182
|
+
table = document.elements.first
|
183
|
+
table.name.must_equal 'table'
|
184
|
+
(table.elements.size >= 2).must_equal true
|
185
|
+
header = table.elements.first
|
186
|
+
header.name.must_equal 'thead'
|
187
|
+
body = table.elements[2] # 2 = second element (not third) in REXML
|
188
|
+
body.name.must_equal 'tbody'
|
189
|
+
if table.elements.size == 3
|
190
|
+
footer = table.elements[3]
|
191
|
+
footer.name.must_equal 'tfoot'
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
def not_empty?(node, xpath)
|
196
|
+
node.each(xpath) do
|
197
|
+
return true
|
198
|
+
end
|
199
|
+
false
|
200
|
+
end
|
201
|
+
|
202
|
+
def check_not_empty(node, xpath)
|
203
|
+
not_empty?(node, xpath).must_equal true
|
204
|
+
end
|
205
|
+
|
206
|
+
class ViewContext
|
207
|
+
def link_to(text, url)
|
208
|
+
"<a href=\"#{ url }\">#{ text }</a>"
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
class CarTableWithActions < DiningTable::Table
|
2
|
+
def define
|
3
|
+
column :brand
|
4
|
+
column :number_of_doors
|
5
|
+
actions :header => 'Action', :html => { :td_options => { class: 'left' }, :th_options => { class: :left } } do |object|
|
6
|
+
action { |object| h.link_to( 'Show', '#show' ) }
|
7
|
+
action { |object| h.link_to( 'Edit', '#edit' ) }
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class CarTableWithOptions < DiningTable::Table
|
2
|
+
def define
|
3
|
+
column :brand, :html => { :td_options => { class: 'center' }, :th_options => { class: :center } } do |object|
|
4
|
+
object.brand.upcase
|
5
|
+
end
|
6
|
+
column :stock, :html => { :td_options => { class: 'left' }, :th_options => { class: :left } }
|
7
|
+
column :launch_date if options[:with_normal_launch_date]
|
8
|
+
column :launch_date, :class => DateColumn if options[:with_date_column_launch_date]
|
9
|
+
end
|
10
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: dining-table
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Michaël Van Damme
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-09-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -47,6 +47,8 @@ extra_rdoc_files:
|
|
47
47
|
- LICENSE
|
48
48
|
- README.md
|
49
49
|
files:
|
50
|
+
- ".travis.yml"
|
51
|
+
- CHANGELOG.md
|
50
52
|
- Gemfile
|
51
53
|
- Gemfile.lock
|
52
54
|
- LICENSE
|
@@ -64,6 +66,16 @@ files:
|
|
64
66
|
- lib/dining-table/presenters/presenter.rb
|
65
67
|
- lib/dining-table/presenters/spreadsheet_presenter.rb
|
66
68
|
- lib/dining-table/table.rb
|
69
|
+
- spec/collection.rb
|
70
|
+
- spec/csv_table_spec.rb
|
71
|
+
- spec/date_column.rb
|
72
|
+
- spec/html_table_spec.rb
|
73
|
+
- spec/spec_helper.rb
|
74
|
+
- spec/tables/car_table.rb
|
75
|
+
- spec/tables/car_table_with_actions.rb
|
76
|
+
- spec/tables/car_table_with_footer.rb
|
77
|
+
- spec/tables/car_table_with_header.rb
|
78
|
+
- spec/tables/car_table_with_options.rb
|
67
79
|
homepage: http://github.com/mvdamme/dining-table
|
68
80
|
licenses:
|
69
81
|
- MIT
|