entable 0.0.1

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.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .#*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tabler.gemspec
4
+ gemspec
5
+
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 conanite
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # Entable
2
+
3
+ LibreOffice and Microsoft Office are both able to open a HTML file and interpret the contents of the <table> element as a worksheet.
4
+
5
+ This gem generates such a HTML file, given a collection and a configuration. For each column, the configuration specifies the column header text, and how to extract the data for each cell in that column.
6
+
7
+
8
+ ## Installation
9
+
10
+ Add this line to your application's Gemfile:
11
+
12
+ gem 'entable'
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install entable
21
+
22
+ ## Usage
23
+
24
+
25
+ Basic usage:
26
+
27
+ include 'entable/builder'
28
+
29
+ def table_config
30
+ # return a Hash that you read from somewhere
31
+ end
32
+
33
+ def to_xls items, *args
34
+ @interpreter ||= build_interpreter(table_config)
35
+ @interpreter.to_xls items, *args # returns a HTML file as text
36
+ end
37
+
38
+
39
+ A configuration for a simple one-row-per-item table should look like this:
40
+
41
+ ---
42
+ columns:
43
+ - title: Last Name
44
+ content: "%{last}"
45
+ - title: First Name
46
+ content: "%{first}"
47
+ - title: Address
48
+ content: "%{address}"
49
+
50
+
51
+ A more complex configuration, where you want to filter your collection and wrap each item, producing multiple rows per item, might look like this:
52
+
53
+ ---
54
+ preprocess:
55
+ wrap: contact_export
56
+ transform: sort_by_last_name
57
+ multi-row:
58
+ - - title: Last Name
59
+ content: "%{last}"
60
+ - title: First Name
61
+ content: "%{ first }"
62
+ - - title: Address
63
+ content: "%{full_address}"
64
+ attributes:
65
+ colspan: 2
66
+
67
+ In this example, there are two lines per item, and the single cell on the second line will span two columns. Before anything happens, the collection is transformed by #sort_by_last_name, and then each item is wrapped by #contact_export
68
+
69
+ A transformer allows you sort, filter, or transform your collection in any way. The collection passed here is the collection that was passed to #to_xls above. Here's how you install a transformer:
70
+
71
+ Entable.add_transformer :sort_by_full_name do |collection|
72
+ collection.sort { |a, b| a.full_name <=> b.full_name }
73
+ end
74
+
75
+ Or, similarly,
76
+
77
+ Entable.add_transformer :sort_by_full_name do |collection|
78
+ collection.order("full_name ASC")
79
+ end
80
+
81
+
82
+ Wrappers are not strictly necessary; you could apply a wrapper inside a transformer.
83
+ Separating the two frees you to apply each independently.
84
+
85
+ The most important purpose of a wrapper is to provide an isolation layer between
86
+ the table configuration and your objects. Remember, the table configuration can
87
+ invoke any ruby method on each item, so if you are allowing untrusted parties
88
+ create a configuration, you need to make sure that only safe methods are exposed.
89
+
90
+ A secondary purpose of a wrapper is to expose pre-formatted data values; you might
91
+ need to provide translations for some fields, or localised versions of numbers and
92
+ dates.
93
+
94
+ To install a wrapper, call Entable#add_wrapper
95
+
96
+ Entable.add_wrapper :contact_export do |item, *args|
97
+ ContactTable.new item, *args
98
+ end
99
+
100
+ In this example, the _item_ is the object to be wrapped, and _*args_ are the arguments passed to #to_xls earlier. This allows you pass any extra parameters to your wrapper that you might need in order to render each item as a row in a spreadsheet.
101
+
102
+
103
+
104
+ ## Contributing
105
+
106
+ 1. Fork it
107
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
108
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
109
+ 4. Push to the branch (`git push origin my-new-feature`)
110
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/entable.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- coding: utf-8; mode: ruby -*-
2
+
3
+ lib = File.expand_path('../lib', __FILE__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+
6
+ require 'entable/version'
7
+
8
+ Gem::Specification.new do |gem|
9
+ gem.name = "entable"
10
+ gem.version = Entable::VERSION
11
+ gem.authors = ["conanite"]
12
+ gem.email = ["conan@conandalton.net"]
13
+ gem.description = %q{Generate HTML tables which popular spreadsheet software packages know how to read }
14
+ gem.summary = %q{LibreOffice and Microsoft Office are both able to open a HTML file and interpret the contents of the <table> element as a worksheet.
15
+
16
+ This gem generates such a HTML file, given a collection and a configuration. For each column, the configuration specifies the column header text, and how to extract the data for each cell in that column.}
17
+
18
+ gem.homepage = "https://github.com/conanite/entable"
19
+
20
+ gem.add_development_dependency 'rspec', '~> 2.9'
21
+
22
+ gem.files = `git ls-files`.split($/)
23
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
24
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
25
+ gem.require_paths = ["lib"]
26
+ end
data/lib/entable.rb ADDED
@@ -0,0 +1,16 @@
1
+ require "entable/version"
2
+
3
+ module Entable
4
+ def self.add_transformer name, &block
5
+ Entable::Transformer.add_transformer name, &block
6
+ end
7
+
8
+ def self.add_wrapper name, &block
9
+ Entable::Wrapper.add_wrapper name, &block
10
+ end
11
+ end
12
+
13
+ require 'entable/xls_export'
14
+ require 'entable/html_builder'
15
+ require 'entable/transformer'
16
+ require 'entable/wrapper'
@@ -0,0 +1,101 @@
1
+ module Entable::HtmlBuilder
2
+ class ContentDefinitionError < StandardError; end
3
+
4
+ def parse_column_content str, errors
5
+ error = false
6
+
7
+ str = str.strip.gsub(/%\{[^}]*\}/) { |match|
8
+ attr = match.gsub(/^%\{/, '').gsub(/\}$/, '').gsub(/-/, '_').strip
9
+ if attr.match(/^[a-zA-Z_][a-zA-Z0-9_]*$/)
10
+ "%{item.#{attr}}"
11
+ else
12
+ errors << "prohibited attribute #{match}"
13
+ error = true
14
+ end
15
+ }
16
+
17
+ raise ContentDefinitionError.new if error
18
+ str
19
+ end
20
+
21
+ def parse_column_definitions conf
22
+ title_rows = []
23
+ content_rows = []
24
+ attribute_rows = []
25
+ errors = []
26
+
27
+ raise "no config: #{conf.inspect}" unless conf.is_a?(Hash)
28
+
29
+ rows = conf["multi-row"]
30
+ if rows.nil?
31
+ one_row = conf["columns"]
32
+ rows = one_row ? [one_row] : []
33
+ end
34
+
35
+ rows.each do |columns|
36
+ titles = []
37
+ contents = []
38
+ attrs = []
39
+
40
+ columns.each do |column_def|
41
+ titles << column_def["title"]
42
+ attrs << (column_def["attributes"] || { })
43
+ begin
44
+ contents << parse_column_content(column_def["content"], errors)
45
+ rescue ContentDefinitionError => e
46
+ end
47
+ end
48
+
49
+ title_rows << titles
50
+ content_rows << contents
51
+ attribute_rows << attrs
52
+ end
53
+
54
+ raise errors.join("\n") unless errors.empty?
55
+
56
+ preprocess = conf["preprocess"] || { }
57
+ [title_rows, content_rows, attribute_rows, preprocess["transform"], preprocess["wrap"]]
58
+ end
59
+
60
+ def build_title_rows title_rows, attribute_rows
61
+ titles = title_rows.zip(attribute_rows).map { |tr, attrs|
62
+ "<tr>" + tr.zip(attrs).map { |t, attr|
63
+ colspan_attr = attr["colspan"] ? " colspan=#{attr["colspan"]}" : ""
64
+ "<td#{colspan_attr}>#{quote_for_xls(to_utf8 t)}</td>"}.join(" ") + "</tr>"
65
+ }.join("\n")
66
+ end
67
+
68
+ def build_line_interpreter columns, attrs
69
+ columns.zip(attrs).map { |cell, attr|
70
+ colspan_attr = attr["colspan"] ? " colspan=#{attr["colspan"]}" : ""
71
+ "<td#{colspan_attr}>\#{to_utf8(#{cell.inspect})}</td>"
72
+ }.join(' ').gsub("%{", '#{')
73
+ end
74
+
75
+ def build_interpreter spreadsheet_config
76
+ title_rows, content_rows, attribute_rows, transformer, wrapper = parse_column_definitions(spreadsheet_config)
77
+ titles = build_title_rows title_rows, attribute_rows
78
+ titles = to_utf8 "\n#{titles}\n"
79
+
80
+ code = <<CODE
81
+
82
+ include Entable::XlsExport
83
+
84
+ def to_xls items, *args
85
+ #{transformer ? "items = Entable::Transformer.apply_transform(items, :#{transformer})" : ""}
86
+ #{wrapper ? "items = Entable::Wrapper.apply_wrapper :#{wrapper}, items, *args" : ""}
87
+ #{ "#{xls_html_prologue}#{titles}".inspect } + content(items) + #{xls_html_epilogue.inspect}
88
+ end
89
+
90
+ def lines item
91
+ "#{content_rows.zip(attribute_rows).map { |row, attrs| "<tr>#{build_line_interpreter row, attrs}</tr>\\n" }.join }"
92
+ end
93
+
94
+ def content(items)
95
+ items.map { |item| lines(item) }.join
96
+ end
97
+ CODE
98
+
99
+ Class.new do; class_eval code; end.new
100
+ end
101
+ end
@@ -0,0 +1,10 @@
1
+ module Entable::Transformer
2
+ def self.add_transformer name, &block
3
+ @@transformers ||= { }
4
+ @@transformers[name.to_sym] = block
5
+ end
6
+
7
+ def self.apply_transform collection, transform_name
8
+ @@transformers[transform_name.to_sym].call collection
9
+ end
10
+ end
@@ -0,0 +1,3 @@
1
+ module Entable
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,11 @@
1
+ module Entable::Wrapper
2
+ def self.add_wrapper name, &block
3
+ @@wrappers ||= { }
4
+ @@wrappers[name.to_sym] = block
5
+ end
6
+
7
+ def self.apply_wrapper name, items, *args
8
+ wrapper = @@wrappers[name.to_sym]
9
+ items.map { |item| wrapper.call(item, *args) }
10
+ end
11
+ end
@@ -0,0 +1,68 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ module Entable::XlsExport
4
+ def enc; "UTF-8"; end
5
+
6
+ def csv_date date
7
+ return I18n.l(date, :format => :csv) if date
8
+ end
9
+
10
+ def xls_html_prologue
11
+ "<html><head><meta content='application/vnd.ms-excel;charset=#{enc}' http-equiv='Content-Type'><meta content='#{enc}' http-equiv='Encoding'></head><body><table>"
12
+ end
13
+
14
+ def xls_html_epilogue
15
+ "</table></body></html>"
16
+ end
17
+
18
+ def to_utf8 str
19
+ (str || "").encode(enc)
20
+ rescue
21
+ raise "unable to convert '#{str}' to #{enc}"
22
+ end
23
+
24
+ def write_items_as_csv items
25
+ render :text => items.map(&:to_csv).join("\n"), :content_type => "text/csv; charset=ISO-8859-1"
26
+ end
27
+
28
+ def array_for_xls item
29
+ item.is_a?(Array) ? item : item.to_csv_array
30
+ end
31
+
32
+ def items_as_xls_string items
33
+ prologue = xls_html_prologue
34
+ epilogue = xls_html_epilogue
35
+ items = items.map { |item| array_for_xls(item) }
36
+ items = items.map { |item| yield item } if block_given?
37
+ lines = items.map { |item| "<tr><td>" + item.join("</td><td>") + "</td></tr>" }
38
+ lines = lines.map { |line| to_utf8 line }
39
+ prologue + lines.join("\n") + epilogue
40
+ end
41
+
42
+ def write_items_as_xls items, &block
43
+ write_text_as_xls items_as_xls_string(items, &block)
44
+ end
45
+
46
+ def write_text_as_xls text
47
+ render :text => text, :content_type => "application/vnd.ms-excel; charset=#{enc}"
48
+ end
49
+
50
+ def quote_for_xls text
51
+ (text.is_a?(String) && needs_quoting?(text)) ? "=\"#{text}\"" : text
52
+ end
53
+
54
+ def needs_quoting? text
55
+ text.is_a?(String) && (text.match(/^0\d+$/) || text.match(/^\d+[^\d]+/))
56
+ end
57
+
58
+ def make_filename filename
59
+ return filename.reject { |x| x.blank? }.join(" ").strip.gsub(/ +/, '-') if filename.is_a?(Array)
60
+ filename
61
+ end
62
+
63
+ def set_xls_headers filename
64
+ headers["Content-Type"] = "application/vnd.ms-excel; charset=#{enc}"
65
+ headers["Content-disposition"] = "attachment; filename=\"#{make_filename filename}.xls\""
66
+ headers["charset"] = enc
67
+ end
68
+ end
@@ -0,0 +1,124 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require File.expand_path('../../spec_helper', __FILE__)
4
+
5
+ describe Entable do
6
+
7
+ it "should generate a very simple table with one row per item" do
8
+ config = {
9
+ "columns" => [
10
+ { "title" => "First", "content" => "%{firstname}" },
11
+ { "title" => "Last", "content" => "%{lastname}" },
12
+ { "title" => "Phone", "content" => "%{phone}" },
13
+ { "title" => "Postcode", "content" => "%{postcode}" },
14
+ ]
15
+ }
16
+ Exporter.new(config).to_xls(CONTACTS).should == %{<html><head><meta content='application/vnd.ms-excel;charset=UTF-8' http-equiv='Content-Type'><meta content='UTF-8' http-equiv='Encoding'></head><body><table>
17
+ <tr><td>First</td> <td>Last</td> <td>Phone</td> <td>Postcode</td></tr>
18
+ <tr><td>Conan</td> <td>Dalton</td> <td>01234567</td> <td>75020</td></tr>
19
+ <tr><td>Zed</td> <td>Zenumbra</td> <td>999999</td> <td>99999</td></tr>
20
+ <tr><td>Abraham</td> <td>Aardvark</td> <td>0000000</td> <td>0</td></tr>
21
+ <tr><td>James</td> <td>Joyce</td> <td>3647583</td> <td>75001</td></tr>
22
+ </table></body></html>}
23
+ end
24
+
25
+ it "should generate a simple table wrapping each item first" do
26
+ config = {
27
+ "preprocess" => {
28
+ "wrap" => "uppercase"
29
+ },
30
+ "columns" => [
31
+ { "title" => "First", "content" => "%{firstname}" },
32
+ { "title" => "Last", "content" => "%{lastname}" },
33
+ { "title" => "Phone", "content" => "%{phone}" },
34
+ ]
35
+ }
36
+ Exporter.new(config).to_xls(CONTACTS).should == %{<html><head><meta content='application/vnd.ms-excel;charset=UTF-8' http-equiv='Content-Type'><meta content='UTF-8' http-equiv='Encoding'></head><body><table>
37
+ <tr><td>First</td> <td>Last</td> <td>Phone</td></tr>
38
+ <tr><td>CONAN</td> <td>DALTON</td> <td>01234567</td></tr>
39
+ <tr><td>ZED</td> <td>ZENUMBRA</td> <td>999999</td></tr>
40
+ <tr><td>ABRAHAM</td> <td>AARDVARK</td> <td>0000000</td></tr>
41
+ <tr><td>JAMES</td> <td>JOYCE</td> <td>3647583</td></tr>
42
+ </table></body></html>}
43
+ end
44
+
45
+ it "should generate a simple table sorting items first" do
46
+ config = {
47
+ "preprocess" => {
48
+ "transform" => "sort_by_last_name"
49
+ },
50
+ "columns" => [
51
+ { "title" => "First", "content" => "%{firstname}" },
52
+ { "title" => "Last", "content" => "%{lastname}" },
53
+ { "title" => "Phone", "content" => "%{phone}" },
54
+ ]
55
+ }
56
+ Exporter.new(config).to_xls(CONTACTS).should == %{<html><head><meta content='application/vnd.ms-excel;charset=UTF-8' http-equiv='Content-Type'><meta content='UTF-8' http-equiv='Encoding'></head><body><table>
57
+ <tr><td>First</td> <td>Last</td> <td>Phone</td></tr>
58
+ <tr><td>Abraham</td> <td>Aardvark</td> <td>0000000</td></tr>
59
+ <tr><td>Conan</td> <td>Dalton</td> <td>01234567</td></tr>
60
+ <tr><td>James</td> <td>Joyce</td> <td>3647583</td></tr>
61
+ <tr><td>Zed</td> <td>Zenumbra</td> <td>999999</td></tr>
62
+ </table></body></html>}
63
+ end
64
+
65
+
66
+ it "should generate a table with two rows per item, applying #colspan attribute " do
67
+ config = {
68
+ "multi-row" => [
69
+ [
70
+ { "title" => "First", "content" => "%{firstname}" },
71
+ { "title" => "Last", "content" => "%{lastname}" },
72
+ ],
73
+ [
74
+ { "title" => "Postcode", "content" => "%{postcode}", "attributes" => { "colspan" => 2 } },
75
+ ]
76
+ ]
77
+ }
78
+ Exporter.new(config).to_xls(CONTACTS).should == %{<html><head><meta content='application/vnd.ms-excel;charset=UTF-8' http-equiv='Content-Type'><meta content='UTF-8' http-equiv='Encoding'></head><body><table>
79
+ <tr><td>First</td> <td>Last</td></tr>
80
+ <tr><td colspan=2>Postcode</td></tr>
81
+ <tr><td>Conan</td> <td>Dalton</td></tr>
82
+ <tr><td colspan=2>75020</td></tr>
83
+ <tr><td>Zed</td> <td>Zenumbra</td></tr>
84
+ <tr><td colspan=2>99999</td></tr>
85
+ <tr><td>Abraham</td> <td>Aardvark</td></tr>
86
+ <tr><td colspan=2>0</td></tr>
87
+ <tr><td>James</td> <td>Joyce</td></tr>
88
+ <tr><td colspan=2>75001</td></tr>
89
+ </table></body></html>}
90
+ end
91
+
92
+
93
+ it "should generate a table with two rows per item, applying #colspan attribute, sorting by last name, uppercasing names " do
94
+ config = {
95
+ "preprocess" => {
96
+ "transform" => "sort_by_last_name",
97
+ "wrap" => "uppercase"
98
+ },
99
+ "multi-row" => [
100
+ [
101
+ { "title" => "First", "content" => "%{firstname}" },
102
+ { "title" => "Last", "content" => "%{lastname}" },
103
+ ],
104
+ [
105
+ { "title" => "Postcode", "content" => "%{postcode}", "attributes" => { "colspan" => 2 } },
106
+ ]
107
+ ]
108
+ }
109
+ Exporter.new(config).to_xls(CONTACTS).should == %{<html><head><meta content='application/vnd.ms-excel;charset=UTF-8' http-equiv='Content-Type'><meta content='UTF-8' http-equiv='Encoding'></head><body><table>
110
+ <tr><td>First</td> <td>Last</td></tr>
111
+ <tr><td colspan=2>Postcode</td></tr>
112
+ <tr><td>ABRAHAM</td> <td>AARDVARK</td></tr>
113
+ <tr><td colspan=2>0</td></tr>
114
+ <tr><td>CONAN</td> <td>DALTON</td></tr>
115
+ <tr><td colspan=2>75020</td></tr>
116
+ <tr><td>JAMES</td> <td>JOYCE</td></tr>
117
+ <tr><td colspan=2>75001</td></tr>
118
+ <tr><td>ZED</td> <td>ZENUMBRA</td></tr>
119
+ <tr><td colspan=2>99999</td></tr>
120
+ </table></body></html>}
121
+ end
122
+
123
+
124
+ end
@@ -0,0 +1,64 @@
1
+ require 'entable'
2
+
3
+ # This file was generated by the `rspec --init` command. Conventionally, all
4
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
5
+ # Require this file using `require "spec_helper"` to ensure that it is only
6
+ # loaded once.
7
+ #
8
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
9
+ RSpec.configure do |config|
10
+ config.treat_symbols_as_metadata_keys_with_true_values = true
11
+ config.run_all_when_everything_filtered = true
12
+ config.filter_run :focus
13
+
14
+ # Run specs in random order to surface order dependencies. If you find an
15
+ # order dependency and want to debug it, you can fix the order by providing
16
+ # the seed, which is printed after each run.
17
+ # --seed 1234
18
+ config.order = 'random'
19
+ end
20
+
21
+ Contact = Struct.new :firstname, :lastname, :phone, :postcode
22
+
23
+ CONTACTS = []
24
+ CONTACTS << Contact.new("Conan", "Dalton", "01234567", "75020")
25
+ CONTACTS << Contact.new("Zed", "Zenumbra", "999999", "99999")
26
+ CONTACTS << Contact.new("Abraham", "Aardvark", "0000000", "0")
27
+ CONTACTS << Contact.new("James", "Joyce", "3647583", "75001")
28
+
29
+ class ContactUpper
30
+ def firstname; contact.firstname.upcase; end
31
+ def lastname; contact.lastname.upcase; end
32
+ def phone; contact.phone; end
33
+ def postcode; contact.postcode; end
34
+
35
+ attr_accessor :contact
36
+
37
+ def initialize contact
38
+ self.contact = contact
39
+ end
40
+ end
41
+
42
+ Entable.add_transformer :sort_by_last_name do |collection|
43
+ collection.sort_by &:lastname
44
+ end
45
+
46
+ Entable.add_wrapper :uppercase do |item|
47
+ ContactUpper.new item
48
+ end
49
+
50
+ class Exporter
51
+ include Entable::XlsExport
52
+ include Entable::HtmlBuilder
53
+
54
+ attr_accessor :config
55
+
56
+ def initialize config
57
+ self.config = config
58
+ end
59
+
60
+ def to_xls items
61
+ build_interpreter(config).to_xls items
62
+ end
63
+ end
64
+
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: entable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - conanite
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '2.9'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '2.9'
30
+ description: ! 'Generate HTML tables which popular spreadsheet software packages know
31
+ how to read '
32
+ email:
33
+ - conan@conandalton.net
34
+ executables: []
35
+ extensions: []
36
+ extra_rdoc_files: []
37
+ files:
38
+ - .gitignore
39
+ - .rspec
40
+ - Gemfile
41
+ - LICENSE.txt
42
+ - README.md
43
+ - Rakefile
44
+ - entable.gemspec
45
+ - lib/entable.rb
46
+ - lib/entable/html_builder.rb
47
+ - lib/entable/transformer.rb
48
+ - lib/entable/version.rb
49
+ - lib/entable/wrapper.rb
50
+ - lib/entable/xls_export.rb
51
+ - spec/entable/entable_spec.rb
52
+ - spec/spec_helper.rb
53
+ homepage: https://github.com/conanite/entable
54
+ licenses: []
55
+ post_install_message:
56
+ rdoc_options: []
57
+ require_paths:
58
+ - lib
59
+ required_ruby_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ! '>='
63
+ - !ruby/object:Gem::Version
64
+ version: '0'
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ requirements: []
72
+ rubyforge_project:
73
+ rubygems_version: 1.8.24
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: LibreOffice and Microsoft Office are both able to open a HTML file and interpret
77
+ the contents of the <table> element as a worksheet. This gem generates such a HTML
78
+ file, given a collection and a configuration. For each column, the configuration
79
+ specifies the column header text, and how to extract the data for each cell in that
80
+ column.
81
+ test_files:
82
+ - spec/entable/entable_spec.rb
83
+ - spec/spec_helper.rb