dining-table 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d79df74fda15fcb68b6215a45dbdee28c6ddf5d3
4
+ data.tar.gz: bb73bf1a0edc0fe3c0aeda71acafbc13ae337e65
5
+ SHA512:
6
+ metadata.gz: 637d781f1ec3417a01f6d1f3004ccc60e8a95b64bef354c96703760259643aab537dfcec0bacef622587fb72a7d9bba8cfa5411cb5c19685c6145ff4ea403b99
7
+ data.tar.gz: 9ef097bc93ee0492e6bda519020089349da0fb552473f9c74d81da15a4bc0153308707a7a66cd472e48887b45e76ebd8d5d099aee667da1f77275655ded343a2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ group :development do
2
+ gem "bundler", "~> 1.0"
3
+ gem "juwelier", "~> 2.1.0"
4
+ end
@@ -0,0 +1,59 @@
1
+ GEM
2
+ remote: https://rubygems.org/
3
+ specs:
4
+ addressable (2.4.0)
5
+ builder (3.2.2)
6
+ descendants_tracker (0.0.4)
7
+ thread_safe (~> 0.3, >= 0.3.1)
8
+ faraday (0.9.2)
9
+ multipart-post (>= 1.2, < 3)
10
+ git (1.3.0)
11
+ github_api (0.13.1)
12
+ addressable (~> 2.4.0)
13
+ descendants_tracker (~> 0.0.4)
14
+ faraday (~> 0.8, < 0.10)
15
+ hashie (>= 3.4)
16
+ multi_json (>= 1.7.5, < 2.0)
17
+ oauth2
18
+ hashie (3.4.4)
19
+ highline (1.7.8)
20
+ json (1.8.3)
21
+ juwelier (2.1.1)
22
+ builder
23
+ bundler (>= 1.0)
24
+ git (>= 1.2.5)
25
+ github_api
26
+ highline (>= 1.6.15)
27
+ nokogiri (>= 1.5.10)
28
+ rake
29
+ rdoc
30
+ semver
31
+ jwt (1.5.1)
32
+ mini_portile2 (2.0.0)
33
+ multi_json (1.12.0)
34
+ multi_xml (0.5.5)
35
+ multipart-post (2.0.0)
36
+ nokogiri (1.6.7.2)
37
+ mini_portile2 (~> 2.0.0.rc2)
38
+ oauth2 (1.1.0)
39
+ faraday (>= 0.8, < 0.10)
40
+ jwt (~> 1.0, < 1.5.2)
41
+ multi_json (~> 1.3)
42
+ multi_xml (~> 0.5)
43
+ rack (>= 1.2, < 3)
44
+ rack (1.6.4)
45
+ rake (11.1.2)
46
+ rdoc (4.2.2)
47
+ json (~> 1.4)
48
+ semver (1.0.1)
49
+ thread_safe (0.3.5)
50
+
51
+ PLATFORMS
52
+ ruby
53
+
54
+ DEPENDENCIES
55
+ bundler (~> 1.0)
56
+ juwelier (~> 2.1.0)
57
+
58
+ BUNDLED WITH
59
+ 1.12.3
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Michaël Van Damme
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,298 @@
1
+ # dining-table
2
+
3
+ dining-table was inspired by the (now unfortunately unmaintained) [table_cloth](https://github.com/bobbytables/table_cloth) gem.
4
+ 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
5
+ (no Rails required to use `dining-table`) and more flexible.
6
+ In addition, it not only supports HTML output but you can output tabular data in csv or xlsx formats as well.
7
+
8
+ ## Installation
9
+
10
+ Add the following to your Gemfile:
11
+
12
+ ```ruby
13
+ gem 'dining-table'
14
+ ```
15
+
16
+ ## Basic example
17
+
18
+ A table is defined by creating a table class (usually placed in app/tables) that inherits from `DiningTable::Table` and implements the `define' method:
19
+
20
+ ```ruby
21
+ class CarTable < DiningTable::Table
22
+ def define
23
+ column :brand
24
+ column :number_of_doors
25
+ column :stock
26
+ end
27
+ end
28
+ ```
29
+
30
+ In your views, you can now provide a collection of `@cars` and render the table in HTML:
31
+
32
+ ```ruby
33
+ <%= CarTable.new(@cars, self).render %>
34
+ ```
35
+
36
+ By default, a table will have a header with column names, and no footer. There is one row per element in the collection (`@cars` in this example), and rows are rendered in order.
37
+
38
+ ## Table class
39
+
40
+ ### Defining columns
41
+
42
+ Columns are defined by using `column` in the `define` method of the table class. The content of the cell is determined by calling the
43
+ corresponding method of the object: for `column :brand`, the `brand` method is called for each car in the collection, and the result is placed in the appropriate cells.
44
+ If this is not what you want, you can provide a block:
45
+
46
+ ```ruby
47
+ class CarTable < DiningTable::Table
48
+ def define
49
+ column :brand do |object|
50
+ object.brand.upcase
51
+ end
52
+ end
53
+ end
54
+ ```
55
+
56
+ ### Headers and footers
57
+
58
+ When you don't explicitly specify a header, the header is set using `human_attribute_name` (if the objects in the collection respond to that method).
59
+ You can also manually specify a header:
60
+
61
+ ```ruby
62
+ class CarTable < DiningTable::Table
63
+ def define
64
+ column :brand, header: I18n.t('car.brand') do |object|
65
+ object.brand.upcase
66
+ end
67
+ end
68
+ end
69
+ ```
70
+
71
+ The custom header can be a string, but also a lambda or a proc.
72
+
73
+ By default, `dining-table` doesn't add a footer to the table, except when at least one column explicitly specifies a footer:
74
+
75
+ ```ruby
76
+ class CarTable < DiningTable::Table
77
+ def define
78
+ column :brand
79
+ column :stock, footer: lambda { "Total: #{ collection.sum(&:stock) }" }
80
+ end
81
+ end
82
+ ```
83
+
84
+ Please note how the collection passed in when creating the table obect (`@cars` in `CarTable.new(@cars, self)`) is available as `collection`.
85
+
86
+ ### Links and view helpers
87
+
88
+ When rendering the table in a view using `<%= CarTable.new(@cars, self).render %>`, the `self` parameter is the view context. It is made available through the `h`
89
+ method (or the `helpers` method if you prefer to be more explicit). You can use `h` to get access to methods like Rails' `link_to`, path helpers, and view helpers:
90
+
91
+ ```ruby
92
+ class CarTable < DiningTable::Table
93
+ def define
94
+ column :brand do |object|
95
+ h.link_to( object.brand, h.car_path(object) )
96
+ end
97
+ column :stock do |object|
98
+ h.number_with_delimiter( object.stock )
99
+ end
100
+ end
101
+ end
102
+ ```
103
+
104
+ You can also use `h` in headers or footers:
105
+
106
+ ```ruby
107
+ class CarTable < DiningTable::Table
108
+ def define
109
+ column :brand, header: h.link_to('Brand', h.some_path)
110
+ end
111
+ end
112
+ ```
113
+
114
+ When you want to render a table outside of a view (or when rendering csv or xlsx tables, see further) you have the following options:
115
+ * Pass in `nil` if you don't use the `h` helper in any column: `CarTable.new(@cars, nil).render`
116
+ * If you do use `h`, pass in an object that responds to the methods you use. This might be a Rails view context, or any other object:
117
+
118
+ ```ruby
119
+ # in app/tables/car_table.rb
120
+ class CarTable < DiningTable::Table
121
+ def define
122
+ column :brand do |object|
123
+ h.my_helper( object.brand )
124
+ end
125
+ end
126
+ end
127
+
128
+ # somewhere else
129
+ class FakeViewContext
130
+ def my_helper(a)
131
+ a
132
+ end
133
+ end
134
+
135
+ # when rendering
136
+ cars = Car.order(:brand)
137
+ CarTable.new(cars, FakeViewContext.new).render
138
+ ```
139
+
140
+ ### Actions
141
+
142
+ In the case of HTML tables, one often includes an actions column with links to show, edit, delete, etc. the object. While you can do that using a regular column
143
+ it is easier using a special actions column:
144
+
145
+ ```ruby
146
+ class CarTable < DiningTable::Table
147
+ def define
148
+ column :brand
149
+ actions header: I18n.t('shared.action') do |object|
150
+ action { |object| h.link_to( I18n.t('shared.show'), h.car_path(object) ) }
151
+ action { |object| h.link_to( I18n.t('shared.edit'), h.edit_car_path(object) ) if object.editable? }
152
+ end
153
+ end
154
+ end
155
+ ```
156
+
157
+ ### Options
158
+
159
+ When creating the table object to render, you can pass in an options hash:
160
+
161
+ ```ruby
162
+ <%= CarTable.new(@cars, self, admin: current_user.admin? ).render %>
163
+ ```
164
+
165
+ The passed in options are available as `options` when defining the table. One example of where this is useful is hiding certain columns or actions from non-admin users:
166
+
167
+ ```ruby
168
+ class CarTable < DiningTable::Table
169
+ def define
170
+ admin = options[:admin]
171
+
172
+ column :brand
173
+ column :number_of_doors
174
+ column :stock if admin
175
+ actions header: I18n.t('shared.action') do |object|
176
+ action { |object| h.link_to( I18n.t('shared.show'), h.car_path(object) ) }
177
+ action { |object| h.link_to( I18n.t('shared.edit'), h.edit_car_path(object) ) if admin }
178
+ end
179
+ end
180
+ end
181
+ ```
182
+
183
+ ## Presenters
184
+
185
+ ### HTML
186
+
187
+ The default presenter is HTML (i.e. `DiningTable::Presenters::HTMLPresenter`), so `CarTable.new(@cars, self).render` will generate a table in HTML.
188
+ 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`
189
+ elements for some columns in the html table:
190
+
191
+ ```ruby
192
+ class CarTable < DiningTable::Table
193
+ def define
194
+ column :brand
195
+ column :number_of_doors, html: { td_options: { class: 'center' }, th_options: { class: :center } }
196
+ column :stock, html: { td_options: { class: 'center' }, th_options: { class: :center } }
197
+ end
198
+ end
199
+ ```
200
+
201
+ 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.
202
+
203
+ By instantiating the presenter yourself it is possible to specify options. For example:
204
+
205
+ ```ruby
206
+ <%= CarTable.new(@cars, self, presenter: DiningTable::Presenters::HTMLPresenter.new( class: 'table table-bordered' )).render %>
207
+ ```
208
+
209
+ It is also possible to wrap the table in another tag (a div for instance):
210
+
211
+ ```ruby
212
+ <%= CarTable.new(@cars, self,
213
+ presenter: DiningTable::Presenters::HTMLPresenter.new( class: 'table table-bordered',
214
+ wrap: { tag: :div, class: 'table-responsive' } )).render %>
215
+ ```
216
+
217
+ Both of these html options are usually best set as defaults, see [Configuration](#configuration)
218
+
219
+ ### CSV
220
+
221
+ `dining-table` can also generate csv files instead of HTML tables. In order to do that, specify the presenter when instantiating the table object. You could do
222
+ the following in a Rails controller action, for instance:
223
+
224
+ ```ruby
225
+ def export_csv
226
+ collection = Car.order(:brand)
227
+ csv = CarTable.new( collection, nil, :presenter => DiningTable::Presenters::CSVPresenter.new ).render
228
+ send_data( csv, :filename => 'export.csv', :content_type => "text/csv" )
229
+ end
230
+ ```
231
+
232
+ The CSV Presenter uses the CSV class from the Ruby standard library. Options passed in through the :csv key will be passed on to `CSV.new`:
233
+
234
+ ```ruby
235
+ csv = CarTable.new( collection, nil, :presenter => DiningTable::Presenters::CSVPresenter.new( csv: { col_sep: ';' } ) ).render
236
+ ```
237
+
238
+ CSV options can also be set as defaults, see [Configuration](#configuration)
239
+
240
+ It can often be useful to use the same table class with both html and csv presenters. Usually, you don't want the action column in your csv file.
241
+ You can easilly omit it when the presenter is not HTML:
242
+
243
+ ```ruby
244
+ class CarTable < DiningTable::Table
245
+ def define
246
+ column :brand
247
+ actions header: I18n.t('shared.action') do |object|
248
+ action { |object| h.link_to( I18n.t('shared.show'), h.car_path(object) ) }
249
+ action { |object| h.link_to( I18n.t('shared.edit'), h.edit_car_path(object) ) }
250
+ end if presenter.type?(:html)
251
+ end
252
+ end
253
+ ```
254
+
255
+ Note that you also have access to the `presenter` inside column blocks, so if necessary you can adapt a column's content accordingly:
256
+
257
+ ```ruby
258
+ class CarTable < DiningTable::Table
259
+ def define
260
+ column :brand do |object|
261
+ if presenter.type?(:html)
262
+ h.link_to( object.brand, h.car_path(object) )
263
+ else
264
+ object.brand # no link for csv and xlsx
265
+ end
266
+ end
267
+ end
268
+ end
269
+ ```
270
+
271
+ ### Excel (xlsx)
272
+
273
+ 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
274
+ your Gemfile yourself if you want to use the Excel presenter.
275
+
276
+ In order to use the Excel presenter, pass it in as a presenter and provide an axlsx worksheet:
277
+
278
+ ```ruby
279
+ collection = Car.order(:brand)
280
+ # sheet is the axlsx worksheet in which the table will be rendered
281
+ CarTable.new( collection, nil, :presenter => DiningTable::Presenters::ExcelPresenter.new( sheet ) ).render
282
+ ```
283
+
284
+ ## Configuration <a name="configuration"></a>
285
+
286
+ You can set default options for the different presenters in an initializer (e.g. `config/initializers/dining-table.rb`):
287
+
288
+ ```ruby
289
+ DiningTable.configure do |config|
290
+ config.html_presenter.default_options = { class: 'table table-bordered table-hover',
291
+ wrap: { tag: :div, class: 'table-responsive' } }
292
+ config.csv_presenter.default_options = { :csv => { col_sep: ';' } }
293
+ end
294
+ ```
295
+
296
+ ## Copyright
297
+
298
+ Copyright (c) 2016 Michaël Van Damme. See LICENSE.txt for further details.
@@ -0,0 +1,51 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+
14
+ require 'juwelier'
15
+ Juwelier::Tasks.new do |gem|
16
+ # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
17
+ gem.name = "dining-table"
18
+ gem.homepage = "http://github.com/mvdamme/dining-table"
19
+ gem.license = "MIT"
20
+ gem.summary = %Q{Create tables easily. Supports html, csv and xlsx.}
21
+ gem.description = %Q{Easily output tabular data, be it in HTML, CSV or XLSX. Create clean table classes instead of messing with views to create nice tables.}
22
+ gem.email = "michael.vandamme@vub.ac.be"
23
+ gem.authors = ["Micha\u{eb}l Van Damme"]
24
+ # dependencies defined in Gemfile
25
+ end
26
+ Juwelier::RubygemsDotOrgTasks.new
27
+
28
+ require 'rake/testtask'
29
+ Rake::TestTask.new(:test) do |test|
30
+ test.libs << 'lib' << 'test'
31
+ test.pattern = 'test/**/test_*.rb'
32
+ test.verbose = true
33
+ end
34
+
35
+ desc "Code coverage detail"
36
+ task :simplecov do
37
+ ENV['COVERAGE'] = "true"
38
+ Rake::Task['test'].execute
39
+ end
40
+
41
+ task :default => :test
42
+
43
+ require 'rdoc/task'
44
+ Rake::RDocTask.new do |rdoc|
45
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
46
+
47
+ rdoc.rdoc_dir = 'rdoc'
48
+ rdoc.title = "dining-table #{version}"
49
+ rdoc.rdoc_files.include('README*')
50
+ rdoc.rdoc_files.include('lib/**/*.rb')
51
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,60 @@
1
+ # Generated by juwelier
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: dining-table 0.1.0 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "dining-table"
9
+ s.version = "0.1.0"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib"]
13
+ s.authors = ["Micha\u{eb}l Van Damme"]
14
+ s.date = "2016-07-31"
15
+ s.description = "Easily output tabular data, be it in HTML, CSV or XLSX. Create clean table classes instead of messing with views to create nice tables."
16
+ s.email = "michael.vandamme@vub.ac.be"
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.md"
20
+ ]
21
+ s.files = [
22
+ "Gemfile",
23
+ "Gemfile.lock",
24
+ "LICENSE",
25
+ "README.md",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "dining-table.gemspec",
29
+ "lib/dining-table.rb",
30
+ "lib/dining-table/columns/actions_column.rb",
31
+ "lib/dining-table/columns/column.rb",
32
+ "lib/dining-table/config.rb",
33
+ "lib/dining-table/presenters/csv_presenter.rb",
34
+ "lib/dining-table/presenters/excel_presenter.rb",
35
+ "lib/dining-table/presenters/html_presenter.rb",
36
+ "lib/dining-table/presenters/presenter.rb",
37
+ "lib/dining-table/presenters/spreadsheet_presenter.rb",
38
+ "lib/dining-table/table.rb"
39
+ ]
40
+ s.homepage = "http://github.com/mvdamme/dining-table"
41
+ s.licenses = ["MIT"]
42
+ s.rubygems_version = "2.4.8"
43
+ s.summary = "Create tables easily. Supports html, csv and xlsx."
44
+
45
+ if s.respond_to? :specification_version then
46
+ s.specification_version = 4
47
+
48
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
49
+ s.add_development_dependency(%q<bundler>, ["~> 1.0"])
50
+ s.add_development_dependency(%q<juwelier>, ["~> 2.1.0"])
51
+ else
52
+ s.add_dependency(%q<bundler>, ["~> 1.0"])
53
+ s.add_dependency(%q<juwelier>, ["~> 2.1.0"])
54
+ end
55
+ else
56
+ s.add_dependency(%q<bundler>, ["~> 1.0"])
57
+ s.add_dependency(%q<juwelier>, ["~> 2.1.0"])
58
+ end
59
+ end
60
+
@@ -0,0 +1,12 @@
1
+ require 'dining-table/config'
2
+
3
+ require 'dining-table/table'
4
+
5
+ require 'dining-table/columns/column'
6
+ require 'dining-table/columns/actions_column'
7
+
8
+ require 'dining-table/presenters/presenter'
9
+ require 'dining-table/presenters/html_presenter'
10
+ require 'dining-table/presenters/spreadsheet_presenter'
11
+ require 'dining-table/presenters/csv_presenter'
12
+ require 'dining-table/presenters/excel_presenter'
@@ -0,0 +1,36 @@
1
+ module DiningTable
2
+
3
+ module Columns
4
+
5
+ class ActionsColumn < Column
6
+
7
+ def value(object)
8
+ if block
9
+ @incremental_value = ''
10
+ @current_object = object
11
+ self.instance_eval(&block)
12
+ @incremental_value
13
+ end
14
+ end
15
+
16
+ private
17
+
18
+ def action(&block)
19
+ action_value = yield(@current_object)
20
+ @incremental_value += action_value.try(:to_s) if action_value
21
+ end
22
+
23
+ # offer methods normally available on Table that could be used by the action blocks
24
+ [ :h, :helpers, :collection, :index, :presenter ].each do |method|
25
+ self.class_eval <<-eos, __FILE__, __LINE__+1
26
+ def #{method}(*args)
27
+ table.#{method}
28
+ end
29
+ eos
30
+ end
31
+
32
+ end
33
+
34
+ end
35
+
36
+ end
@@ -0,0 +1,55 @@
1
+ module DiningTable
2
+
3
+ module Columns
4
+
5
+ class Column
6
+
7
+ attr_accessor :name, :table, :options, :block
8
+
9
+ def initialize( table, name, options = {}, &block)
10
+ self.table = table
11
+ self.name = name
12
+ self.options = options
13
+ self.block = block
14
+ end
15
+
16
+ def value(object)
17
+ if block
18
+ block.call(object, self)
19
+ else
20
+ object.send(name).try(:to_s) if object.respond_to?(name)
21
+ end
22
+ end
23
+
24
+ def header
25
+ @header ||= begin
26
+ label = determine_label(:header)
27
+ return label if label
28
+ object_class = table.collection.first.try(:class)
29
+ object_class.human_attribute_name( name ) if object_class && object_class.respond_to?( :human_attribute_name )
30
+ end
31
+ end
32
+
33
+ def footer
34
+ @footer ||= determine_label(:footer)
35
+ end
36
+
37
+ def options_for(identifier)
38
+ options[ identifier ] || { }
39
+ end
40
+
41
+ private
42
+
43
+ def determine_label( name )
44
+ if options[ name ]
45
+ label_ = options[ name ]
46
+ label_ = label_.call if label_.respond_to?(:call)
47
+ return label_.try(:to_s)
48
+ end
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,35 @@
1
+ # configuration, see http://robots.thoughtbot.com/mygem-configure-block.
2
+ module DiningTable
3
+
4
+ class << self
5
+ attr_accessor :configuration
6
+ end
7
+
8
+ def self.configure
9
+ self.configuration ||= Configuration.new
10
+ yield(configuration)
11
+ end
12
+
13
+ class PresenterConfiguration
14
+ attr_accessor :default_options
15
+
16
+ def initialize
17
+ @default_options = { }
18
+ end
19
+ end
20
+
21
+ class Configuration
22
+ attr_accessor :html_presenter
23
+ attr_accessor :csv_presenter
24
+ attr_accessor :excel_presenter
25
+
26
+ def initialize
27
+ @html_presenter = PresenterConfiguration.new
28
+ @csv_presenter = PresenterConfiguration.new
29
+ @excel_presenter = PresenterConfiguration.new
30
+ end
31
+ end
32
+
33
+ end
34
+
35
+ SuckerPunch::Backgroundable.configure {}
@@ -0,0 +1,41 @@
1
+ module DiningTable
2
+
3
+ module Presenters
4
+
5
+ class CSVPresenter < SpreadsheetPresenter
6
+
7
+ def initialize( *args )
8
+ super
9
+ self.output = ''
10
+ end
11
+
12
+ def identifier
13
+ :csv
14
+ end
15
+
16
+ def output
17
+ stringio.string
18
+ end
19
+
20
+ private
21
+
22
+ attr_writer :output
23
+ attr_accessor :stringio
24
+
25
+ def csv
26
+ @csv ||= begin
27
+ self.stringio = StringIO.new
28
+ csv_options = options[:csv] || { }
29
+ CSV.new(stringio, csv_options)
30
+ end
31
+ end
32
+
33
+ def add_row(array)
34
+ csv.add_row( array )
35
+ end
36
+
37
+ end
38
+
39
+ end
40
+
41
+ end
@@ -0,0 +1,28 @@
1
+ module DiningTable
2
+
3
+ module Presenters
4
+
5
+ class ExcelPresenter < SpreadsheetPresenter
6
+
7
+ def initialize( worksheet, *args )
8
+ super( *args )
9
+ self.worksheet = worksheet
10
+ end
11
+
12
+ def identifier
13
+ :xlsx
14
+ end
15
+
16
+ private
17
+
18
+ attr_accessor :worksheet
19
+
20
+ def add_row(array)
21
+ worksheet.add_row( array )
22
+ end
23
+
24
+ end
25
+
26
+ end
27
+
28
+ end
@@ -0,0 +1,142 @@
1
+ module DiningTable
2
+
3
+ module Presenters
4
+
5
+ class HTMLPresenter < Presenter
6
+
7
+ def initialize( *args )
8
+ super
9
+ self.output = ''
10
+ end
11
+
12
+ def identifier
13
+ :html
14
+ end
15
+
16
+ def start_table
17
+ if options[:wrap]
18
+ add_tag(:start, wrap_tag, wrap_options )
19
+ end
20
+ add_tag(:start, :table, options )
21
+ end
22
+
23
+ def end_table
24
+ add_tag(:end, :table)
25
+ if options[:wrap]
26
+ add_tag(:end, wrap_tag )
27
+ end
28
+ end
29
+
30
+ def start_body
31
+ add_tag(:start, :tbody)
32
+ end
33
+
34
+ def end_body
35
+ add_tag(:end, :tbody)
36
+ end
37
+
38
+ def render_row( object )
39
+ add_tag(:start, :tr)
40
+ columns.each do |column|
41
+ value = column.value( object )
42
+ render_cell( value, column.options_for( identifier ) )
43
+ end
44
+ add_tag(:end, :tr)
45
+ end
46
+
47
+ def render_header
48
+ add_tag(:start, :thead)
49
+ add_tag(:start, :tr)
50
+ columns.each do |column|
51
+ value = column.header
52
+ render_header_cell( value, column.options_for( identifier ) )
53
+ end
54
+ add_tag(:end, :tr)
55
+ add_tag(:end, :thead)
56
+ end
57
+
58
+ def render_footer
59
+ footers = columns.each.map(&:footer)
60
+ if footers.map(&:blank?).uniq != [ true ]
61
+ add_tag(:start, :tfoot)
62
+ add_tag(:start, :tr)
63
+ columns.each_with_index do |column, index|
64
+ value = footers[index]
65
+ render_footer_cell( value, column.options_for( identifier ) )
66
+ end
67
+ add_tag(:end, :tr)
68
+ add_tag(:end, :tfoot)
69
+ end
70
+ end
71
+
72
+ def output
73
+ @output.html_safe
74
+ end
75
+
76
+ private
77
+
78
+ attr_writer :output
79
+
80
+ def output_
81
+ @output
82
+ end
83
+
84
+ def add_tag(type, tag, options = {})
85
+ string = send("#{ type.to_s }_tag", tag, options)
86
+ output_ << string
87
+ end
88
+
89
+ def start_tag(tag, options = {})
90
+ "<#{ tag.to_s } #{ options_string(options) }>"
91
+ end
92
+
93
+ def end_tag(tag, options = {})
94
+ "</#{ tag.to_s }>"
95
+ end
96
+
97
+ def render_cell( string, options )
98
+ render_general_cell( string, options, :td, :td_options )
99
+ end
100
+
101
+ def render_header_cell( string, options )
102
+ render_general_cell( string, options, :th, :th_options )
103
+ end
104
+
105
+ def render_footer_cell( string, options )
106
+ render_general_cell( string, options, :td, :footer_options )
107
+ end
108
+
109
+ def render_general_cell( string, options, cell_tag, options_identifier )
110
+ add_tag(:start, cell_tag, options[ options_identifier ] )
111
+ output_ << string.to_s
112
+ add_tag(:end, cell_tag)
113
+ end
114
+
115
+ def options_string(options)
116
+ return '' if options.nil?
117
+ options.each_key.inject('') do |result, key|
118
+ result += " #{key.to_s}=\"#{ options[key] }\"" if !options_to_skip.include?( key )
119
+ result
120
+ end
121
+ end
122
+
123
+ # don't output these keys as html options in options_string
124
+ def options_to_skip
125
+ [ :wrap ]
126
+ end
127
+
128
+ def wrap_tag
129
+ options[:wrap][:tag]
130
+ end
131
+
132
+ def wrap_options
133
+ options_ = options[:wrap].dup
134
+ options_.delete(:tag)
135
+ options_
136
+ end
137
+
138
+ end
139
+
140
+ end
141
+
142
+ end
@@ -0,0 +1,51 @@
1
+ module DiningTable
2
+
3
+ module Presenters
4
+
5
+ class Presenter
6
+
7
+ attr_accessor :table, :options, :view_context
8
+
9
+ def initialize( options = {} )
10
+ self.options = default_options.merge( options )
11
+ end
12
+
13
+ def connect_to( table )
14
+ self.table = table
15
+ end
16
+
17
+ def identifier
18
+ raise NotImplementedError
19
+ end
20
+
21
+ def type?( identifier_ )
22
+ identifier == identifier_
23
+ end
24
+
25
+ [ :start_table, :end_table, :render_header, :start_body, :end_body, :row, :render_footer, :output ].each do |method|
26
+ self.class_eval <<-eos, __FILE__, __LINE__+1
27
+ def #{method}(*args)
28
+ end
29
+ eos
30
+ end
31
+
32
+ private
33
+
34
+ def columns
35
+ table.columns
36
+ end
37
+
38
+ def default_options
39
+ presenter = "#{ identifier }_presenter"
40
+ if DiningTable.configuration.respond_to?( presenter )
41
+ DiningTable.configuration.send( presenter ).default_options
42
+ else
43
+ { }
44
+ end
45
+ end
46
+
47
+ end
48
+
49
+ end
50
+
51
+ end
@@ -0,0 +1,54 @@
1
+ module DiningTable
2
+
3
+ module Presenters
4
+
5
+ class SpreadsheetPresenter < Presenter
6
+
7
+ def render_row( object )
8
+ values = columns.map do |column|
9
+ value = column.value( object )
10
+ value = clean( value ) if !!options[:clean]
11
+ value
12
+ end
13
+ add_row( values )
14
+ end
15
+
16
+ def render_header
17
+ add_row( header_strings )
18
+ end
19
+
20
+ def render_footer
21
+ footers = footer_strings
22
+ if footers.map(&:blank?).uniq != [ true ]
23
+ add_row( footers )
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def header_strings
30
+ columns.map(&:header)
31
+ end
32
+
33
+ def footer_strings
34
+ columns.map(&:footer)
35
+ end
36
+
37
+ def add_row( row )
38
+ raise NotImplementedError
39
+ end
40
+
41
+ def clean(string)
42
+ replacements = [['&mdash;', '--'], ['&ndash;', '-'], ['&nbsp;', ' '] ]
43
+ base = view_context.strip_tags(string)
44
+ replacements.each do |pattern, replacement|
45
+ base.gsub!(pattern, replacement)
46
+ end
47
+ base
48
+ end
49
+
50
+ end
51
+
52
+ end
53
+
54
+ end
@@ -0,0 +1,69 @@
1
+ module DiningTable
2
+
3
+ class Table
4
+
5
+ attr_accessor :collection, :presenter, :options, :index, :columns, :action_columns, :view_context
6
+
7
+ def initialize( collection, view_context, options = {} )
8
+ self.collection = collection
9
+ self.view_context = view_context
10
+ self.index = 0
11
+ self.columns = [ ]
12
+ self.options = options
13
+ initialize_presenter( options )
14
+ define
15
+ end
16
+
17
+ def define
18
+ raise NotImplementedError
19
+ end
20
+
21
+ def render
22
+ presenter.start_table
23
+ presenter.render_header unless no_header
24
+ collection.each_with_index do |object, index_|
25
+ self.index = index_
26
+ presenter.render_row( object )
27
+ end
28
+ presenter.render_footer
29
+ presenter.end_table
30
+ presenter.output
31
+ end
32
+
33
+ def helpers
34
+ view_context
35
+ end
36
+ alias_method :h, :helpers
37
+
38
+ private
39
+
40
+ attr_accessor :no_header
41
+
42
+ # auxiliary function
43
+ def column(name, options = {}, &block)
44
+ klass = options[:class]
45
+ klass ||= Columns::Column
46
+ self.columns << klass.new(self, name, options, &block)
47
+ end
48
+
49
+ def actions(options = {}, &block)
50
+ column(:actions__, { :class => Columns::ActionsColumn }.merge( options ), &block )
51
+ end
52
+
53
+ def initialize_presenter( options )
54
+ self.presenter = options[ :presenter ]
55
+ self.presenter ||= default_presenter.new
56
+ self.presenter.connect_to( self )
57
+ end
58
+
59
+ def default_presenter
60
+ Presenters::HTMLPresenter
61
+ end
62
+
63
+ def skip_header
64
+ self.no_header = true
65
+ end
66
+
67
+ end
68
+
69
+ end
metadata ADDED
@@ -0,0 +1,91 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dining-table
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Michaël Van Damme
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-07-31 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: juwelier
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 2.1.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 2.1.0
41
+ description: Easily output tabular data, be it in HTML, CSV or XLSX. Create clean
42
+ table classes instead of messing with views to create nice tables.
43
+ email: michael.vandamme@vub.ac.be
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files:
47
+ - LICENSE
48
+ - README.md
49
+ files:
50
+ - Gemfile
51
+ - Gemfile.lock
52
+ - LICENSE
53
+ - README.md
54
+ - Rakefile
55
+ - VERSION
56
+ - dining-table.gemspec
57
+ - lib/dining-table.rb
58
+ - lib/dining-table/columns/actions_column.rb
59
+ - lib/dining-table/columns/column.rb
60
+ - lib/dining-table/config.rb
61
+ - lib/dining-table/presenters/csv_presenter.rb
62
+ - lib/dining-table/presenters/excel_presenter.rb
63
+ - lib/dining-table/presenters/html_presenter.rb
64
+ - lib/dining-table/presenters/presenter.rb
65
+ - lib/dining-table/presenters/spreadsheet_presenter.rb
66
+ - lib/dining-table/table.rb
67
+ homepage: http://github.com/mvdamme/dining-table
68
+ licenses:
69
+ - MIT
70
+ metadata: {}
71
+ post_install_message:
72
+ rdoc_options: []
73
+ require_paths:
74
+ - lib
75
+ required_ruby_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ required_rubygems_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: '0'
85
+ requirements: []
86
+ rubyforge_project:
87
+ rubygems_version: 2.4.8
88
+ signing_key:
89
+ specification_version: 4
90
+ summary: Create tables easily. Supports html, csv and xlsx.
91
+ test_files: []