dining-table 0.2.1 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +137 -13
- data/VERSION +1 -1
- data/dining-table.gemspec +8 -4
- data/lib/dining-table.rb +1 -0
- data/lib/dining-table/columns/actions_column.rb +3 -3
- data/lib/dining-table/presenters/csv_presenter.rb +5 -4
- data/lib/dining-table/presenters/excel_presenter.rb +4 -3
- data/lib/dining-table/presenters/html_presenter.rb +110 -27
- data/lib/dining-table/presenters/html_presenter_configuration.rb +103 -0
- data/lib/dining-table/presenters/presenter.rb +1 -1
- data/lib/dining-table/table.rb +14 -9
- data/spec/html_table_spec.rb +106 -2
- data/spec/tables/car_table_with_actions.rb +4 -3
- data/spec/tables/car_table_with_config_blocks.rb +31 -0
- data/spec/tables/car_table_with_options.rb +2 -2
- data/spec/tables/car_table_with_options_old_syntax.rb +10 -0
- data/spec/tables/car_table_without_header.rb +9 -0
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e2bb002b4a0ab18643810cb14fb3790d285b384
|
4
|
+
data.tar.gz: 0578437ede4fe83f41814a2c4d99ee898b52a953
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 882749ff1912bd3c9467c334f9a5abfa4eaf48e0b3a43fccb2595a74c1a50e77318720952e2a1ded192e4b75e63ccf51a7c6a307c78084bfb7cd6c791a973a6e
|
7
|
+
data.tar.gz: abffd0f5edd5d89312482265a01b3fd2cafe6a73c20bb9f405a5bf37e35a2b6b1960c087f7fee9676f643c20bcea2ea09a0de7a7f0ef38b299b4a281ef88db5a
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,10 +1,12 @@
|
|
1
1
|
# dining-table
|
2
2
|
[![Build Status](https://travis-ci.org/mvdamme/dining-table.png)](https://travis-ci.org/mvdamme/dining-table)
|
3
3
|
|
4
|
+
dining-table allows you to write clean Ruby classes instead of messy view code to generate HTML tables. You can re-use the same classes to
|
5
|
+
generate csv or xlsx output as well.
|
6
|
+
|
4
7
|
dining-table was inspired by the (now unfortunately unmaintained) [table_cloth](https://github.com/bobbytables/table_cloth) gem.
|
5
|
-
This gem is definitely not a drop-in replacement for [
|
6
|
-
(no Rails required to use `dining-table
|
7
|
-
In addition, it not only supports HTML output but you can output tabular data in csv or xlsx formats as well.
|
8
|
+
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
|
9
|
+
(no Rails required to use `dining-table`, in fact it has no dependencies (except if you chose to generate xlsx output)) and more flexible.
|
8
10
|
|
9
11
|
## Installation
|
10
12
|
|
@@ -71,6 +73,17 @@ end
|
|
71
73
|
|
72
74
|
The custom header can be a string, but also a lambda or a proc.
|
73
75
|
|
76
|
+
If for some reason you don't want a header, call `skip_header`:
|
77
|
+
|
78
|
+
```ruby
|
79
|
+
class CarTable < DiningTable::Table
|
80
|
+
def define
|
81
|
+
skip_header
|
82
|
+
column :brand
|
83
|
+
end
|
84
|
+
end
|
85
|
+
```
|
86
|
+
|
74
87
|
By default, `dining-table` doesn't add a footer to the table, except when at least one column explicitly specifies a footer:
|
75
88
|
|
76
89
|
```ruby
|
@@ -84,6 +97,17 @@ end
|
|
84
97
|
|
85
98
|
Please note how the collection passed in when creating the table obect (`@cars` in `CarTable.new(@cars, self)`) is available as `collection`.
|
86
99
|
|
100
|
+
Similarly to `skip_header`, if for some reason you don't want a footer (even though at least one column defines one), call `skip_footer`:
|
101
|
+
|
102
|
+
```ruby
|
103
|
+
class CarTable < DiningTable::Table
|
104
|
+
def define
|
105
|
+
skip_footer
|
106
|
+
column :brand, footer: 'Footer'
|
107
|
+
end
|
108
|
+
end
|
109
|
+
```
|
110
|
+
|
87
111
|
### Links and view helpers
|
88
112
|
|
89
113
|
When rendering the table in a view using `<%= CarTable.new(@cars, self).render %>`, the `self` parameter is the view context. It is made available through the `h`
|
@@ -185,6 +209,8 @@ end
|
|
185
209
|
|
186
210
|
### HTML
|
187
211
|
|
212
|
+
#### Introduction
|
213
|
+
|
188
214
|
The default presenter is HTML (i.e. `DiningTable::Presenters::HTMLPresenter`), so `CarTable.new(@cars, self).render` will generate a table in HTML.
|
189
215
|
When defining columns, you can specify options that apply only when using a certain presenter. For example, here we provide css classes for `td` and `th`
|
190
216
|
elements for some columns in the html table:
|
@@ -193,29 +219,126 @@ elements for some columns in the html table:
|
|
193
219
|
class CarTable < DiningTable::Table
|
194
220
|
def define
|
195
221
|
column :brand
|
196
|
-
column :number_of_doors, html: {
|
197
|
-
column :stock, html: {
|
222
|
+
column :number_of_doors, html: { td: { class: 'center' }, th: { class: 'center' } }
|
223
|
+
column :stock, html: { td: { class: 'center' }, th: { class: 'center' } }
|
198
224
|
end
|
199
225
|
end
|
200
226
|
```
|
201
227
|
|
202
228
|
The same table class can also be used with other presenters (csv, xlsx or a custom presenter), but the options will only be in effect when using the HTML presenter.
|
203
229
|
|
204
|
-
|
230
|
+
#### Presenter configuration
|
231
|
+
|
232
|
+
By instantiating the presenter yourself it is possible to specify options for a specific table. Using the `:tags` key you can specify
|
233
|
+
options for all HTML tags used in the table. Example:
|
205
234
|
|
206
235
|
```ruby
|
207
|
-
<%= CarTable.new(@cars, self,
|
236
|
+
<%= CarTable.new(@cars, self,
|
237
|
+
presenter: DiningTable::Presenters::HTMLPresenter.new(
|
238
|
+
tags: { table: { class: 'table table-bordered', id: 'car_table' },
|
239
|
+
tr: { class: 'car_table_row' } } )).render %>
|
208
240
|
```
|
241
|
+
In the above example, we specify the CSS class and HTML id for the table, and the CSS class to be used for all rows in the table.
|
242
|
+
The supported HTML tags are: `table`, `thead`, `tbody`, `tfoot`, `tr`, `th`, `td`.
|
209
243
|
|
210
|
-
It is also possible to wrap the table in another tag (a div for instance):
|
244
|
+
It is also possible to wrap the table in another tag (a div for instance), and specify options for this tag:
|
211
245
|
|
212
246
|
```ruby
|
213
247
|
<%= CarTable.new(@cars, self,
|
214
|
-
presenter: DiningTable::Presenters::HTMLPresenter.new(
|
215
|
-
|
248
|
+
presenter: DiningTable::Presenters::HTMLPresenter.new(
|
249
|
+
tags: { table: { class: 'table table-bordered', id: 'car_table' },
|
250
|
+
wrap: { tag: :div, class: 'table-responsive' } )).render %>
|
216
251
|
```
|
217
252
|
|
218
|
-
|
253
|
+
Most of the html options are usually best set as defaults, see [Configuration](#configuration).
|
254
|
+
|
255
|
+
Note that configuration information provided to the presenter constructor is added to the default configuration,
|
256
|
+
it doesn't replace it. This means you can have the default configuration define the CSS class for the
|
257
|
+
table tag, for instance, and add the html id attribute when initializing the presenter, or from inside the
|
258
|
+
table definition.
|
259
|
+
|
260
|
+
#### Configuration inside the table definition
|
261
|
+
|
262
|
+
It is possible to specify or modify the configuration from within the table definition. This allows you to use custom
|
263
|
+
CSS classes, ids, etc. per row or even per cell. Example:
|
264
|
+
|
265
|
+
```ruby
|
266
|
+
class CarTableWithConfigBlocks < DiningTable::Table
|
267
|
+
def define
|
268
|
+
table_id = options[:table_id] # custom option, see 'Options' above
|
269
|
+
|
270
|
+
presenter.table_config do |config|
|
271
|
+
config.table.class = 'table-class'
|
272
|
+
config.table.id = table_id || 'table-id'
|
273
|
+
config.thead.class = 'thead-class'
|
274
|
+
end if presenter.type?(:html)
|
275
|
+
|
276
|
+
presenter.row_config do |config, index, object|
|
277
|
+
if index == :header
|
278
|
+
config.tr.class = 'header-tr'
|
279
|
+
config.th.class = 'header-th'
|
280
|
+
elsif index == :footer
|
281
|
+
config.tr.class = 'footer-tr'
|
282
|
+
else # normal row
|
283
|
+
config.tr.class = index.odd? ? 'odd' : 'even'
|
284
|
+
config.tr.class += ' lowstock' if object.stock < 10
|
285
|
+
end
|
286
|
+
end if presenter.type?(:html)
|
287
|
+
|
288
|
+
column :brand
|
289
|
+
column :stock, footer: 'Footer text'
|
290
|
+
end
|
291
|
+
end
|
292
|
+
```
|
293
|
+
This example shows how to use `presenter.table_config` to set the configuration for (in this case) the `table` and `thead`tags. The block you use with `table_config`
|
294
|
+
is called once, when the table is being rendered. A configuration object is passed in that allows you to set any HTML attribute of the
|
295
|
+
seven supported tags.
|
296
|
+
|
297
|
+
Note that the configuration object already contains the pre-existing configuration information (coming
|
298
|
+
from either the presenter initialisation and/or from the global configuration), so you can refine the configuration in the block
|
299
|
+
instead of having to re-specify it in full. This means you can easily add CSS classes without knowledge of previously existing
|
300
|
+
configuration:
|
301
|
+
```ruby
|
302
|
+
presenter.table_config do |config|
|
303
|
+
config.table.class += ' my-table-class'
|
304
|
+
end if presenter.type?(:html)
|
305
|
+
```
|
306
|
+
Per row configuration can be specified with `presenter.row_config`. The block used with this method is called once for each row being
|
307
|
+
rendered, and receives three parameters: the configuration object (identical as with `table_config`), an index value, and the object
|
308
|
+
containing the data being rendered in this row.
|
309
|
+
The index value is equal to the row number of the row being rendered (starting at zero), except for the header and footer rows, in which case it
|
310
|
+
is equal to `:header` and `:footer`, respectively. `object` is the current object being rendered (`nil` for the header and footer rows).
|
311
|
+
As above, the passed in configuration object already contains the configuration which is in effect before calling the block.
|
312
|
+
|
313
|
+
#### Per cell configuration
|
314
|
+
|
315
|
+
As shown above, you can specify per column configuration using a hash:
|
316
|
+
|
317
|
+
```ruby
|
318
|
+
class CarTable < DiningTable::Table
|
319
|
+
def define
|
320
|
+
column :number_of_doors, html: { td: { class: 'center' }, th: { class: 'center' } }
|
321
|
+
end
|
322
|
+
end
|
323
|
+
```
|
324
|
+
For each column, the per column configuration is merged with the row configuration (see `presenter.row_config` above) before
|
325
|
+
cells from the column are rendered.
|
326
|
+
|
327
|
+
Sometimes, you might want to specify the configuration per cell, for instance to add a CSS class for cells with a certain content.
|
328
|
+
This is possible by supplying a lamba or proc instead of a hash:
|
329
|
+
|
330
|
+
```ruby
|
331
|
+
class CarTable < DiningTable::Table
|
332
|
+
def define
|
333
|
+
number_of_doors_options = ->( config, index, object ) do
|
334
|
+
config.td.class = 'center'
|
335
|
+
config.td.class += ' five_doors' if object && object.number_of_doors == 5
|
336
|
+
end
|
337
|
+
column :number_of_doors, html: number_of_doors_options
|
338
|
+
end
|
339
|
+
end
|
340
|
+
```
|
341
|
+
The arguments provided to the lambda or proc are the same as in the case of `presenter.row_config`.
|
219
342
|
|
220
343
|
### CSV
|
221
344
|
|
@@ -315,7 +438,8 @@ You can set default options for the different presenters in an initializer (e.g.
|
|
315
438
|
|
316
439
|
```ruby
|
317
440
|
DiningTable.configure do |config|
|
318
|
-
config.html_presenter.default_options = { class: 'table table-bordered
|
441
|
+
config.html_presenter.default_options = { tags: { table: { class: 'table table-bordered' },
|
442
|
+
thead: { class: 'header' } },
|
319
443
|
wrap: { tag: :div, class: 'table-responsive' } }
|
320
444
|
config.csv_presenter.default_options = { csv: { col_sep: ';' } }
|
321
445
|
end
|
@@ -323,4 +447,4 @@ end
|
|
323
447
|
|
324
448
|
## Copyright
|
325
449
|
|
326
|
-
Copyright (c)
|
450
|
+
Copyright (c) 2018 Michaël Van Damme. See LICENSE.txt for further details.
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
1.0.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 1.0.0 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "dining-table".freeze
|
9
|
-
s.version = "0.
|
9
|
+
s.version = "1.0.0"
|
10
10
|
|
11
11
|
s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
|
12
12
|
s.require_paths = ["lib".freeze]
|
13
13
|
s.authors = ["Micha\u{eb}l Van Damme".freeze]
|
14
|
-
s.date = "2018-
|
14
|
+
s.date = "2018-06-17"
|
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.".freeze
|
16
16
|
s.email = "michael.vandamme@vub.ac.be".freeze
|
17
17
|
s.extra_rdoc_files = [
|
@@ -34,6 +34,7 @@ Gem::Specification.new do |s|
|
|
34
34
|
"lib/dining-table/presenters/csv_presenter.rb",
|
35
35
|
"lib/dining-table/presenters/excel_presenter.rb",
|
36
36
|
"lib/dining-table/presenters/html_presenter.rb",
|
37
|
+
"lib/dining-table/presenters/html_presenter_configuration.rb",
|
37
38
|
"lib/dining-table/presenters/presenter.rb",
|
38
39
|
"lib/dining-table/presenters/spreadsheet_presenter.rb",
|
39
40
|
"lib/dining-table/table.rb",
|
@@ -44,9 +45,12 @@ Gem::Specification.new do |s|
|
|
44
45
|
"spec/spec_helper.rb",
|
45
46
|
"spec/tables/car_table.rb",
|
46
47
|
"spec/tables/car_table_with_actions.rb",
|
48
|
+
"spec/tables/car_table_with_config_blocks.rb",
|
47
49
|
"spec/tables/car_table_with_footer.rb",
|
48
50
|
"spec/tables/car_table_with_header.rb",
|
49
|
-
"spec/tables/car_table_with_options.rb"
|
51
|
+
"spec/tables/car_table_with_options.rb",
|
52
|
+
"spec/tables/car_table_with_options_old_syntax.rb",
|
53
|
+
"spec/tables/car_table_without_header.rb"
|
50
54
|
]
|
51
55
|
s.homepage = "http://github.com/mvdamme/dining-table".freeze
|
52
56
|
s.licenses = ["MIT".freeze]
|
data/lib/dining-table.rb
CHANGED
@@ -6,6 +6,7 @@ require 'dining-table/columns/column'
|
|
6
6
|
require 'dining-table/columns/actions_column'
|
7
7
|
|
8
8
|
require 'dining-table/presenters/presenter'
|
9
|
+
require 'dining-table/presenters/html_presenter_configuration'
|
9
10
|
require 'dining-table/presenters/html_presenter'
|
10
11
|
require 'dining-table/presenters/spreadsheet_presenter'
|
11
12
|
require 'dining-table/presenters/csv_presenter'
|
@@ -16,11 +16,11 @@ module DiningTable
|
|
16
16
|
private
|
17
17
|
|
18
18
|
def action(&block)
|
19
|
-
action_value =
|
19
|
+
action_value = table.instance_exec(@current_object, &block)
|
20
20
|
@incremental_value += action_value.to_s if action_value && action_value.respond_to?(:to_s)
|
21
21
|
end
|
22
22
|
|
23
|
-
# offer methods normally available on Table that could be used by the action
|
23
|
+
# offer methods normally available on Table that could be used by the action-column block
|
24
24
|
[ :h, :helpers, :collection, :index, :presenter ].each do |method|
|
25
25
|
self.class_eval <<-eos, __FILE__, __LINE__+1
|
26
26
|
def #{method}(*args)
|
@@ -28,7 +28,7 @@ module DiningTable
|
|
28
28
|
end
|
29
29
|
eos
|
30
30
|
end
|
31
|
-
|
31
|
+
|
32
32
|
end
|
33
33
|
|
34
34
|
end
|
@@ -5,7 +5,11 @@ module DiningTable
|
|
5
5
|
module Presenters
|
6
6
|
|
7
7
|
class CSVPresenter < SpreadsheetPresenter
|
8
|
-
|
8
|
+
|
9
|
+
attr_writer :output
|
10
|
+
attr_accessor :stringio
|
11
|
+
private :output, :stringio, :output=, :stringio=
|
12
|
+
|
9
13
|
def initialize( *args )
|
10
14
|
super
|
11
15
|
self.output = ''
|
@@ -21,9 +25,6 @@ module DiningTable
|
|
21
25
|
|
22
26
|
private
|
23
27
|
|
24
|
-
attr_writer :output
|
25
|
-
attr_accessor :stringio
|
26
|
-
|
27
28
|
def csv
|
28
29
|
@csv ||= begin
|
29
30
|
self.stringio = StringIO.new
|
@@ -3,7 +3,10 @@ module DiningTable
|
|
3
3
|
module Presenters
|
4
4
|
|
5
5
|
class ExcelPresenter < SpreadsheetPresenter
|
6
|
-
|
6
|
+
|
7
|
+
attr_accessor :worksheet
|
8
|
+
private :worksheet, :worksheet=
|
9
|
+
|
7
10
|
def initialize( worksheet, *args )
|
8
11
|
super( *args )
|
9
12
|
self.worksheet = worksheet
|
@@ -15,8 +18,6 @@ module DiningTable
|
|
15
18
|
|
16
19
|
private
|
17
20
|
|
18
|
-
attr_accessor :worksheet
|
19
|
-
|
20
21
|
def add_row(array)
|
21
22
|
worksheet.add_row( array )
|
22
23
|
end
|
@@ -3,9 +3,16 @@ module DiningTable
|
|
3
3
|
module Presenters
|
4
4
|
|
5
5
|
class HTMLPresenter < Presenter
|
6
|
-
|
7
|
-
|
6
|
+
|
7
|
+
attr_accessor :tags_configuration, :table_tags_configuration, :base_tags_configuration, :table_config_block, :row_config_block
|
8
|
+
|
9
|
+
attr_writer :output
|
10
|
+
private :output, :output=
|
11
|
+
|
12
|
+
def initialize( options = {} )
|
8
13
|
super
|
14
|
+
self.base_tags_configuration = HTMLPresenterConfiguration::TagsConfiguration.from_hash( default_options )
|
15
|
+
base_tags_configuration.merge_hash( options )
|
9
16
|
self.output = ''
|
10
17
|
end
|
11
18
|
|
@@ -14,10 +21,11 @@ module DiningTable
|
|
14
21
|
end
|
15
22
|
|
16
23
|
def start_table
|
24
|
+
set_up_configuration
|
17
25
|
if options[:wrap]
|
18
26
|
add_tag(:start, wrap_tag, wrap_options )
|
19
27
|
end
|
20
|
-
add_tag(:start, :table,
|
28
|
+
add_tag(:start, :table, table_options )
|
21
29
|
end
|
22
30
|
|
23
31
|
def end_table
|
@@ -28,7 +36,7 @@ module DiningTable
|
|
28
36
|
end
|
29
37
|
|
30
38
|
def start_body
|
31
|
-
add_tag(:start, :tbody)
|
39
|
+
add_tag(:start, :tbody, tag_options(:tbody))
|
32
40
|
end
|
33
41
|
|
34
42
|
def end_body
|
@@ -36,33 +44,42 @@ module DiningTable
|
|
36
44
|
end
|
37
45
|
|
38
46
|
def render_row( object )
|
39
|
-
|
47
|
+
set_up_row_configuration( table.index, object )
|
48
|
+
add_tag(:start, :tr, row_options)
|
40
49
|
columns.each do |column|
|
41
50
|
value = column.value( object )
|
42
|
-
|
51
|
+
configuration = cell_configuration( tags_configuration, column, table.index, object )
|
52
|
+
#render_cell( value, column.options_for( identifier ) )
|
53
|
+
render_cell( value, configuration )
|
43
54
|
end
|
44
55
|
add_tag(:end, :tr)
|
45
56
|
end
|
46
57
|
|
47
58
|
def render_header
|
48
|
-
|
49
|
-
add_tag(:start, :
|
59
|
+
set_up_row_configuration( :header, nil )
|
60
|
+
add_tag(:start, :thead, tag_options(:thead))
|
61
|
+
add_tag(:start, :tr, row_options)
|
50
62
|
columns.each do |column|
|
51
63
|
value = column.header
|
52
|
-
|
64
|
+
configuration = cell_configuration( tags_configuration, column, :header, nil )
|
65
|
+
#render_header_cell( value, column.options_for( identifier ) )
|
66
|
+
render_header_cell( value, configuration )
|
53
67
|
end
|
54
68
|
add_tag(:end, :tr)
|
55
69
|
add_tag(:end, :thead)
|
56
70
|
end
|
57
71
|
|
58
72
|
def render_footer
|
73
|
+
set_up_row_configuration( :footer, nil )
|
59
74
|
footers = columns.each.map(&:footer)
|
60
75
|
if footers.map { |s| blank?(s) }.uniq != [ true ]
|
61
|
-
add_tag(:start, :tfoot)
|
62
|
-
add_tag(:start, :tr)
|
76
|
+
add_tag(:start, :tfoot, tag_options(:tfoot))
|
77
|
+
add_tag(:start, :tr, row_options)
|
63
78
|
columns.each_with_index do |column, index|
|
64
79
|
value = footers[index]
|
65
|
-
|
80
|
+
configuration = cell_configuration( tags_configuration, column, :header, nil )
|
81
|
+
#render_footer_cell( value, column.options_for( identifier ) )
|
82
|
+
render_footer_cell( value, configuration )
|
66
83
|
end
|
67
84
|
add_tag(:end, :tr)
|
68
85
|
add_tag(:end, :tfoot)
|
@@ -72,11 +89,17 @@ module DiningTable
|
|
72
89
|
def output
|
73
90
|
@output.respond_to?(:html_safe) ? @output.html_safe : @output
|
74
91
|
end
|
75
|
-
|
92
|
+
|
93
|
+
def table_config(&block)
|
94
|
+
self.table_config_block = block
|
95
|
+
end
|
96
|
+
|
97
|
+
def row_config(&block)
|
98
|
+
self.row_config_block = block
|
99
|
+
end
|
100
|
+
|
76
101
|
private
|
77
102
|
|
78
|
-
attr_writer :output
|
79
|
-
|
80
103
|
def output_
|
81
104
|
@output
|
82
105
|
end
|
@@ -93,21 +116,21 @@ module DiningTable
|
|
93
116
|
def end_tag(tag, options = {})
|
94
117
|
"</#{ tag.to_s }>"
|
95
118
|
end
|
96
|
-
|
97
|
-
def render_cell( string,
|
98
|
-
render_general_cell( string,
|
119
|
+
|
120
|
+
def render_cell( string, configuration )
|
121
|
+
render_general_cell( string, configuration, :td)
|
99
122
|
end
|
100
123
|
|
101
|
-
def render_header_cell( string,
|
102
|
-
render_general_cell( string,
|
124
|
+
def render_header_cell( string, configuration )
|
125
|
+
render_general_cell( string, configuration, :th)
|
103
126
|
end
|
104
|
-
|
105
|
-
def render_footer_cell( string,
|
106
|
-
|
127
|
+
|
128
|
+
def render_footer_cell( string, configuration )
|
129
|
+
render_cell( string, configuration )
|
107
130
|
end
|
108
|
-
|
109
|
-
def render_general_cell( string,
|
110
|
-
add_tag(:start, cell_tag,
|
131
|
+
|
132
|
+
def render_general_cell( string, configuration, cell_tag )
|
133
|
+
add_tag(:start, cell_tag, tag_options(cell_tag, configuration) )
|
111
134
|
output_ << string.to_s
|
112
135
|
add_tag(:end, cell_tag)
|
113
136
|
end
|
@@ -134,7 +157,67 @@ module DiningTable
|
|
134
157
|
options_.delete(:tag)
|
135
158
|
options_
|
136
159
|
end
|
137
|
-
|
160
|
+
|
161
|
+
def table_options
|
162
|
+
options_ = tag_options(:table)
|
163
|
+
return options_ unless options_.empty?
|
164
|
+
if options[:class]
|
165
|
+
warn "[DEPRECATION] dining-table: option \"class\" is deprecated, please use \"tags: { table: { class: 'my_class' } }\" instead."
|
166
|
+
{ :class => options[:class] }
|
167
|
+
else
|
168
|
+
{ }
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def row_options
|
173
|
+
tag_options(:tr)
|
174
|
+
end
|
175
|
+
|
176
|
+
def column_options_cache( column )
|
177
|
+
@column_options_cache ||= { }
|
178
|
+
@column_options_cache[ column ] ||= begin
|
179
|
+
column_options = column.options_for( identifier )
|
180
|
+
if column_options.is_a?(Hash)
|
181
|
+
if column_options[:th_options] || column_options[:td_options]
|
182
|
+
warn "[DEPRECATION] dining-table: options \"th_options\" and \"td_options\" are deprecated, please use \"th\" and \"td\" instead. Example: \"{ td: { class: 'my_class' } }\"."
|
183
|
+
column_options[:th] = column_options.delete(:th_options)
|
184
|
+
column_options[:td] = column_options.delete(:td_options)
|
185
|
+
end
|
186
|
+
column_options[:tags] ? column_options : { :tags => column_options }
|
187
|
+
elsif column_options.respond_to?(:call)
|
188
|
+
column_options
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
def cell_configuration( start_configuration, column, index, object )
|
194
|
+
column_options = column_options_cache( column )
|
195
|
+
return start_configuration if !column_options
|
196
|
+
new_configuration = start_configuration.dup
|
197
|
+
if column_options.is_a?(Hash)
|
198
|
+
new_configuration.merge_hash( column_options )
|
199
|
+
else # callable
|
200
|
+
column_options.call( new_configuration, index, object )
|
201
|
+
new_configuration
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
def tag_options( tag, configuration = nil )
|
206
|
+
configuration ||= tags_configuration
|
207
|
+
configuration.send( tag ).to_h
|
208
|
+
end
|
209
|
+
|
210
|
+
def set_up_configuration
|
211
|
+
self.table_tags_configuration = base_tags_configuration.dup
|
212
|
+
table_config_block.call( table_tags_configuration ) if table_config_block
|
213
|
+
self.tags_configuration = table_tags_configuration.dup
|
214
|
+
end
|
215
|
+
|
216
|
+
def set_up_row_configuration( index, object )
|
217
|
+
self.tags_configuration = table_tags_configuration.dup
|
218
|
+
row_config_block.call( tags_configuration, index, object ) if row_config_block
|
219
|
+
end
|
220
|
+
|
138
221
|
end
|
139
222
|
|
140
223
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module DiningTable
|
2
|
+
|
3
|
+
module Presenters
|
4
|
+
|
5
|
+
module HTMLPresenterConfiguration
|
6
|
+
|
7
|
+
# configuration classes that allow us to avoid implementing a deep-merge for the config hash,
|
8
|
+
# and are more user friendly for use in config blocks in the table definition
|
9
|
+
class TagConfiguration
|
10
|
+
|
11
|
+
attr_accessor :__data_hash
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
self.__data_hash = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(name, *args, &block)
|
18
|
+
if name.to_s[-1] == '='
|
19
|
+
key = name.to_s[0..-2] # strip away '='
|
20
|
+
__data_hash[ key.to_sym ] = args.first
|
21
|
+
else
|
22
|
+
__data_hash.key?( name ) ? __data_hash[ name ] : super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def respond_to_missing?(method_name, *args)
|
27
|
+
return true if method_name.to_s[-1] == '='
|
28
|
+
__data_hash.key?( method_name ) ? true : super
|
29
|
+
end
|
30
|
+
|
31
|
+
# override class method (since it is a very common html attribute), and the method missing approach doesn't
|
32
|
+
# work here, as it returns the Ruby class by default.
|
33
|
+
def class
|
34
|
+
__data_hash.key?( :class ) ? __data_hash[ :class ] : super
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_h
|
38
|
+
__data_hash
|
39
|
+
end
|
40
|
+
|
41
|
+
def merge_hash( hash )
|
42
|
+
return self if !hash
|
43
|
+
hash.each do |key, value|
|
44
|
+
self.send("#{ key }=", value)
|
45
|
+
end
|
46
|
+
self
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.from_hash( hash )
|
50
|
+
new.merge_hash( hash )
|
51
|
+
end
|
52
|
+
|
53
|
+
# for deep dup
|
54
|
+
def initialize_copy( source )
|
55
|
+
self.__data_hash = source.__data_hash.dup
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
59
|
+
|
60
|
+
class TagsConfiguration
|
61
|
+
TAGS = [ :table, :thead, :tbody, :tfoot, :tr, :th, :td ]
|
62
|
+
attr_accessor(*TAGS)
|
63
|
+
|
64
|
+
def initialize
|
65
|
+
TAGS.each do |tag|
|
66
|
+
self.send("#{ tag }=", TagConfiguration.new)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def to_h
|
71
|
+
hashes = TAGS.map do |identifier|
|
72
|
+
self.send(identifier).to_h
|
73
|
+
end
|
74
|
+
{ :tags => Hash[ TAGS.zip( hashes ) ] }
|
75
|
+
end
|
76
|
+
|
77
|
+
def merge_hash( hash )
|
78
|
+
return self if !hash
|
79
|
+
tags = hash[ :tags ]
|
80
|
+
TAGS.each do |tag|
|
81
|
+
self.send("#{ tag }").merge_hash( tags[ tag ] )
|
82
|
+
end if tags
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.from_hash( hash )
|
87
|
+
new.merge_hash( hash )
|
88
|
+
end
|
89
|
+
|
90
|
+
# for deep dup
|
91
|
+
def initialize_copy( source )
|
92
|
+
TAGS.each do |tag|
|
93
|
+
self.send("#{ tag }=", source.send( tag ).dup)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
@@ -22,7 +22,7 @@ module DiningTable
|
|
22
22
|
identifier == identifier_
|
23
23
|
end
|
24
24
|
|
25
|
-
[ :start_table, :end_table, :render_header, :start_body, :end_body, :
|
25
|
+
[ :start_table, :end_table, :render_header, :start_body, :end_body, :render_row, :render_footer, :output ].each do |method|
|
26
26
|
self.class_eval <<-eos, __FILE__, __LINE__+1
|
27
27
|
def #{method}(*args)
|
28
28
|
end
|
data/lib/dining-table/table.rb
CHANGED
@@ -3,7 +3,10 @@ module DiningTable
|
|
3
3
|
class Table
|
4
4
|
|
5
5
|
attr_accessor :collection, :presenter, :options, :index, :columns, :action_columns, :view_context
|
6
|
-
|
6
|
+
|
7
|
+
attr_accessor :no_header, :no_footer
|
8
|
+
private :no_header, :no_footer, :no_header=, :no_footer=
|
9
|
+
|
7
10
|
def initialize( collection, view_context, options = {} )
|
8
11
|
self.collection = collection
|
9
12
|
self.view_context = view_context
|
@@ -27,7 +30,7 @@ module DiningTable
|
|
27
30
|
presenter.render_row( object )
|
28
31
|
end
|
29
32
|
presenter.end_body
|
30
|
-
presenter.render_footer
|
33
|
+
presenter.render_footer unless no_footer
|
31
34
|
presenter.end_table
|
32
35
|
presenter.output
|
33
36
|
end
|
@@ -36,11 +39,17 @@ module DiningTable
|
|
36
39
|
view_context
|
37
40
|
end
|
38
41
|
alias_method :h, :helpers
|
39
|
-
|
42
|
+
|
43
|
+
def skip_header
|
44
|
+
self.no_header = true
|
45
|
+
end
|
46
|
+
|
47
|
+
def skip_footer
|
48
|
+
self.no_footer = true
|
49
|
+
end
|
50
|
+
|
40
51
|
private
|
41
52
|
|
42
|
-
attr_accessor :no_header
|
43
|
-
|
44
53
|
# auxiliary function
|
45
54
|
def column(name, options = {}, &block)
|
46
55
|
klass = options[:class]
|
@@ -62,10 +71,6 @@ module DiningTable
|
|
62
71
|
Presenters::HTMLPresenter
|
63
72
|
end
|
64
73
|
|
65
|
-
def skip_header
|
66
|
-
self.no_header = true
|
67
|
-
end
|
68
|
-
|
69
74
|
end
|
70
75
|
|
71
76
|
end
|
data/spec/html_table_spec.rb
CHANGED
@@ -63,7 +63,8 @@ describe 'HTMLTableSpec' do
|
|
63
63
|
xpath = "/table/tfoot/tr[1]/td[#{ col_index + 1 }]"
|
64
64
|
check_not_empty(doc.elements, xpath)
|
65
65
|
doc.elements.each(xpath) do |element|
|
66
|
-
element.text.must_equal footer
|
66
|
+
element.text.must_equal footer if footer
|
67
|
+
element.text.must_be_nil if !footer # avoid minitest deprecation warning
|
67
68
|
end
|
68
69
|
end
|
69
70
|
# last footer has link
|
@@ -73,6 +74,15 @@ describe 'HTMLTableSpec' do
|
|
73
74
|
end
|
74
75
|
end
|
75
76
|
|
77
|
+
it "allows skipping header and footer" do
|
78
|
+
@cars = CarWithHumanAttributeName.collection
|
79
|
+
html = CarTableWithoutHeader.new(@cars, @view_context).render
|
80
|
+
doc = REXML::Document.new( html )
|
81
|
+
table = doc.elements.first
|
82
|
+
table.elements.size.must_equal 1 # only body
|
83
|
+
table.elements.first.name.must_equal 'tbody'
|
84
|
+
end
|
85
|
+
|
76
86
|
it "correctly renders a table with column options and column blocks" do
|
77
87
|
html = CarTableWithOptions.new(@cars, nil).render
|
78
88
|
doc = document( html )
|
@@ -96,6 +106,28 @@ describe 'HTMLTableSpec' do
|
|
96
106
|
end
|
97
107
|
end
|
98
108
|
|
109
|
+
it "still supports deprecated syntax for html column options" do
|
110
|
+
html = CarTableWithOptionsOldSyntax.new(@cars, nil).render
|
111
|
+
doc = document( html )
|
112
|
+
@cars.each_with_index do |car, index|
|
113
|
+
[ :brand, :stock ].each_with_index do |column, col_index|
|
114
|
+
xpath = "/table/tbody/tr[#{ index + 1 }]/td[#{ col_index + 1 }]"
|
115
|
+
check_not_empty(doc.elements, xpath)
|
116
|
+
doc.elements.each(xpath) do |element|
|
117
|
+
class_ = col_index == 0 ? 'center' : 'left'
|
118
|
+
element.attributes.get_attribute('class').value.must_equal class_
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
# also check header
|
123
|
+
[ 1, 2 ].each do |index|
|
124
|
+
doc.elements.each("/table/thead/tr[1]/th[#{ index }]") do |element|
|
125
|
+
class_ = index == 1 ? 'center' : 'left'
|
126
|
+
element.attributes.get_attribute('class').value.must_equal class_
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
99
131
|
it "correctly renders a table with actions" do
|
100
132
|
html = CarTableWithActions.new(@cars, @view_context).render
|
101
133
|
doc = document( html )
|
@@ -140,6 +172,38 @@ describe 'HTMLTableSpec' do
|
|
140
172
|
end
|
141
173
|
|
142
174
|
it "respects presenter options" do
|
175
|
+
html = CarTableWithFooter.new(@cars, @view_context,
|
176
|
+
:presenter => DiningTable::Presenters::HTMLPresenter.new(
|
177
|
+
:tags => { :table => { :class => 'table table-bordered', :id => 'my_table_id', :'data-custom' => 'custom1!' },
|
178
|
+
:thead => { :class => 'mythead', :id => 'my_thead_id', :'data-custom' => 'custom2!' },
|
179
|
+
:tbody => { :class => 'mytbody', :id => 'my_tbody_id', :'data-custom' => 'custom3!' },
|
180
|
+
:tfoot => { :class => 'mytfoot', :id => 'my_tfoot_id', :'data-custom' => 'custom4!' },
|
181
|
+
:tr => { :class => 'mytr', :'data-custom' => 'custom5!' },
|
182
|
+
:th => { :class => 'myth', :'data-custom' => 'custom6!' },
|
183
|
+
:td => { :class => 'mytd', :'data-custom' => 'custom7!' }
|
184
|
+
} ) ).render
|
185
|
+
doc = document( html )
|
186
|
+
table = doc.elements.first
|
187
|
+
check_attributes( table, ['class', 'id', 'data-custom'], ['table table-bordered', 'my_table_id', 'custom1!'])
|
188
|
+
header = table.elements[1] # 1 = first element (not second) in REXML
|
189
|
+
check_attributes( header, ['class', 'id', 'data-custom'], ['mythead', 'my_thead_id', 'custom2!'])
|
190
|
+
body = table.elements[2] # 2 = second element (not third) in REXML
|
191
|
+
check_attributes( body, ['class', 'id', 'data-custom'], ['mytbody', 'my_tbody_id', 'custom3!'])
|
192
|
+
footer = table.elements[3] # 3 = third element (not fourth) in REXML
|
193
|
+
check_attributes( footer, ['class', 'id', 'data-custom'], ['mytfoot', 'my_tfoot_id', 'custom4!'])
|
194
|
+
row = header.elements.first
|
195
|
+
check_attributes( row, ['class', 'data-custom'], ['mytr', 'custom5!'])
|
196
|
+
row.elements.each do |header_cell|
|
197
|
+
check_attributes( header_cell, ['class', 'data-custom'], ['myth', 'custom6!'])
|
198
|
+
end
|
199
|
+
body.elements.each do |row_|
|
200
|
+
check_attributes( row_, ['class', 'data-custom'], ['mytr', 'custom5!'])
|
201
|
+
end
|
202
|
+
row = footer.elements.first
|
203
|
+
check_attributes( row, ['class', 'data-custom'], ['mytr', 'custom5!'])
|
204
|
+
end
|
205
|
+
|
206
|
+
it "still supports old (deprecated) way of specifying the table class" do
|
143
207
|
html = CarTable.new(@cars, nil,
|
144
208
|
:presenter => DiningTable::Presenters::HTMLPresenter.new( :class => 'table table-bordered' ) ).render
|
145
209
|
doc = document( html )
|
@@ -156,7 +220,7 @@ describe 'HTMLTableSpec' do
|
|
156
220
|
|
157
221
|
it "respects global html options" do
|
158
222
|
DiningTable.configure do |config|
|
159
|
-
config.html_presenter.default_options = { :class => 'table-hover',
|
223
|
+
config.html_presenter.default_options = { :tags => { :table => { :class => 'table-hover' }, :tr => { :class => 'rowrow' } },
|
160
224
|
:wrap => { :tag => :div, :class => 'table-responsive' } }
|
161
225
|
end
|
162
226
|
html = CarTable.new(@cars, nil).render
|
@@ -165,12 +229,45 @@ describe 'HTMLTableSpec' do
|
|
165
229
|
doc.elements.first.attributes.get_attribute('class').value.must_equal 'table-responsive'
|
166
230
|
table = doc.elements.first.elements.first
|
167
231
|
table.attributes.get_attribute('class').value.must_equal 'table-hover'
|
232
|
+
body = table.elements[2]
|
233
|
+
body.elements.each do |row|
|
234
|
+
row.attributes.get_attribute('class').value.must_equal 'rowrow'
|
235
|
+
end
|
168
236
|
# reset configuration for other specs
|
169
237
|
DiningTable.configure do |config|
|
170
238
|
config.html_presenter.default_options = { }
|
171
239
|
end
|
172
240
|
end
|
173
241
|
|
242
|
+
it "respects in-table presenter config blocks" do
|
243
|
+
html = CarTableWithConfigBlocks.new(@cars, @view_context).render
|
244
|
+
doc = REXML::Document.new( html )
|
245
|
+
table = doc.elements.first
|
246
|
+
table.attributes.get_attribute('class').value.must_equal 'my-table-class'
|
247
|
+
header = table.elements.first
|
248
|
+
header.attributes.get_attribute('class').value.must_equal 'my-thead-class'
|
249
|
+
row = header.elements.first
|
250
|
+
row.attributes.get_attribute('class').value.must_equal 'header-tr'
|
251
|
+
row.elements.each do |cell|
|
252
|
+
cell.attributes.get_attribute('class').value.must_equal 'header-th'
|
253
|
+
end
|
254
|
+
body = table.elements[2]
|
255
|
+
body.elements.each_with_index do |row_, index|
|
256
|
+
row_.attributes.get_attribute('class').value.must_match( index.odd? ? /odd/ : /even/ )
|
257
|
+
row_.attributes.get_attribute('class').value.must_match( /lowstock/ ) if @cars[index].stock < 10
|
258
|
+
row_.elements.each_with_index do |td, td_index|
|
259
|
+
if td_index == 0
|
260
|
+
td.attributes.get_attribute('class').value.must_equal 'left'
|
261
|
+
elsif td_index == 1
|
262
|
+
td.attributes.get_attribute('class').value.must_match( /center/ )
|
263
|
+
td.attributes.get_attribute('class').value.must_match( /five_doors/ ) if @cars[index].number_of_doors == 5
|
264
|
+
else
|
265
|
+
td.attributes.get_attribute('class').must_be_nil if td_index != 1
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
end
|
270
|
+
|
174
271
|
def document( html )
|
175
272
|
doc = REXML::Document.new( html )
|
176
273
|
check_table_structure( doc )
|
@@ -203,6 +300,13 @@ describe 'HTMLTableSpec' do
|
|
203
300
|
not_empty?(node, xpath).must_equal true
|
204
301
|
end
|
205
302
|
|
303
|
+
def check_attributes( element, attributes, values )
|
304
|
+
attributes.each_with_index do |attribute, index|
|
305
|
+
value = values[ index ]
|
306
|
+
element.attributes.get_attribute( attribute ).value.must_equal value
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
206
310
|
class ViewContext
|
207
311
|
def link_to(text, url)
|
208
312
|
"<a href=\"#{ url }\">#{ text }</a>"
|
@@ -2,9 +2,10 @@ class CarTableWithActions < DiningTable::Table
|
|
2
2
|
def define
|
3
3
|
column :brand
|
4
4
|
column :number_of_doors
|
5
|
-
actions :header => 'Action', :html => { :
|
6
|
-
|
7
|
-
action { |
|
5
|
+
actions :header => 'Action', :html => { :td => { class: 'left' }, :th => { class: :left } } do |object|
|
6
|
+
h.link_to( 'Show', '#show' ) # doesn't do anything, simply verify that h helper is available
|
7
|
+
action { |object_| h.link_to( 'Show', '#show' ) }
|
8
|
+
action { |object_| h.link_to( 'Edit', '#edit' ) }
|
8
9
|
end
|
9
10
|
end
|
10
11
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class CarTableWithConfigBlocks < DiningTable::Table
|
2
|
+
def define
|
3
|
+
|
4
|
+
presenter.table_config do |config|
|
5
|
+
config.table.class = 'my-table-class'
|
6
|
+
config.thead.class = 'my-thead-class'
|
7
|
+
end if presenter.type?(:html)
|
8
|
+
|
9
|
+
presenter.row_config do |config, index, object|
|
10
|
+
if index == :header
|
11
|
+
config.tr.class = 'header-tr'
|
12
|
+
config.th.class = 'header-th'
|
13
|
+
elsif index == :footer
|
14
|
+
config.tr.class = 'header-tr'
|
15
|
+
else
|
16
|
+
config.tr.class = index.odd? ? 'odd' : 'even'
|
17
|
+
config.tr.class += ' lowstock' if object.stock < 10
|
18
|
+
end
|
19
|
+
end if presenter.type?(:html)
|
20
|
+
|
21
|
+
column :brand, :html => { :td => { :class => 'left' } }
|
22
|
+
|
23
|
+
number_of_doors_options = ->( config, index, object ) do
|
24
|
+
config.td.class = 'center'
|
25
|
+
config.td.class += ' five_doors' if object && object.number_of_doors == 5
|
26
|
+
end
|
27
|
+
column :number_of_doors, :footer => 'Total', :html => number_of_doors_options
|
28
|
+
|
29
|
+
column :stock, :footer => lambda { h.link_to("Total: #{ collection.map(&:stock).inject(&:+) }", '#') }
|
30
|
+
end
|
31
|
+
end
|
@@ -1,9 +1,9 @@
|
|
1
1
|
class CarTableWithOptions < DiningTable::Table
|
2
2
|
def define
|
3
|
-
column :brand, :html => { :
|
3
|
+
column :brand, :html => { :td => { class: 'center' }, :th => { class: :center } } do |object|
|
4
4
|
object.brand.upcase
|
5
5
|
end
|
6
|
-
column :stock, :html => { :
|
6
|
+
column :stock, :html => { :td => { class: 'left' }, :th => { class: :left } }
|
7
7
|
column :launch_date if options[:with_normal_launch_date]
|
8
8
|
column :launch_date, :class => DateColumn if options[:with_date_column_launch_date]
|
9
9
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class CarTableWithOptionsOldSyntax < DiningTable::Table
|
2
|
+
def define
|
3
|
+
column :brand, :html => { :td => { class: 'center' }, :th => { class: :center } } do |object|
|
4
|
+
object.brand.upcase
|
5
|
+
end
|
6
|
+
column :stock, :html => { :td_options => { class: 'left' }, :th_options => { class: :left } } # deprecated options syntax
|
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: 1.0.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: 2018-
|
11
|
+
date: 2018-06-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -62,6 +62,7 @@ files:
|
|
62
62
|
- lib/dining-table/presenters/csv_presenter.rb
|
63
63
|
- lib/dining-table/presenters/excel_presenter.rb
|
64
64
|
- lib/dining-table/presenters/html_presenter.rb
|
65
|
+
- lib/dining-table/presenters/html_presenter_configuration.rb
|
65
66
|
- lib/dining-table/presenters/presenter.rb
|
66
67
|
- lib/dining-table/presenters/spreadsheet_presenter.rb
|
67
68
|
- lib/dining-table/table.rb
|
@@ -72,9 +73,12 @@ files:
|
|
72
73
|
- spec/spec_helper.rb
|
73
74
|
- spec/tables/car_table.rb
|
74
75
|
- spec/tables/car_table_with_actions.rb
|
76
|
+
- spec/tables/car_table_with_config_blocks.rb
|
75
77
|
- spec/tables/car_table_with_footer.rb
|
76
78
|
- spec/tables/car_table_with_header.rb
|
77
79
|
- spec/tables/car_table_with_options.rb
|
80
|
+
- spec/tables/car_table_with_options_old_syntax.rb
|
81
|
+
- spec/tables/car_table_without_header.rb
|
78
82
|
homepage: http://github.com/mvdamme/dining-table
|
79
83
|
licenses:
|
80
84
|
- MIT
|