dining-table 0.2.1 → 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
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.