dining-table 0.2.1 → 1.0.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/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
|
[](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
|