dining-table 0.2.1 → 1.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 67ee845d370a9b92ee759acdb3fff4fbfccdbd12
4
- data.tar.gz: 88e0481f7a7d2218cb709e36d94ccf22d22add8b
2
+ SHA256:
3
+ metadata.gz: e3d53dc971fea99ed47e6793a2cdf74aa9c1f02b96726e56947ce9b264e5b688
4
+ data.tar.gz: 60cb9fdb7f239528b13aea9eab65ec9185038162c8ee4b4506f54e3e607614ff
5
5
  SHA512:
6
- metadata.gz: adfc7a39866c15e9da27a68b262a24695a72d46951abe91658e2e71917541407cac95c9a7afdb0ede4e825dbaa4e9fee4c5f765a7bb8207d8efd1878a1bec440
7
- data.tar.gz: 382b77586358e8505d819ed50a8d999abc53f451b2b083a85ea3cc0af7c2709f92f6a8ae0f323d1100b5bf94b589411ef45cab9b35b811de5f35f620214fa927
6
+ metadata.gz: 33ba0f66b71201b5adde87aee1237e8616fe371a17fa4651a4b006ca8b6a1f3780c69e8e71cf4d34ec93371f9598d20938491f039a520548c37b45467ec45466
7
+ data.tar.gz: 7eb66dae080a8efd67c3b5705f697a3d217c49c109c7d7eff7983e367ebfbb49e72bc2630558e149506e2c9935a789e3ec9eed652bb80cbe23f6a29da8f25680
@@ -1,8 +1,9 @@
1
1
  language: ruby
2
2
  rvm:
3
- - 2.2.0
4
- - 2.3.0
5
- - jruby-9.1.5.0
3
+ - 2.3.8
4
+ - 2.6.3
5
+ - 2.7.1
6
+ - jruby-9.1.9.0
6
7
  script: bundle exec rake
7
8
  before_install:
8
9
  - gem install bundler
@@ -1,3 +1,25 @@
1
+ ## 1.1.3 (22/06/2020)
2
+
3
+ * Fix ruby 2.7 deprecation warning
4
+
5
+ ## 1.1.2 (28/11/2019)
6
+
7
+ * Bugfix - fix html escaping issue
8
+
9
+ ## 1.1.1 (12/10/2019)
10
+
11
+ * Bugfix - call per-cell configuration block with correct index for footer row
12
+
13
+ ## 1.1.0 (28/11/2018)
14
+
15
+ * Allow passing in class of object to avoid header edge case when the collection is empty
16
+
17
+ ## 1.0.0 (17/06/2018)
18
+
19
+ * New configuration mechanism for HTML presenter
20
+ * Described new configuration mechanism in readme
21
+ * Documented skip_header and added and documented skip_footer
22
+
1
23
  ## 0.2.1 (26/05/2018)
2
24
 
3
25
  * Removed Gemfile.lock file
data/Gemfile CHANGED
@@ -1,7 +1,7 @@
1
1
  source "http://rubygems.org"
2
2
 
3
3
  group :development do
4
- gem "bundler", "~> 1.0"
4
+ gem "bundler"
5
5
  gem "juwelier", "~> 2.1.0"
6
6
  end
7
7
 
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 [table-cloth](https://github.com/bobbytables/table_cloth), it aims to be less dependent on Rails
6
- (no Rails required to use `dining-table`) and more flexible.
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
@@ -82,7 +95,27 @@ class CarTable < DiningTable::Table
82
95
  end
83
96
  ```
84
97
 
85
- Please note how the collection passed in when creating the table obect (`@cars` in `CarTable.new(@cars, self)`) is available as `collection`.
98
+ Please note how the collection passed in when creating the table object (`@cars` in `CarTable.new(@cars, self)`) is available as `collection`.
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
+ #### Empty collection
111
+
112
+ Note that when the collection to be presented in the table is empty, `dining-table` can't determine table headers
113
+ that aren't explicitly specified as there are no objects to use with `human_attribute_name`. In order to avoid this
114
+ edge case, you can pass in the class of the objects normally present in the collection when creating the table:
115
+
116
+ ```ruby
117
+ <%= CarTable.new(@cars, self, class: Car).render %>
118
+ ```
86
119
 
87
120
  ### Links and view helpers
88
121
 
@@ -185,6 +218,8 @@ end
185
218
 
186
219
  ### HTML
187
220
 
221
+ #### Introduction
222
+
188
223
  The default presenter is HTML (i.e. `DiningTable::Presenters::HTMLPresenter`), so `CarTable.new(@cars, self).render` will generate a table in HTML.
189
224
  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
225
  elements for some columns in the html table:
@@ -193,29 +228,126 @@ elements for some columns in the html table:
193
228
  class CarTable < DiningTable::Table
194
229
  def define
195
230
  column :brand
196
- column :number_of_doors, html: { td_options: { class: 'center' }, th_options: { class: :center } }
197
- column :stock, html: { td_options: { class: 'center' }, th_options: { class: :center } }
231
+ column :number_of_doors, html: { td: { class: 'center' }, th: { class: 'center' } }
232
+ column :stock, html: { td: { class: 'center' }, th: { class: 'center' } }
198
233
  end
199
234
  end
200
235
  ```
201
236
 
202
237
  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
238
 
204
- By instantiating the presenter yourself it is possible to specify options. For example:
239
+ #### Presenter configuration
240
+
241
+ By instantiating the presenter yourself it is possible to specify options for a specific table. Using the `:tags` key you can specify
242
+ options for all HTML tags used in the table. Example:
205
243
 
206
244
  ```ruby
207
- <%= CarTable.new(@cars, self, presenter: DiningTable::Presenters::HTMLPresenter.new( class: 'table table-bordered' )).render %>
245
+ <%= CarTable.new(@cars, self,
246
+ presenter: DiningTable::Presenters::HTMLPresenter.new(
247
+ tags: { table: { class: 'table table-bordered', id: 'car_table' },
248
+ tr: { class: 'car_table_row' } } )).render %>
208
249
  ```
250
+ 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.
251
+ The supported HTML tags are: `table`, `thead`, `tbody`, `tfoot`, `tr`, `th`, `td`.
209
252
 
210
- It is also possible to wrap the table in another tag (a div for instance):
253
+ It is also possible to wrap the table in another tag (a div for instance), and specify options for this tag:
211
254
 
212
255
  ```ruby
213
256
  <%= CarTable.new(@cars, self,
214
- presenter: DiningTable::Presenters::HTMLPresenter.new( class: 'table table-bordered',
215
- wrap: { tag: :div, class: 'table-responsive' } )).render %>
257
+ presenter: DiningTable::Presenters::HTMLPresenter.new(
258
+ tags: { table: { class: 'table table-bordered', id: 'car_table' },
259
+ wrap: { tag: :div, class: 'table-responsive' } )).render %>
216
260
  ```
217
261
 
218
- Both of these html options are usually best set as defaults, see [Configuration](#configuration)
262
+ Most of the html options are usually best set as defaults, see [Configuration](#configuration).
263
+
264
+ Note that configuration information provided to the presenter constructor is added to the default configuration,
265
+ it doesn't replace it. This means you can have the default configuration define the CSS class for the
266
+ table tag, for instance, and add the html id attribute when initializing the presenter, or from inside the
267
+ table definition.
268
+
269
+ #### Configuration inside the table definition
270
+
271
+ It is possible to specify or modify the configuration from within the table definition. This allows you to use custom
272
+ CSS classes, ids, etc. per row or even per cell. Example:
273
+
274
+ ```ruby
275
+ class CarTableWithConfigBlocks < DiningTable::Table
276
+ def define
277
+ table_id = options[:table_id] # custom option, see 'Options' above
278
+
279
+ presenter.table_config do |config|
280
+ config.table.class = 'table-class'
281
+ config.table.id = table_id || 'table-id'
282
+ config.thead.class = 'thead-class'
283
+ end if presenter.type?(:html)
284
+
285
+ presenter.row_config do |config, index, object|
286
+ if index == :header
287
+ config.tr.class = 'header-tr'
288
+ config.th.class = 'header-th'
289
+ elsif index == :footer
290
+ config.tr.class = 'footer-tr'
291
+ else # normal row
292
+ config.tr.class = index.odd? ? 'odd' : 'even'
293
+ config.tr.class += ' lowstock' if object.stock < 10
294
+ end
295
+ end if presenter.type?(:html)
296
+
297
+ column :brand
298
+ column :stock, footer: 'Footer text'
299
+ end
300
+ end
301
+ ```
302
+ 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`
303
+ 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
304
+ seven supported tags.
305
+
306
+ Note that the configuration object already contains the pre-existing configuration information (coming
307
+ from either the presenter initialisation and/or from the global configuration), so you can refine the configuration in the block
308
+ instead of having to re-specify it in full. This means you can easily add CSS classes without knowledge of previously existing
309
+ configuration:
310
+ ```ruby
311
+ presenter.table_config do |config|
312
+ config.table.class += ' my-table-class'
313
+ end if presenter.type?(:html)
314
+ ```
315
+ Per row configuration can be specified with `presenter.row_config`. The block used with this method is called once for each row being
316
+ rendered, and receives three parameters: the configuration object (identical as with `table_config`), an index value, and the object
317
+ containing the data being rendered in this row.
318
+ 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
319
+ is equal to `:header` and `:footer`, respectively. `object` is the current object being rendered (`nil` for the header and footer rows).
320
+ As above, the passed in configuration object already contains the configuration which is in effect before calling the block.
321
+
322
+ #### Per cell configuration
323
+
324
+ As shown above, you can specify per column configuration using a hash:
325
+
326
+ ```ruby
327
+ class CarTable < DiningTable::Table
328
+ def define
329
+ column :number_of_doors, html: { td: { class: 'center' }, th: { class: 'center' } }
330
+ end
331
+ end
332
+ ```
333
+ For each column, the per column configuration is merged with the row configuration (see `presenter.row_config` above) before
334
+ cells from the column are rendered.
335
+
336
+ Sometimes, you might want to specify the configuration per cell, for instance to add a CSS class for cells with a certain content.
337
+ This is possible by supplying a lamba or proc instead of a hash:
338
+
339
+ ```ruby
340
+ class CarTable < DiningTable::Table
341
+ def define
342
+ number_of_doors_options = ->( config, index, object ) do
343
+ config.td.class = 'center'
344
+ config.td.class += ' five_doors' if object && object.number_of_doors == 5
345
+ end
346
+ column :number_of_doors, html: number_of_doors_options
347
+ end
348
+ end
349
+ ```
350
+ The arguments provided to the lambda or proc are the same as in the case of `presenter.row_config`.
219
351
 
220
352
  ### CSV
221
353
 
@@ -271,14 +403,14 @@ end
271
403
 
272
404
  ### Excel (xlsx)
273
405
 
274
- The Excel presenter depends on [axlsx](https://github.com/randym/axlsx). Note that `dining-table` doesn't require `axlsx`, you have to add it to
406
+ The Excel presenter depends on [xlsxtream](https://github.com/felixbuenemann/xlsxtream) or [axlsx](https://github.com/randym/axlsx). Note that `dining-table` doesn't require either `xlsxtream` or `axlsx`, you have to add one of them to
275
407
  your Gemfile yourself if you want to use the Excel presenter.
276
408
 
277
- In order to use the Excel presenter, pass it in as a presenter and provide an axlsx worksheet:
409
+ In order to use the Excel presenter, pass it in as a presenter and provide a xlsxtream or axlsx worksheet:
278
410
 
279
411
  ```ruby
280
412
  collection = Car.order(:brand)
281
- # sheet is the axlsx worksheet in which the table will be rendered
413
+ # sheet is the xlsxtream or axlsx worksheet in which the table will be rendered
282
414
  CarTable.new( collection, nil, presenter: DiningTable::Presenters::ExcelPresenter.new( sheet ) ).render
283
415
  ```
284
416
 
@@ -315,7 +447,8 @@ You can set default options for the different presenters in an initializer (e.g.
315
447
 
316
448
  ```ruby
317
449
  DiningTable.configure do |config|
318
- config.html_presenter.default_options = { class: 'table table-bordered table-hover',
450
+ config.html_presenter.default_options = { tags: { table: { class: 'table table-bordered' },
451
+ thead: { class: 'header' } },
319
452
  wrap: { tag: :div, class: 'table-responsive' } }
320
453
  config.csv_presenter.default_options = { csv: { col_sep: ';' } }
321
454
  end
@@ -323,4 +456,4 @@ end
323
456
 
324
457
  ## Copyright
325
458
 
326
- Copyright (c) 2016 Michaël Van Damme. See LICENSE.txt for further details.
459
+ Copyright (c) 2018 Michaël Van Damme. See LICENSE.txt for further details.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 1.1.3
@@ -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.2.1 ruby lib
5
+ # stub: dining-table 1.1.3 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "dining-table".freeze
9
- s.version = "0.2.1"
9
+ s.version = "1.1.3"
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
- s.authors = ["Micha\u{eb}l Van Damme".freeze]
14
- s.date = "2018-05-26"
13
+ s.authors = ["Micha\u00EBl Van Damme".freeze]
14
+ s.date = "2020-06-22"
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,27 +45,27 @@ 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]
53
- s.rubygems_version = "2.6.11".freeze
57
+ s.rubygems_version = "3.1.2".freeze
54
58
  s.summary = "Create tables easily. Supports html, csv and xlsx.".freeze
55
59
 
56
60
  if s.respond_to? :specification_version then
57
61
  s.specification_version = 4
62
+ end
58
63
 
59
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
60
- s.add_development_dependency(%q<bundler>.freeze, ["~> 1.0"])
61
- s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
62
- else
63
- s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
64
- s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
65
- end
64
+ if s.respond_to? :add_runtime_dependency then
65
+ s.add_development_dependency(%q<bundler>.freeze, [">= 0"])
66
+ s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
66
67
  else
67
- s.add_dependency(%q<bundler>.freeze, ["~> 1.0"])
68
+ s.add_dependency(%q<bundler>.freeze, [">= 0"])
68
69
  s.add_dependency(%q<juwelier>.freeze, ["~> 2.1.0"])
69
70
  end
70
71
  end
@@ -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'
@@ -6,7 +6,7 @@ module DiningTable
6
6
 
7
7
  def value(object)
8
8
  if block
9
- @incremental_value = ''
9
+ @incremental_value = ''.html_safe
10
10
  @current_object = object
11
11
  self.instance_eval(&block)
12
12
  @incremental_value
@@ -16,11 +16,11 @@ module DiningTable
16
16
  private
17
17
 
18
18
  def action(&block)
19
- action_value = yield(@current_object)
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 blocks
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
@@ -26,6 +26,7 @@ module DiningTable
26
26
  label = determine_label(:header)
27
27
  return label if label
28
28
  object_class = table.collection.first.class if table.collection.first
29
+ object_class ||= table.object_class if !object_class
29
30
  object_class.human_attribute_name( name ) if object_class && object_class.respond_to?( :human_attribute_name )
30
31
  end
31
32
  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,14 +25,11 @@ 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
30
31
  csv_options = options[:csv] || { }
31
- CSV.new(stringio, csv_options)
32
+ CSV.new(stringio, **csv_options)
32
33
  end
33
34
  end
34
35
 
@@ -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,10 +3,17 @@ module DiningTable
3
3
  module Presenters
4
4
 
5
5
  class HTMLPresenter < Presenter
6
-
7
- def initialize( *args )
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
9
- self.output = ''
14
+ self.base_tags_configuration = HTMLPresenterConfiguration::TagsConfiguration.from_hash( default_options )
15
+ base_tags_configuration.merge_hash( options )
16
+ self.output = ''.html_safe
10
17
  end
11
18
 
12
19
  def identifier
@@ -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, options )
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,39 @@ module DiningTable
36
44
  end
37
45
 
38
46
  def render_row( object )
39
- add_tag(:start, :tr)
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
- render_cell( value, column.options_for( identifier ) )
51
+ configuration = cell_configuration( tags_configuration, column, table.index, object )
52
+ render_cell( value, configuration )
43
53
  end
44
54
  add_tag(:end, :tr)
45
55
  end
46
56
 
47
57
  def render_header
48
- add_tag(:start, :thead)
49
- add_tag(:start, :tr)
58
+ set_up_row_configuration( :header, nil )
59
+ add_tag(:start, :thead, tag_options(:thead))
60
+ add_tag(:start, :tr, row_options)
50
61
  columns.each do |column|
51
62
  value = column.header
52
- render_header_cell( value, column.options_for( identifier ) )
63
+ configuration = cell_configuration( tags_configuration, column, :header, nil )
64
+ render_header_cell( value, configuration )
53
65
  end
54
66
  add_tag(:end, :tr)
55
67
  add_tag(:end, :thead)
56
68
  end
57
69
 
58
70
  def render_footer
71
+ set_up_row_configuration( :footer, nil )
59
72
  footers = columns.each.map(&:footer)
60
73
  if footers.map { |s| blank?(s) }.uniq != [ true ]
61
- add_tag(:start, :tfoot)
62
- add_tag(:start, :tr)
74
+ add_tag(:start, :tfoot, tag_options(:tfoot))
75
+ add_tag(:start, :tr, row_options)
63
76
  columns.each_with_index do |column, index|
64
77
  value = footers[index]
65
- render_footer_cell( value, column.options_for( identifier ) )
78
+ configuration = cell_configuration( tags_configuration, column, :footer, nil )
79
+ render_footer_cell( value, configuration )
66
80
  end
67
81
  add_tag(:end, :tr)
68
82
  add_tag(:end, :tfoot)
@@ -70,13 +84,19 @@ module DiningTable
70
84
  end
71
85
 
72
86
  def output
73
- @output.respond_to?(:html_safe) ? @output.html_safe : @output
87
+ @output
74
88
  end
75
-
89
+
90
+ def table_config(&block)
91
+ self.table_config_block = block
92
+ end
93
+
94
+ def row_config(&block)
95
+ self.row_config_block = block
96
+ end
97
+
76
98
  private
77
99
 
78
- attr_writer :output
79
-
80
100
  def output_
81
101
  @output
82
102
  end
@@ -87,27 +107,27 @@ module DiningTable
87
107
  end
88
108
 
89
109
  def start_tag(tag, options = {})
90
- "<#{ tag.to_s }#{ options_string(options) }>"
110
+ "<#{ tag.to_s }#{ options_string(options) }>".html_safe
91
111
  end
92
112
 
93
113
  def end_tag(tag, options = {})
94
- "</#{ tag.to_s }>"
114
+ "</#{ tag.to_s }>".html_safe
95
115
  end
96
-
97
- def render_cell( string, options )
98
- render_general_cell( string, options, :td, :td_options )
116
+
117
+ def render_cell( string, configuration )
118
+ render_general_cell( string, configuration, :td)
99
119
  end
100
120
 
101
- def render_header_cell( string, options )
102
- render_general_cell( string, options, :th, :th_options )
121
+ def render_header_cell( string, configuration )
122
+ render_general_cell( string, configuration, :th)
103
123
  end
104
-
105
- def render_footer_cell( string, options )
106
- render_general_cell( string, options, :td, :footer_options )
124
+
125
+ def render_footer_cell( string, configuration )
126
+ render_cell( string, configuration )
107
127
  end
108
-
109
- def render_general_cell( string, options, cell_tag, options_identifier )
110
- add_tag(:start, cell_tag, options[ options_identifier ] )
128
+
129
+ def render_general_cell( string, configuration, cell_tag )
130
+ add_tag(:start, cell_tag, tag_options(cell_tag, configuration) )
111
131
  output_ << string.to_s
112
132
  add_tag(:end, cell_tag)
113
133
  end
@@ -134,7 +154,67 @@ module DiningTable
134
154
  options_.delete(:tag)
135
155
  options_
136
156
  end
137
-
157
+
158
+ def table_options
159
+ options_ = tag_options(:table)
160
+ return options_ unless options_.empty?
161
+ if options[:class]
162
+ warn "[DEPRECATION] dining-table: option \"class\" is deprecated, please use \"tags: { table: { class: 'my_class' } }\" instead."
163
+ { :class => options[:class] }
164
+ else
165
+ { }
166
+ end
167
+ end
168
+
169
+ def row_options
170
+ tag_options(:tr)
171
+ end
172
+
173
+ def column_options_cache( column )
174
+ @column_options_cache ||= { }
175
+ @column_options_cache[ column ] ||= begin
176
+ column_options = column.options_for( identifier )
177
+ if column_options.is_a?(Hash)
178
+ if column_options[:th_options] || column_options[:td_options]
179
+ warn "[DEPRECATION] dining-table: options \"th_options\" and \"td_options\" are deprecated, please use \"th\" and \"td\" instead. Example: \"{ td: { class: 'my_class' } }\"."
180
+ column_options[:th] = column_options.delete(:th_options)
181
+ column_options[:td] = column_options.delete(:td_options)
182
+ end
183
+ column_options[:tags] ? column_options : { :tags => column_options }
184
+ elsif column_options.respond_to?(:call)
185
+ column_options
186
+ end
187
+ end
188
+ end
189
+
190
+ def cell_configuration( start_configuration, column, index, object )
191
+ column_options = column_options_cache( column )
192
+ return start_configuration if !column_options
193
+ new_configuration = start_configuration.dup
194
+ if column_options.is_a?(Hash)
195
+ new_configuration.merge_hash( column_options )
196
+ else # callable
197
+ column_options.call( new_configuration, index, object )
198
+ new_configuration
199
+ end
200
+ end
201
+
202
+ def tag_options( tag, configuration = nil )
203
+ configuration ||= tags_configuration
204
+ configuration.send( tag ).to_h
205
+ end
206
+
207
+ def set_up_configuration
208
+ self.table_tags_configuration = base_tags_configuration.dup
209
+ table_config_block.call( table_tags_configuration ) if table_config_block
210
+ self.tags_configuration = table_tags_configuration.dup
211
+ end
212
+
213
+ def set_up_row_configuration( index, object )
214
+ self.tags_configuration = table_tags_configuration.dup
215
+ row_config_block.call( tags_configuration, index, object ) if row_config_block
216
+ end
217
+
138
218
  end
139
219
 
140
220
  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, :row, :render_footer, :output ].each do |method|
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
@@ -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,21 @@ 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
+
51
+ def object_class
52
+ options[:class]
53
+ end
54
+
40
55
  private
41
56
 
42
- attr_accessor :no_header
43
-
44
57
  # auxiliary function
45
58
  def column(name, options = {}, &block)
46
59
  klass = options[:class]
@@ -62,10 +75,6 @@ module DiningTable
62
75
  Presenters::HTMLPresenter
63
76
  end
64
77
 
65
- def skip_header
66
- self.no_header = true
67
- end
68
-
69
78
  end
70
79
 
71
80
  end
@@ -36,7 +36,19 @@ describe 'HTMLTableSpec' do
36
36
  end
37
37
  end
38
38
  end
39
-
39
+
40
+ it "correctly renders a table's header when the table body is empty and a class is provided" do
41
+ html = CarTable.new([], nil, :class => CarWithHumanAttributeName).render
42
+ doc = document( html )
43
+ [ 'Brand', 'Number of doors', 'Stock' ].each_with_index do |header, col_index|
44
+ xpath = "/table/thead/tr[1]/th[#{ col_index + 1 }]"
45
+ check_not_empty(doc.elements, xpath)
46
+ doc.elements.each(xpath) do |element|
47
+ element.text.must_equal header
48
+ end
49
+ end
50
+ end
51
+
40
52
  it "correctly renders a table's header when explicit headers are defined" do
41
53
  @cars = CarWithHumanAttributeName.collection
42
54
  html = CarTableWithHeader.new(@cars, @view_context).render
@@ -55,6 +67,23 @@ describe 'HTMLTableSpec' do
55
67
  end
56
68
  end
57
69
 
70
+ it "correctly renders a table's header when explicit headers are defined and the table body is empty" do
71
+ html = CarTableWithHeader.new([], @view_context).render
72
+ doc = document( html )
73
+ [ 'The brand', 'The number of doors' ].each_with_index do |header, col_index|
74
+ xpath = "/table/thead/tr[1]/th[#{ col_index + 1 }]"
75
+ check_not_empty(doc.elements, xpath)
76
+ doc.elements.each(xpath) do |element|
77
+ element.text.must_equal header
78
+ end
79
+ end
80
+ # last header has link
81
+ doc.elements.each("/table/thead/tr[1]/th[3]/a") do |element|
82
+ element.text.must_equal 'Stock'
83
+ element.attributes.get_attribute('href').value.must_equal "http://www.google.com"
84
+ end
85
+ end
86
+
58
87
  it "correctly renders a table's footer when footers are defined" do
59
88
  @cars = CarWithHumanAttributeName.collection
60
89
  html = CarTableWithFooter.new(@cars, @view_context).render
@@ -63,7 +92,8 @@ describe 'HTMLTableSpec' do
63
92
  xpath = "/table/tfoot/tr[1]/td[#{ col_index + 1 }]"
64
93
  check_not_empty(doc.elements, xpath)
65
94
  doc.elements.each(xpath) do |element|
66
- element.text.must_equal footer
95
+ element.text.must_equal footer if footer
96
+ element.text.must_be_nil if !footer # avoid minitest deprecation warning
67
97
  end
68
98
  end
69
99
  # last footer has link
@@ -73,6 +103,15 @@ describe 'HTMLTableSpec' do
73
103
  end
74
104
  end
75
105
 
106
+ it "allows skipping header and footer" do
107
+ @cars = CarWithHumanAttributeName.collection
108
+ html = CarTableWithoutHeader.new(@cars, @view_context).render
109
+ doc = REXML::Document.new( html )
110
+ table = doc.elements.first
111
+ table.elements.size.must_equal 1 # only body
112
+ table.elements.first.name.must_equal 'tbody'
113
+ end
114
+
76
115
  it "correctly renders a table with column options and column blocks" do
77
116
  html = CarTableWithOptions.new(@cars, nil).render
78
117
  doc = document( html )
@@ -96,6 +135,28 @@ describe 'HTMLTableSpec' do
96
135
  end
97
136
  end
98
137
 
138
+ it "still supports deprecated syntax for html column options" do
139
+ html = CarTableWithOptionsOldSyntax.new(@cars, nil).render
140
+ doc = document( html )
141
+ @cars.each_with_index do |car, index|
142
+ [ :brand, :stock ].each_with_index do |column, col_index|
143
+ xpath = "/table/tbody/tr[#{ index + 1 }]/td[#{ col_index + 1 }]"
144
+ check_not_empty(doc.elements, xpath)
145
+ doc.elements.each(xpath) do |element|
146
+ class_ = col_index == 0 ? 'center' : 'left'
147
+ element.attributes.get_attribute('class').value.must_equal class_
148
+ end
149
+ end
150
+ end
151
+ # also check header
152
+ [ 1, 2 ].each do |index|
153
+ doc.elements.each("/table/thead/tr[1]/th[#{ index }]") do |element|
154
+ class_ = index == 1 ? 'center' : 'left'
155
+ element.attributes.get_attribute('class').value.must_equal class_
156
+ end
157
+ end
158
+ end
159
+
99
160
  it "correctly renders a table with actions" do
100
161
  html = CarTableWithActions.new(@cars, @view_context).render
101
162
  doc = document( html )
@@ -140,6 +201,38 @@ describe 'HTMLTableSpec' do
140
201
  end
141
202
 
142
203
  it "respects presenter options" do
204
+ html = CarTableWithFooter.new(@cars, @view_context,
205
+ :presenter => DiningTable::Presenters::HTMLPresenter.new(
206
+ :tags => { :table => { :class => 'table table-bordered', :id => 'my_table_id', :'data-custom' => 'custom1!' },
207
+ :thead => { :class => 'mythead', :id => 'my_thead_id', :'data-custom' => 'custom2!' },
208
+ :tbody => { :class => 'mytbody', :id => 'my_tbody_id', :'data-custom' => 'custom3!' },
209
+ :tfoot => { :class => 'mytfoot', :id => 'my_tfoot_id', :'data-custom' => 'custom4!' },
210
+ :tr => { :class => 'mytr', :'data-custom' => 'custom5!' },
211
+ :th => { :class => 'myth', :'data-custom' => 'custom6!' },
212
+ :td => { :class => 'mytd', :'data-custom' => 'custom7!' }
213
+ } ) ).render
214
+ doc = document( html )
215
+ table = doc.elements.first
216
+ check_attributes( table, ['class', 'id', 'data-custom'], ['table table-bordered', 'my_table_id', 'custom1!'])
217
+ header = table.elements[1] # 1 = first element (not second) in REXML
218
+ check_attributes( header, ['class', 'id', 'data-custom'], ['mythead', 'my_thead_id', 'custom2!'])
219
+ body = table.elements[2] # 2 = second element (not third) in REXML
220
+ check_attributes( body, ['class', 'id', 'data-custom'], ['mytbody', 'my_tbody_id', 'custom3!'])
221
+ footer = table.elements[3] # 3 = third element (not fourth) in REXML
222
+ check_attributes( footer, ['class', 'id', 'data-custom'], ['mytfoot', 'my_tfoot_id', 'custom4!'])
223
+ row = header.elements.first
224
+ check_attributes( row, ['class', 'data-custom'], ['mytr', 'custom5!'])
225
+ row.elements.each do |header_cell|
226
+ check_attributes( header_cell, ['class', 'data-custom'], ['myth', 'custom6!'])
227
+ end
228
+ body.elements.each do |row_|
229
+ check_attributes( row_, ['class', 'data-custom'], ['mytr', 'custom5!'])
230
+ end
231
+ row = footer.elements.first
232
+ check_attributes( row, ['class', 'data-custom'], ['mytr', 'custom5!'])
233
+ end
234
+
235
+ it "still supports old (deprecated) way of specifying the table class" do
143
236
  html = CarTable.new(@cars, nil,
144
237
  :presenter => DiningTable::Presenters::HTMLPresenter.new( :class => 'table table-bordered' ) ).render
145
238
  doc = document( html )
@@ -156,7 +249,7 @@ describe 'HTMLTableSpec' do
156
249
 
157
250
  it "respects global html options" do
158
251
  DiningTable.configure do |config|
159
- config.html_presenter.default_options = { :class => 'table-hover',
252
+ config.html_presenter.default_options = { :tags => { :table => { :class => 'table-hover' }, :tr => { :class => 'rowrow' } },
160
253
  :wrap => { :tag => :div, :class => 'table-responsive' } }
161
254
  end
162
255
  html = CarTable.new(@cars, nil).render
@@ -165,12 +258,56 @@ describe 'HTMLTableSpec' do
165
258
  doc.elements.first.attributes.get_attribute('class').value.must_equal 'table-responsive'
166
259
  table = doc.elements.first.elements.first
167
260
  table.attributes.get_attribute('class').value.must_equal 'table-hover'
261
+ body = table.elements[2]
262
+ body.elements.each do |row|
263
+ row.attributes.get_attribute('class').value.must_equal 'rowrow'
264
+ end
168
265
  # reset configuration for other specs
169
266
  DiningTable.configure do |config|
170
267
  config.html_presenter.default_options = { }
171
268
  end
172
269
  end
173
270
 
271
+ it "respects in-table presenter config blocks" do
272
+ html = CarTableWithConfigBlocks.new(@cars, @view_context).render
273
+ doc = REXML::Document.new( html )
274
+ table = doc.elements.first
275
+ table.attributes.get_attribute('class').value.must_equal 'my-table-class'
276
+ header = table.elements.first
277
+ header.attributes.get_attribute('class').value.must_equal 'my-thead-class'
278
+ row = header.elements.first
279
+ row.attributes.get_attribute('class').value.must_equal 'header-tr'
280
+ row.elements.each do |cell|
281
+ cell.attributes.get_attribute('class').value.must_equal 'header-th'
282
+ end
283
+ body = table.elements[2]
284
+ body.elements.each_with_index do |row_, index|
285
+ row_.attributes.get_attribute('class').value.must_match( index.odd? ? /odd/ : /even/ )
286
+ row_.attributes.get_attribute('class').value.must_match( /lowstock/ ) if @cars[index].stock < 10
287
+ row_.elements.each_with_index do |td, td_index|
288
+ if td_index == 0
289
+ td.attributes.get_attribute('class').value.must_equal 'left'
290
+ elsif td_index == 1
291
+ td.attributes.get_attribute('class').value.must_match( /center/ )
292
+ td.attributes.get_attribute('class').value.must_match( /five_doors/ ) if @cars[index].number_of_doors == 5
293
+ else
294
+ td.attributes.get_attribute('class').must_be_nil if td_index != 1
295
+ end
296
+ end
297
+ end
298
+ footer = table.elements[3]
299
+ footer.attributes.get_attribute('class').value.must_equal 'my-tfoot-class'
300
+ row = footer.elements.first
301
+ row.attributes.get_attribute('class').value.must_equal 'footer-tr'
302
+ row.elements.each_with_index do |cell, index|
303
+ if index == 0
304
+ cell.attributes.get_attribute('class').value.must_equal 'left' # brand column
305
+ else
306
+ cell.attributes.get_attribute('class').value.must_equal 'footer-td'
307
+ end
308
+ end
309
+ end
310
+
174
311
  def document( html )
175
312
  doc = REXML::Document.new( html )
176
313
  check_table_structure( doc )
@@ -203,6 +340,13 @@ describe 'HTMLTableSpec' do
203
340
  not_empty?(node, xpath).must_equal true
204
341
  end
205
342
 
343
+ def check_attributes( element, attributes, values )
344
+ attributes.each_with_index do |attribute, index|
345
+ value = values[ index ]
346
+ element.attributes.get_attribute( attribute ).value.must_equal value
347
+ end
348
+ end
349
+
206
350
  class ViewContext
207
351
  def link_to(text, url)
208
352
  "<a href=\"#{ url }\">#{ text }</a>"
@@ -1,2 +1,8 @@
1
1
  require 'dining-table'
2
- require 'minitest/autorun'
2
+ require 'minitest/autorun'
3
+
4
+ class String
5
+ def html_safe
6
+ self
7
+ end
8
+ end
@@ -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 => { :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' ) }
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,33 @@
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
+ config.tfoot.class = 'my-tfoot-class'
8
+ end if presenter.type?(:html)
9
+
10
+ presenter.row_config do |config, index, object|
11
+ if index == :header
12
+ config.tr.class = 'header-tr'
13
+ config.th.class = 'header-th'
14
+ elsif index == :footer
15
+ config.tr.class = 'footer-tr'
16
+ config.td.class = 'footer-td'
17
+ else
18
+ config.tr.class = index.odd? ? 'odd' : 'even'
19
+ config.tr.class += ' lowstock' if object.stock < 10
20
+ end
21
+ end if presenter.type?(:html)
22
+
23
+ column :brand, :html => { :td => { :class => 'left' } }
24
+
25
+ number_of_doors_options = ->( config, index, object ) do
26
+ config.td.class = 'center' unless index == :footer
27
+ config.td.class += ' five_doors' if object && object.number_of_doors == 5
28
+ end
29
+ column :number_of_doors, :footer => 'Total', :html => number_of_doors_options
30
+
31
+ column :stock, :footer => lambda { h.link_to("Total: #{ collection.map(&:stock).inject(&:+) }", '#') }
32
+ end
33
+ end
@@ -1,9 +1,9 @@
1
1
  class CarTableWithOptions < DiningTable::Table
2
2
  def define
3
- column :brand, :html => { :td_options => { class: 'center' }, :th_options => { class: :center } } do |object|
3
+ column :brand, :html => { :td => { class: 'center' }, :th => { class: :center } } do |object|
4
4
  object.brand.upcase
5
5
  end
6
- column :stock, :html => { :td_options => { class: 'left' }, :th_options => { class: :left } }
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
@@ -0,0 +1,9 @@
1
+ class CarTableWithoutHeader < DiningTable::Table
2
+ def define
3
+ skip_header
4
+ skip_footer
5
+
6
+ column :brand
7
+ column :number_of_doors, :footer => 'Total'
8
+ end
9
+ end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dining-table
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 1.1.3
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-05-26 00:00:00.000000000 Z
11
+ date: 2020-06-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
- version: '1.0'
19
+ version: '0'
20
20
  type: :development
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - "~>"
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
- version: '1.0'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: juwelier
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -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
@@ -94,8 +98,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
94
98
  - !ruby/object:Gem::Version
95
99
  version: '0'
96
100
  requirements: []
97
- rubyforge_project:
98
- rubygems_version: 2.6.11
101
+ rubygems_version: 3.1.2
99
102
  signing_key:
100
103
  specification_version: 4
101
104
  summary: Create tables easily. Supports html, csv and xlsx.