make_exportable 1.0.0
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/Gemfile +12 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +187 -0
- data/Rakefile +50 -0
- data/VERSION +1 -0
- data/lib/make_exportable.rb +19 -0
- data/lib/make_exportable/core.rb +309 -0
- data/lib/make_exportable/errors.rb +7 -0
- data/lib/make_exportable/exportable_format.rb +33 -0
- data/lib/make_exportable/exportable_formats/csv.rb +34 -0
- data/lib/make_exportable/exportable_formats/excel.rb +39 -0
- data/lib/make_exportable/exportable_formats/html.rb +39 -0
- data/lib/make_exportable/exportable_formats/json.rb +36 -0
- data/lib/make_exportable/exportable_formats/tsv.rb +34 -0
- data/lib/make_exportable/exportable_formats/xml.rb +43 -0
- data/lib/make_exportable/make_exportable_helper.rb +27 -0
- data/lib/make_exportable/version.rb +9 -0
- data/rails/init.rb +1 -0
- data/spec/database.yml +10 -0
- data/spec/database.yml.sample +10 -0
- data/spec/make_exportable/formats_spec.rb +81 -0
- data/spec/make_exportable/make_exportable_helper_spec.rb +31 -0
- data/spec/make_exportable/make_exportable_spec.rb +510 -0
- data/spec/models.rb +44 -0
- data/spec/schema.rb +16 -0
- data/spec/spec_helper.rb +55 -0
- metadata +98 -0
@@ -0,0 +1,33 @@
|
|
1
|
+
module MakeExportable #:nodoc:
|
2
|
+
class ExportableFormat
|
3
|
+
|
4
|
+
class_inheritable_accessor :reference
|
5
|
+
class_inheritable_accessor :name
|
6
|
+
|
7
|
+
attr_accessor :long
|
8
|
+
attr_accessor :mime_type
|
9
|
+
|
10
|
+
class << self
|
11
|
+
# Register this format with the mothership
|
12
|
+
def register_format
|
13
|
+
unless MakeExportable.exportable_formats[self.reference]
|
14
|
+
MakeExportable.exportable_formats[self.reference] = self
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
|
20
|
+
def generate(data_set, data_headers=nil)
|
21
|
+
end
|
22
|
+
|
23
|
+
def sanitize(value)
|
24
|
+
value
|
25
|
+
end
|
26
|
+
|
27
|
+
def generate_header_option(data_headers=[])
|
28
|
+
self.mime_type += (self.data_headers.blank? || data_headers === false) ? " header=absent" : " header=present"
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require "csv"
|
2
|
+
# for compatibility with Rails 2
|
3
|
+
require 'fastercsv' if CSV.const_defined?(:Reader)
|
4
|
+
|
5
|
+
module MakeExportable #:nodoc:
|
6
|
+
class CSV < ExportableFormat
|
7
|
+
|
8
|
+
cattr_accessor :csv_type
|
9
|
+
|
10
|
+
self.reference = :csv
|
11
|
+
self.name = 'CSV'
|
12
|
+
self.register_format
|
13
|
+
self.csv_type = ::CSV.const_defined?(:Reader) ? FasterCSV : ::CSV
|
14
|
+
|
15
|
+
attr_accessor :data_set, :data_headers
|
16
|
+
|
17
|
+
def initialize(data_set, data_headers=[])
|
18
|
+
self.long = 'Comma-separated (CSV)'
|
19
|
+
self.mime_type = 'text/csv; charset=utf-8;'
|
20
|
+
self.data_set = data_set
|
21
|
+
self.data_headers = data_headers
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
def generate
|
26
|
+
generate_header_option(data_headers)
|
27
|
+
@@csv_type.generate do |csv|
|
28
|
+
csv << data_headers.map {|h| sanitize(h.humanize.titleize)} unless data_headers.blank?
|
29
|
+
data_set.each {|row| csv << row }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module MakeExportable #:nodoc:
|
2
|
+
class Excel < ExportableFormat
|
3
|
+
|
4
|
+
self.reference = :xls
|
5
|
+
self.name = 'Excel'
|
6
|
+
self.register_format
|
7
|
+
|
8
|
+
attr_accessor :data_set, :data_headers
|
9
|
+
|
10
|
+
def initialize(data_set, data_headers=[])
|
11
|
+
self.long = 'Microsoft Excel'
|
12
|
+
self.mime_type = 'application/vnd.ms-excel; charset=utf-8;'
|
13
|
+
self.data_set = data_set
|
14
|
+
self.data_headers = data_headers
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate
|
18
|
+
generate_header_option(data_headers)
|
19
|
+
output = "<table>\n"
|
20
|
+
unless data_headers.blank?
|
21
|
+
output << "\t<tr>\n"
|
22
|
+
output << data_headers.map {|h| "\t\t<th>#{sanitize(h.humanize.titleize)}</th>\n" }.join
|
23
|
+
output << "\t</tr>\n"
|
24
|
+
end
|
25
|
+
data_set.each do |row|
|
26
|
+
output << "\t<tr>\n"
|
27
|
+
output << row.map {|field| "\t\t<td>#{sanitize(field)}</td>\n"}.join
|
28
|
+
output << "\t</tr>\n"
|
29
|
+
end
|
30
|
+
output << "</table>\n"
|
31
|
+
return output
|
32
|
+
end
|
33
|
+
|
34
|
+
def sanitize(value)
|
35
|
+
value.gsub(/</, '<').gsub(/>/, '>')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module MakeExportable #:nodoc:
|
2
|
+
class HTML < ExportableFormat
|
3
|
+
|
4
|
+
self.reference = :html
|
5
|
+
self.name = 'HTML'
|
6
|
+
self.register_format
|
7
|
+
|
8
|
+
attr_accessor :data_set, :data_headers
|
9
|
+
|
10
|
+
def initialize(data_set, data_headers=[])
|
11
|
+
self.long = 'HTML'
|
12
|
+
self.mime_type = 'text/html; charset=utf-8;'
|
13
|
+
self.data_set = data_set
|
14
|
+
self.data_headers = data_headers
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate
|
18
|
+
generate_header_option(data_headers)
|
19
|
+
output = "<table>\n"
|
20
|
+
unless data_headers.blank?
|
21
|
+
output << "\t<tr>\n"
|
22
|
+
output << data_headers.map {|h| "\t\t<th>#{sanitize(h.humanize.titleize)}</th>\n" }.join
|
23
|
+
output << "\t</tr>\n"
|
24
|
+
end
|
25
|
+
data_set.each do |row|
|
26
|
+
output << "\t<tr>\n"
|
27
|
+
output << row.map {|field| "\t\t<td>#{sanitize(field)}</td>\n"}.join
|
28
|
+
output << "\t</tr>\n"
|
29
|
+
end
|
30
|
+
output << "</table>\n"
|
31
|
+
return output
|
32
|
+
end
|
33
|
+
|
34
|
+
def sanitize(value)
|
35
|
+
value.gsub(/</, '<').gsub(/>/, '>')
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module MakeExportable #:nodoc:
|
2
|
+
class JSON < ExportableFormat
|
3
|
+
|
4
|
+
self.reference = :json
|
5
|
+
self.name = "JSON"
|
6
|
+
self.register_format
|
7
|
+
|
8
|
+
attr_accessor :data_set, :data_headers
|
9
|
+
|
10
|
+
def initialize(data_set, data_headers=[])
|
11
|
+
self.long = "JavaScript Object Notation (JSON)"
|
12
|
+
self.mime_type = "application/json; charset=utf-8;"
|
13
|
+
self.data_set = data_set
|
14
|
+
self.data_headers = data_headers
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate
|
18
|
+
output = []
|
19
|
+
unless data_headers.blank?
|
20
|
+
data_set.each do |row|
|
21
|
+
h = {}
|
22
|
+
row.each_with_index do |field, i|
|
23
|
+
h[data_headers[i]] = field
|
24
|
+
end
|
25
|
+
output << h
|
26
|
+
end
|
27
|
+
else
|
28
|
+
end
|
29
|
+
return output.to_json
|
30
|
+
end
|
31
|
+
|
32
|
+
def sanitize(value)
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module MakeExportable #:nodoc:
|
2
|
+
class TSV < ExportableFormat
|
3
|
+
|
4
|
+
self.reference = :tsv
|
5
|
+
self.name = "TSV"
|
6
|
+
self.register_format
|
7
|
+
|
8
|
+
attr_accessor :data_set, :data_headers
|
9
|
+
|
10
|
+
def initialize(data_set, data_headers=[])
|
11
|
+
self.long = "Tab-separated (TSV)"
|
12
|
+
self.mime_type = "text/tab-separated-values; charset=utf-8;"
|
13
|
+
self.data_set = data_set
|
14
|
+
self.data_headers = data_headers
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate
|
18
|
+
generate_header_option(data_headers)
|
19
|
+
output = ""
|
20
|
+
unless data_headers.blank?
|
21
|
+
output << data_headers.map {|h| sanitize(h.humanize.titleize) }.join("\t")
|
22
|
+
end
|
23
|
+
output << "\n" unless output.blank?
|
24
|
+
data_set.each do |row|
|
25
|
+
output << row.map {|field| sanitize(field)}.join("\t") << "\n"
|
26
|
+
end
|
27
|
+
return output
|
28
|
+
end
|
29
|
+
|
30
|
+
def sanitize(value)
|
31
|
+
value.gsub(/(\t|\\t)/, ' ')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module MakeExportable #:nodoc:
|
2
|
+
class XML < ExportableFormat
|
3
|
+
|
4
|
+
self.reference = :xml
|
5
|
+
self.name = 'XML'
|
6
|
+
self.register_format
|
7
|
+
|
8
|
+
attr_accessor :data_set, :data_headers
|
9
|
+
|
10
|
+
def initialize(data_set, data_headers=[])
|
11
|
+
self.long = 'XML'
|
12
|
+
self.mime_type = 'application/xml;'
|
13
|
+
self.data_set = data_set
|
14
|
+
self.data_headers = data_headers
|
15
|
+
end
|
16
|
+
|
17
|
+
def generate
|
18
|
+
generate_header_option(data_headers)
|
19
|
+
xml = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
20
|
+
xml << "<records>\n"
|
21
|
+
data_set.each do |row|
|
22
|
+
xml << "\t<record>\n"
|
23
|
+
row.each_with_index do |field,i|
|
24
|
+
if !data_headers.blank?
|
25
|
+
attr_name = sanitize(data_headers[i].dasherize)
|
26
|
+
else
|
27
|
+
attr_name = "attribute_#{i}"
|
28
|
+
end
|
29
|
+
xml << "\t\t<#{attr_name}>#{sanitize(field)}</#{attr_name}>\n"
|
30
|
+
end
|
31
|
+
xml << "\t</record>\n"
|
32
|
+
end
|
33
|
+
xml << "</records>\n"
|
34
|
+
return xml
|
35
|
+
end
|
36
|
+
|
37
|
+
def sanitize(value)
|
38
|
+
value.gsub(/&/, '&').gsub(/</, '<').gsub(/>/, '>').
|
39
|
+
gsub(/"/, '"').gsub(/'/, ''')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module MakeExportableHelper
|
2
|
+
|
3
|
+
def self.exportable_class_list
|
4
|
+
MakeExportable.exportable_classes.keys.sort {|item1, item2| item1[0] <=> item2[0] }
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.exportable_table_list
|
8
|
+
MakeExportable.exportable_classes.values.map do |klass|
|
9
|
+
klass.table_name
|
10
|
+
end.sort {|item1, item2| item1[0] <=> item2[0] }
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.exportable_format_list
|
14
|
+
MakeExportable.exportable_formats.map do |key, fmt|
|
15
|
+
[fmt.name, key]
|
16
|
+
end.sort {|item1, item2| item1[0] <=> item2[0] }
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.exportable_units
|
20
|
+
hash = {}
|
21
|
+
MakeExportable.exportable_classes.values.map do |klass|
|
22
|
+
hash[klass] = klass.table_name
|
23
|
+
end
|
24
|
+
hash
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), '..', 'init')
|
data/spec/database.yml
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe "Exportable Formats" do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
class User
|
7
|
+
make_exportable
|
8
|
+
end
|
9
|
+
clean_database!
|
10
|
+
User.create(:first_name => "user_1", :last_name => "Doe", :created_at => Time.at(0), :updated_at => Time.at(0))
|
11
|
+
User.create(:first_name => "user_2", :last_name => "Doe", :created_at => Time.at(0), :updated_at => Time.at(0))
|
12
|
+
end
|
13
|
+
|
14
|
+
context "csv format" do
|
15
|
+
|
16
|
+
it "should export the columns as csv" do
|
17
|
+
User.to_export( "csv", :only => [:first_name, "is_admin"]).should == ["First Name,Is Admin\nuser_1,false\nuser_2,false\n", "text/csv; charset=utf-8; header=present"]
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should export the columns as csv and detect that there is no header" do
|
21
|
+
User.to_export( "csv", :only => [:first_name, "is_admin"], :headers => false).should == ["user_1,false\nuser_2,false\n", "text/csv; charset=utf-8; header=absent"]
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
26
|
+
context "excel format" do
|
27
|
+
|
28
|
+
it "should export the columns as xls" do
|
29
|
+
User.to_export( "xls", :only => [:first_name, "is_admin"]).should == ["<table>\n\t<tr>\n\t\t<th>First Name</th>\n\t\t<th>Is Admin</th>\n\t</tr>\n\t<tr>\n\t\t<td>user_1</td>\n\t\t<td>false</td>\n\t</tr>\n\t<tr>\n\t\t<td>user_2</td>\n\t\t<td>false</td>\n\t</tr>\n</table>\n", "application/vnd.ms-excel; charset=utf-8; header=present"]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should export the columns as xls and detect no header" do
|
33
|
+
User.to_export( "xls", :only => [:first_name, "is_admin"], :headers => false).should == ["<table>\n\t<tr>\n\t\t<td>user_1</td>\n\t\t<td>false</td>\n\t</tr>\n\t<tr>\n\t\t<td>user_2</td>\n\t\t<td>false</td>\n\t</tr>\n</table>\n", "application/vnd.ms-excel; charset=utf-8; header=absent"]
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
context "tsv format" do
|
39
|
+
|
40
|
+
it "should export the columns as tsv" do
|
41
|
+
User.to_export( "tsv", :only => [:first_name, "is_admin"]).should == ["First Name\tIs Admin\nuser_1\tfalse\nuser_2\tfalse\n", "text/tab-separated-values; charset=utf-8; header=present"]
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should export the columns as tsv and detect no header" do
|
45
|
+
User.to_export( "tsv", :only => [:first_name, "is_admin"], :headers => false).should == ["user_1\tfalse\nuser_2\tfalse\n", "text/tab-separated-values; charset=utf-8; header=absent"]
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
context "html format" do
|
51
|
+
|
52
|
+
it "should export the columns as html" do
|
53
|
+
User.to_export( "html", :only => [:first_name, "is_admin"]).should == ["<table>\n\t<tr>\n\t\t<th>First Name</th>\n\t\t<th>Is Admin</th>\n\t</tr>\n\t<tr>\n\t\t<td>user_1</td>\n\t\t<td>false</td>\n\t</tr>\n\t<tr>\n\t\t<td>user_2</td>\n\t\t<td>false</td>\n\t</tr>\n</table>\n", "text/html; charset=utf-8; header=present"]
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should export the columns as html and detect no header" do
|
57
|
+
User.to_export( "html", :only => [:first_name, "is_admin"], :headers => false).should == ["<table>\n\t<tr>\n\t\t<td>user_1</td>\n\t\t<td>false</td>\n\t</tr>\n\t<tr>\n\t\t<td>user_2</td>\n\t\t<td>false</td>\n\t</tr>\n</table>\n", "text/html; charset=utf-8; header=absent"]
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
context "xml format" do
|
63
|
+
|
64
|
+
it "should export the columns as xml" do
|
65
|
+
User.to_export( "xml", :only => [:first_name, "is_admin"]).should == ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<records>\n\t<record>\n\t\t<first-name>user_1</first-name>\n\t\t<is-admin>false</is-admin>\n\t</record>\n\t<record>\n\t\t<first-name>user_2</first-name>\n\t\t<is-admin>false</is-admin>\n\t</record>\n</records>\n", "application/xml; header=present"]
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should export the columns as xml and detect no header" do
|
69
|
+
User.to_export( "xml", :only => [:first_name, "is_admin"], :headers => false).should == ["<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<records>\n\t<record>\n\t\t<attribute_0>user_1</attribute_0>\n\t\t<attribute_1>false</attribute_1>\n\t</record>\n\t<record>\n\t\t<attribute_0>user_2</attribute_0>\n\t\t<attribute_1>false</attribute_1>\n\t</record>\n</records>\n", "application/xml; header=absent"]
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
context "json format" do
|
75
|
+
it "should export the columns as json" do
|
76
|
+
User.to_export( "json", :only => [:first_name]).should ==["[{\"first_name\":\"user_1\"},{\"first_name\":\"user_2\"}]", "application/json; charset=utf-8;"]
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require File.expand_path('../../spec_helper', __FILE__)
|
2
|
+
|
3
|
+
describe "Make Exportable Helper" do
|
4
|
+
|
5
|
+
before(:each) do
|
6
|
+
clean_database!
|
7
|
+
end
|
8
|
+
|
9
|
+
it "it should output an array of exportable classes" do
|
10
|
+
MakeExportable.exportable_classes = {}
|
11
|
+
User.class_eval("make_exportable")
|
12
|
+
Post.class_eval("make_exportable")
|
13
|
+
MakeExportableHelper.exportable_class_list.should == ["Post", "User"]
|
14
|
+
MakeExportable.exportable_classes = {}
|
15
|
+
end
|
16
|
+
|
17
|
+
it "it should output an array of exportable tables" do
|
18
|
+
User.class_eval("make_exportable")
|
19
|
+
Post.class_eval("make_exportable")
|
20
|
+
MakeExportableHelper.exportable_table_list.should ==["posts", "users"]
|
21
|
+
MakeExportable.exportable_classes = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
it "it should output an array of exportable classes and tables to check against" do
|
25
|
+
User.class_eval("make_exportable")
|
26
|
+
Post.class_eval("make_exportable")
|
27
|
+
MakeExportableHelper.exportable_units.should == {User => "users", Post => "posts"}
|
28
|
+
MakeExportable.exportable_classes = {}
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|