entable 0.0.1

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